Implement workbench policy support - #4014
Conversation
Soffi AI SummaryThis 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
Workbench Raw Kubernetes Tools
Python Sandbox Infrastructure Querying
Observability Proxy Stability
Cloud Query Connection Pooling
Unicode & Cron Hardening
Frontend Updates
Commits
Updated: 2026-08-19 02:57 UTC |
Greptile SummaryThe PR adds project-scoped workbench policies, policy bindings, sampled evaluation persistence, and enforcement in the workbench tool-execution path.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| 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
3ac1f28 to
84446be
Compare
84446be to
7d315f4
Compare
|
plural deploy this |
|
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.
7d315f4 to
52c00c7
Compare
7a6e976 to
48cb985
Compare
|
plural deploy this |
|
Deployment GitOps PR is ready: https://github.com/pluralsh/plrl-up-demos/pull/2617 It pins image tag |
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).
48cb985 to
6b7dced
Compare
|
plural deploy this |
|
The requested deploy GitOps PR has been created: https://github.com/pluralsh/plrl-up-demos/pull/2618 It pins image tag |
a3907e8 to
19fa4b5
Compare
|
plural deploy this |
|
GitOps deployment PR: https://github.com/pluralsh/plrl-up-demos/pull/2625 Deployed console SHA: a40b4c0 |
|
plural review this |
|
plural review this |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Requesting changes.
The prior review findings remain unresolved on the current head (6033249):
BindingPolicyGraphQL access can expose an unreadable associated policy through the unrestrictedpolicy/bindPolicydataloaders.WorkbenchPolicy.policyandStackPolicy.policyhave 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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
92c40a4 to
a285f47
Compare
|
plural review this, focusing on the authorization issue mentioned already, don't bikeshed on other topics. |
|
plural review this, in light of given comments |
|
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
c41ed1b to
594f8e2
Compare
|
plural deploy this |
|
Deployment PR created: https://github.com/pluralsh/plrl-up-demos/pull/2650 — it pins the demo console image to sha-ccc6618. |
|
plural deploy this (along with cloud query) |
|
Deployment PR: https://github.com/pluralsh/plrl-up-demos/pull/2651 This deployment PR pins console to |
|
plural deploy this (AND CLOUD QUERY) |
|
Corrective GitOps PR: https://github.com/pluralsh/plrl-up-demos/pull/2652 — pins the demo CloudQuery image to |
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
Plural Flow: console