Skip to content

Implement workbench policy support - #4014

Merged
michaeljguarino merged 17 commits into
masterfrom
add-wb-policies
Aug 19, 2026
Merged

Implement workbench policy support#4014
michaeljguarino merged 17 commits into
masterfrom
add-wb-policies

Conversation

@michaeljguarino

Copy link
Copy Markdown
Member

Under the hood this uses the rust regorus library via a nif for very high performance opa validation. Also includes key features like persisting evaluation results and configurable policy bindings against tool names.

Test Plan

unit tests

Test environment: https://console.your-env.onplural.sh/

Checklist

  • I have added a meaningful title and summary to convey the impact of this PR to a user.
  • If required, I have updated the Plural documentation accordingly.
  • I have added tests to cover my changes.
  • I have deployed the agent to a test environment and verified that it works as expected (required only when changing agent code).

Plural Flow: console

@michaeljguarino
michaeljguarino requested a review from a team August 16, 2026 02:04
@michaeljguarino michaeljguarino added the enhancement New feature or request label Aug 16, 2026
@soffi-ai

soffi-ai Bot commented Aug 16, 2026

Copy link
Copy Markdown
Soffi AI Summary

This PR introduces a comprehensive workbench policy enforcement system backed by the Rust-based Regorus OPA engine (via a NIF), enabling high-performance Open Policy Agent (OPA) policy evaluation for AI workbench tool calls and infrastructure stacks.

Core Policy Infrastructure

  • Introduces new Policy, BindingPolicy, PolicyEvaluation, WorkbenchPolicy, and StackPolicy GraphQL types, mutations, and resolvers, allowing project-scoped OPA policies to be authored, stored, and associated with workbenches or stacks.
  • Policies are bound to targets (workbenches, stacks) via BindingPolicy rules with configurable match criteria (e.g., workbench tags) and evaluation intervals (default 1h, minimum 30m). Results are persisted as PolicyEvaluation records for auditability.
  • A PubSub subscription was added so clients receive real-time updates when binding policies change.
  • Auto-approval logic was implemented: if a tool call passes all attached policies, it can be approved without human intervention.

Workbench Raw Kubernetes Tools

  • Added new raw Kubernetes tools (kube_exec, kube_delete, kube_request) to the workbench tool set, giving the AI agent direct, read-enforced access to perform kubectl-style operations against managed clusters.
  • Read-only enforcement was added to cloud-query operations to prevent write operations from agent-initiated queries.

Python Sandbox Infrastructure Querying

  • Integrated Monty-based Python sandboxing for infrastructure querying, enabling safer execution of agent-driven cloud resource lookups.

Observability Proxy Stability

  • Fixed instability in the observability proxy by improving signal handling (SIGTERM/SIGINT) with a configurable drain timeout, adding health check endpoint tests, and improving Console provider reconnection logic with connection-state tracking and test coverage.

Cloud Query Connection Pooling

  • Refactored the cloud-query connection pool to use proper idle/max connection limits, per-connection state tracking (healthy vs. draining), and a background reaper goroutine — replacing a naive approach with a robust pooled connection lifecycle.

Unicode & Cron Hardening

  • Fixed Unicode escape handling in AI tool outputs and extended cron job timeout to accommodate longer-running scheduled tasks.

Frontend Updates

  • Generated GraphQL types updated to expose all new policy-related types and mutations.
  • Minor UI fixes: vulnerability reports tab, side panel close jank, tool call display improvements, and Kubernetes cluster raw tool access.

Commits

Commit Summary
52c00c7 Scaffolds the core workbench policy infrastructure: introduces Policy, WorkbenchPolicy, and StackPolicy Ecto schemas, GraphQL types, mutations, and resolvers. Uses the Rust Regorus NIF for high-performance OPA policy evaluation.
81f3a9f Implements auto-approval of workbench tool calls based on OPA policy outcomes — if all attached policies allow a tool invocation, the call is automatically approved without requiring manual confirmation.
6b7dced Introduces BindingPolicy — a rule that automatically attaches a policy to matching targets (workbenches or stacks) based on configurable criteria (e.g., workbench tags) and runs on a configurable evaluation interval.
4c80400 Fixes the vulnerability reports tab rendering in the frontend.
19fa4b5 Consolidates policy type handling and enforces proper typing across the policy subsystem to ensure consistent OPA evaluation behavior.
a40b4c0 Adds a PubSub subscription for binding policy changes, enabling real-time client updates when binding policies are created, updated, or deleted.
0b965b9 Fixes observability proxy instability by adding graceful shutdown with configurable drain timeout, proper SIGTERM/SIGINT handling, and a health check endpoint with test coverage.
83ba2d4 Fixes Unicode escape handling in AI tool outputs and increases the cron job timeout to support longer-running scheduled tasks.
0a5ab12 Integrates Monty-based Python sandboxing for infrastructure querying, enabling safer agent-driven cloud resource lookups inside the workbench.
e489682 Adds raw Kubernetes tools (kube_exec, kube_delete, kube_request) to the workbench tool set, giving the AI agent direct kubectl-style access to managed clusters.
ac9907c Enforces read-only mode in cloud-query to prevent agent-initiated queries from performing write operations against cloud resources.
ea8e66a Fixes side panel close jank in the frontend UI.
6033249 Addresses code review feedback across the policy and workbench tool implementation.
a285f47 Makes minor improvements to permission checks in the policy and workbench authorization layer.
594f8e2 Additional authorization tweaks to refine access control for policy management operations.
ccc6618 Adjusts timeouts for policy evaluation and related async operations.
314b319 Fixes a connection pooling bug in cloud-query, replacing the naive pool implementation with proper idle/max connection limits, per-connection health/drain state tracking, and a background reaper goroutine.

Updated: 2026-08-19 02:57 UTC

Deploy in Soffi

@greptile-apps

greptile-apps Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds project-scoped workbench policies, policy bindings, sampled evaluation persistence, and enforcement in the workbench tool-execution path.

  • Adds policy and policy-evaluation schemas, migrations, GraphQL APIs, generated clients, and access-token scopes.
  • Loads configured policies into workbench environments and evaluates them before tool implementation.
  • Adds policy cache invalidation, lifecycle events, cron cleanup, and unit/GraphQL coverage.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
lib/console/ai/tool.ex Adds policy matching, compilation, and admission evaluation; evaluation errors now propagate and stop tool execution.
lib/console/ai/chat/memory_engine.ex Enforces configured policies before validating and implementing workbench tool calls.
lib/console/ai/workbench/environment.ex Loads and converts persisted workbench policy associations into runtime policy definitions.
lib/console/deployments/workbenches.ex Adds cached policy loading and authorized transactional CRUD for workbench policy associations.
lib/console/deployments/policy.ex Adds policy CRUD, evaluation, sampling, and persisted evaluation cleanup behavior.
lib/console/schema/workbench_policy.ex Defines policy-to-workbench associations, match configuration validation, and regex compilation.
priv/repo/migrations/20260815204614_add_wb_policies.exs Creates policy, sampled evaluation, and workbench-policy association tables with supporting indexes.
lib/console/graphql/resolvers/deployments/policy.ex Exposes authorized policy queries, mutations, manual evaluation, and sampled evaluation pagination.

Reviews (3): Last reviewed commit: "Implement workbench policy support" | Re-trigger Greptile

Comment thread lib/console/deployments/workbenches.ex Outdated
Comment thread lib/console/graphql/resolvers/deployments/policy.ex
Comment thread lib/console/ai/tool.ex Outdated
@michaeljguarino
michaeljguarino force-pushed the add-wb-policies branch 2 times, most recently from 3ac1f28 to 84446be Compare August 16, 2026 03:47
@michaeljguarino

Copy link
Copy Markdown
Member Author

@greptileai

Comment thread lib/console/deployments/workbenches.ex
@michaeljguarino

Copy link
Copy Markdown
Member Author

@greptileai

@michaeljguarino

Copy link
Copy Markdown
Member Author

plural deploy this

@plural-copilot

Copy link
Copy Markdown
Contributor

GitOps deployment PR created: https://github.com/pluralsh/plrl-up-demos/pull/2616 — updates the demo console image to sha-7d315f4 for this PR head (7d315f4).

Under the hood this uses the rust regorus library via a nif for very high performance opa validation.  Also includes key features like persisting evaluation results and configurable policy bindings against tool names.
@michaeljguarino

Copy link
Copy Markdown
Member Author

plural deploy this

@plural-copilot

Copy link
Copy Markdown
Contributor

Deployment GitOps PR is ready: https://github.com/pluralsh/plrl-up-demos/pull/2617

It pins image tag sha-48cb985 and is annotated for Plural Service: mgmt/apps.

These are OPA policies to determine whether another policy should be attached to a stack or workbench, enabling policy enforcement itself to be fully automated (simplifying annoying logistics around provisioning policies appropriately).
@michaeljguarino

Copy link
Copy Markdown
Member Author

plural deploy this

@plural-copilot

Copy link
Copy Markdown
Contributor

The requested deploy GitOps PR has been created: https://github.com/pluralsh/plrl-up-demos/pull/2618

It pins image tag sha-4c80400.

@michaeljguarino

Copy link
Copy Markdown
Member Author

plural deploy this

@plural-copilot

Copy link
Copy Markdown
Contributor

GitOps deployment PR: https://github.com/pluralsh/plrl-up-demos/pull/2625

Deployed console SHA: a40b4c0

@michaeljguarino

Copy link
Copy Markdown
Member Author

plural review this

plural-copilot[bot]

This comment was marked as resolved.

@michaeljguarino

Copy link
Copy Markdown
Member Author

plural review this

@plural-copilot plural-copilot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found an authorization bypass in the new policy-association GraphQL fields. Cross-project associations are explicitly supported, but the association is authorized only through the workbench/stack while the nested policy field is resolved by the unrestricted dataloader. Any user who can read the workbench (or stack) can therefore retrieve the associated policy—including its source—even when they cannot read the policy's owning project.

Please make nested policy resolution authorization-aware (or restrict association/listing authorization to require both resources). Apply the same fix to StackPolicy.policy; the binding-policy listing already models the required both-policy access check.

end

object :workbench_policy do
field :id, non_null(:id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because cross-project associations are permitted, this unrestricted dataloader exposes the full policy (including policy source) to every reader of the workbench, even if that reader lacks access to the policy's project. Resolve this association with a policy-aware resolver or prevent listing/returning associations unless the viewer can read both the workbench and policy. The equivalent StackPolicy.policy field has the same issue.

@plural-copilot plural-copilot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes.

The prior review findings remain unresolved on the current head (6033249):

  1. BindingPolicy GraphQL access can expose an unreadable associated policy through the unrestricted policy/bindPolicy dataloaders.
  2. WorkbenchPolicy.policy and StackPolicy.policy have the same cross-project disclosure path. A user allowed to read the workbench/stack can retrieve the associated policy source without policy-project access.

Please make the association/nested-policy reads authorization-aware (or require read access to every associated resource) and add regression coverage for the cross-project cases. I also verified the previously reported policy-evaluation fail-open behavior has been addressed: evaluation errors now propagate rather than allowing tool execution.

@plural-copilot plural-copilot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes — one blocking authorization issue remains.

Policy evaluation errors now fail closed, and cross-project association creation is intentional. However, nested GraphQL association resolvers can load an associated policy without applying its project-level read authorization, exposing its Rego source to a user who only has access to the parent resource. The inline comment documents the affected path and required regression coverage.

This review consolidates/supersedes the earlier duplicated automated feedback (reviews 4966975706 and 4966980188).

field :next_poll_at, :datetime
field :matches, :binding_policy_matches

field :policy, :policy, resolve: dataloader(Deployments)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Authorize nested associated policy reads before returning Rego source

BindingPolicy.policy and BindingPolicy.bindPolicy are resolved solely through the Deployments dataloader. That loader queries Policy without applying Policy.for_user/2 or Policies.allow/3, while the Policy GraphQL type returns the policy field (the complete Rego source) directly. The same unscoped nested-loading pattern is used by WorkbenchPolicy.policy and StackPolicy.policy.

This remains reachable after the final commit: a user who can read policy A can query policy(id: A) { bindingPolicies { edges { node { bindPolicy { policy } } } } }; list_binding_policies/3 obtains associations for A without authorizing the other policy, and this resolver loads policy B by foreign key. If B belongs to an unreadable project, its Rego source is returned. Root policy queries do apply Policies.allow(user, :read), so this is an RBAC bypass rather than intended access.

Keep cross-project associations supported, but authorize each associated policy at nested-read time. Use a current-user-scoped policy loader/resolver (or equivalent explicit authorization) consistently for BindingPolicy, WorkbenchPolicy, and StackPolicy, and add regression coverage for a readable parent linked to an unreadable policy.

@michaeljguarino

Copy link
Copy Markdown
Member Author

plural review this, focusing on the authorization issue mentioned already, don't bikeshed on other topics.

plural-copilot[bot]

This comment was marked as off-topic.

@michaeljguarino

Copy link
Copy Markdown
Member Author

plural review this, in light of given comments

plural-copilot[bot]

This comment was marked as off-topic.

@michaeljguarino

Copy link
Copy Markdown
Member Author

plural review this, the prior reviews are marked off topic because fetching a policy explicitly bound to a stack/workbench is not a bypass but deliberately accessible

@plural-copilot plural-copilot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head a285f47 with the prior feedback and maintainer clarification.

  • The evaluation path now propagates evaluator errors, so the earlier fail-open concern is addressed.
  • Cross-project policy associations and visibility of a policy explicitly bound to a workbench/stack are intentional per the maintainer clarification; I am not treating the related nested-loader comments as blocking authorization findings.

No blocking issues found in the requested scope.

plural-copilot[bot]

This comment was marked as outdated.

@plural-copilot plural-copilot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: a :binding policy can be attached to a workbench but is evaluated using the workbench admission query, so it silently cannot enforce its intended policy. Please restrict workbench associations to :workbench policies and cover the rejection path.

The earlier concern about accessing a policy explicitly bound to a stack/workbench is intentionally supported per the maintainer clarification and is not treated as a finding. The previous evaluator fail-open concern is addressed: evaluator errors now propagate rather than authorizing the tool call.

Comment thread lib/console/deployments/workbenches.ex
@michaeljguarino

Copy link
Copy Markdown
Member Author

plural deploy this

@plural-copilot

Copy link
Copy Markdown
Contributor

Deployment PR created: https://github.com/pluralsh/plrl-up-demos/pull/2650 — it pins the demo console image to sha-ccc6618.

@michaeljguarino

Copy link
Copy Markdown
Member Author

plural deploy this (along with cloud query)

@plural-copilot

Copy link
Copy Markdown
Contributor

Deployment PR: https://github.com/pluralsh/plrl-up-demos/pull/2651

This deployment PR pins console to sha-314b319 from source SHA 314b3192e7d18596f23f6ce43ec5fcd5aa512c55.

@michaeljguarino

Copy link
Copy Markdown
Member Author

plural deploy this (AND CLOUD QUERY)

@plural-copilot

Copy link
Copy Markdown
Contributor

Corrective GitOps PR: https://github.com/pluralsh/plrl-up-demos/pull/2652 — pins the demo CloudQuery image to sha-314b319 to match this Console PR head, and is annotated Plural Service: mgmt/apps.

@michaeljguarino
michaeljguarino merged commit 4c7063c into master Aug 19, 2026
73 of 74 checks passed
@michaeljguarino
michaeljguarino deleted the add-wb-policies branch August 19, 2026 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants