Skip to content

feat(l7): add JSON-RPC policy enforcement#1865

Open
krishicks wants to merge 27 commits into
mainfrom
hicks/push-nvuozlywzuwu
Open

feat(l7): add JSON-RPC policy enforcement#1865
krishicks wants to merge 27 commits into
mainfrom
hicks/push-nvuozlywzuwu

Conversation

@krishicks

@krishicks krishicks commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds JSON-RPC L7 policy enforcement for sandbox proxy traffic. The implementation supports JSON-RPC endpoint configuration, rpc_method matching, scalar object params matching, forward-proxy inspection, CONNECT tunnel inspection, and deny-if-any-denied batch handling.

JSON-RPC enforcement applies to sandbox-to-server HTTP request bodies sent to the configured endpoint. It does not yet enforce policy on server-to-client JSON-RPC messages carried on MCP SSE streams or response bodies. Tool results continue to pass because responses are relayed, not matched against rpc_method.

Related Issue

Closes #1793

Changes

  • Add JSON-RPC as an L7 endpoint protocol in policy parsing, validation, proto conversion, and relay dispatch.
  • Parse JSON-RPC HTTP request bodies into normalized call metadata for single requests and batch arrays.
  • Extend OPA policy input and Rego rules to enforce rpc_method and flattened scalar object params matchers for allow and deny rules.
  • Inspect JSON-RPC request bodies on both forward-proxy and CONNECT tunnel paths before relaying upstream.
  • Evaluate JSON-RPC batch items independently and deny the whole batch when any call is denied.
  • Redact raw JSON-RPC params from L7 logs and record endpoint, RPC methods, params SHA-256 digest, and policy version instead.
  • Document current JSON-RPC directionality limits and params matcher scope.

Testing

  • mise run pre-commit passes
  • Unit tests added/updated
  • E2E tests added/updated (if applicable)

Additional targeted checks:

  • cargo test -p openshell-sandbox jsonrpc
  • mise run e2e:rust -- --test forward_proxy_jsonrpc_l7

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

@copy-pr-bot

copy-pr-bot Bot commented Jun 10, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@github-actions

Copy link
Copy Markdown

@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch 2 times, most recently from 62da29d to 8dc2a54 Compare June 11, 2026 15:20
@krishicks krishicks marked this pull request as ready for review June 12, 2026 16:35
@krishicks krishicks requested review from a team, derekwaynecarr and mrunalp as code owners June 12, 2026 16:35
@johntmyers johntmyers added the gator:in-review Gator is reviewing or awaiting PR review feedback label Jun 13, 2026
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Status

Validation: This maintainer-authored PR is project-valid because it implements the JSON-RPC/MCP method-level policy work discussed in #1793, with documented v1 scope around sandbox-to-server HTTP request inspection.
Head SHA: 8dc2a54f9b99d2aa297ccfd49c102ea10ce982f4

Review findings:

  • Blocking: crates/openshell-sandbox/src/l7/jsonrpc.rs flattens JSON-RPC params into dot-separated keys without rejecting literal dotted keys or collisions. A request can present arguments.scope as a top-level param while sending a different nested arguments.scope object path to the upstream server, which can bypass params selectors. Please fail closed on ambiguous/dotted param keys or preserve nested params through policy evaluation.
  • Blocking: crates/openshell-sandbox/src/proxy.rs only force-denies GraphQL parse errors in the forward-proxy path. JSON-RPC parse errors are carried in request_info.jsonrpc.error but can still be allowed by generic REST-style method/path rules such as access: full or read-write. Please include JSON-RPC parse errors in the same force-deny path.
  • Warning: forward-proxy JSON-RPC audit logs use generic FORWARD_L7 / l7 output and omit RPC methods, params digest, and policy version, while the CONNECT path has richer JSON-RPC logging.
  • Warning: json_rpc.on_parse_error and json_rpc.batch_policy appear accepted by policy YAML but are not enforced or converted into proto behavior. Please either implement them or reject/remove them from the accepted schema.

Docs: Fern docs were updated for the new policy schema and sandbox policy behavior.

Next state: gator:in-review

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Follow-Up

Head SHA: 8dc2a54f9b99d2aa297ccfd49c102ea10ce982f4

The required independent reviewer pass confirmed the two blocking findings from the previous gator review:

  • JSON-RPC params flattening can create a policy/upstream parser differential through dotted-key collisions.
  • Forward-proxy JSON-RPC parse errors are not force-denied, so invalid JSON-RPC can still be allowed by generic method/path rules.

Additional non-blocking warning from the independent review:

  • parse_jsonrpc_call accepts any object with a string method and does not require "jsonrpc": "2.0". If this endpoint is documented as JSON-RPC 2.0 enforcement, malformed objects should become parse errors and follow the same fail-closed path.

The earlier warnings also still apply: forward-proxy JSON-RPC audit logs are less detailed than CONNECT logs, and json_rpc.on_parse_error / json_rpc.batch_policy are accepted in YAML but not preserved or enforced in runtime policy semantics.

Next state: gator:in-review

@johntmyers johntmyers added the test:e2e Requires end-to-end coverage label Jun 15, 2026
@github-actions

Copy link
Copy Markdown

Label test:e2e applied for 8dc2a54. Open the existing run and click Re-run all jobs to execute with the label set. The run will execute the standard E2E suite after building the required gateway and supervisor images once. The matching required CI gate status on this PR will flip green automatically once the run finishes.

@ddurst-nvidia

ddurst-nvidia commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

I would recommend adding an Action to add a test the functionality from and to modelcontextprotocol/conformance and through OpenShell.

There is an action, but I don't think it's setup by default in a useful way for testing through OpenShell.

There everything-{client,server} stand ins that can be used to validate. If something isn't supported, we can just mark it as unsupported, but it reduces surprises for people using it.

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Maintainer Update

I re-evaluated latest head 8dc2a54f9b99d2aa297ccfd49c102ea10ce982f4 after @ddurst-nvidia's 2026-06-15 comment recommending OpenShell-through-MCP conformance coverage.

Disposition: not resolved yet. There has not been a new commit or author response after the existing gator review feedback, and the maintainer testing recommendation is additional review feedback to address or have explicitly waived by a maintainer before this can advance.

Remaining items:

  • Blocking: JSON-RPC params matching still needs to fail closed on ambiguous dotted keys/collisions or preserve nested params through policy evaluation.
  • Blocking: JSON-RPC parse errors in the forward-proxy path still need the same fail-closed handling as GraphQL parse errors.
  • Review feedback: please address @ddurst-nvidia's recommendation for MCP conformance coverage through OpenShell, or get a maintainer decision that this does not need to block this PR.
  • Non-blocking warnings remain around forward-proxy JSON-RPC audit log detail, accepted-but-unenforced json_rpc.on_parse_error / json_rpc.batch_policy, and JSON-RPC 2.0 validation strictness.

Checks: required branch, Helm, DCO, docs preview, and standard Rust/Python checks are currently passing. OpenShell / E2E is still pending because the labeled E2E run needs execution.

Next state: gator:in-review

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After CI Update

I re-evaluated latest head 8dc2a54f9b99d2aa297ccfd49c102ea10ce982f4 after the labeled E2E run completed.

Disposition: not resolved. The PR still has unresolved review feedback, and the required OpenShell / E2E gate is now failing.

Remaining items:

  • Blocking: JSON-RPC params matching still needs to fail closed on ambiguous dotted keys/collisions or preserve nested params through policy evaluation.
  • Blocking: JSON-RPC parse errors in the forward-proxy path still need the same fail-closed handling as GraphQL parse errors.
  • Review feedback: please address @ddurst-nvidia's recommendation for MCP conformance coverage through OpenShell, or get a maintainer decision that this does not need to block this PR.
  • CI: OpenShell / E2E failed in run https://github.com/NVIDIA/OpenShell/actions/runs/27429202249. The failed jobs are e2e / E2E (rust-podman), e2e / E2E (rust-docker), e2e / E2E (rust-podman-rootless), e2e / E2E (python), and kubernetes-e2e / Kubernetes E2E (Rust smoke). The failed-log download was not available to this gator run, so a maintainer or author should inspect the failed Run tests steps in the Actions UI before deciding whether this is PR-caused or infrastructure-related.

Next state: gator:in-review

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After CI Log Review

I re-evaluated latest head 8dc2a54f9b99d2aa297ccfd49c102ea10ce982f4 after inspecting the failed OpenShell / E2E logs for run https://github.com/NVIDIA/OpenShell/actions/runs/27429202249.

Disposition: not resolved. The PR still has unresolved review feedback, and the failed E2E logs now show an actionable policy regression.

Remaining items:

  • Blocking: JSON-RPC params matching still needs to fail closed on ambiguous dotted keys/collisions or preserve nested params through policy evaluation.
  • Blocking: JSON-RPC parse errors in the forward-proxy path still need the same fail-closed handling as GraphQL parse errors.
  • Review feedback: please address @ddurst-nvidia's recommendation for MCP conformance coverage through OpenShell, or get a maintainer decision that this does not need to block this PR.
  • CI: OpenShell / E2E is failing. The logs show existing non-JSON-RPC L7 paths hitting an OPA evaluation error in crates/openshell-sandbox/data/sandbox-policy.rego: object.get(jsonrpc, "method", null) expects an object. This appears to make the new JSON-RPC matcher unsafe when request.jsonrpc is null/missing, causing GraphQL/HTTP L7 E2E regressions such as failed GraphQL forward/CONNECT tests, Python sandbox policy L7 tests returning status 0, and a Kubernetes smoke L7 bypass test returning 403 instead of 200.

Next state: gator:in-review

@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch 2 times, most recently from e9786e2 to 3516a0b Compare June 15, 2026 19:03
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Author Update

I re-evaluated latest head 3516a0be911c2717d5789fde4ed6a70ea1a6a9c0 after the new commits addressing the prior JSON-RPC review feedback and adding MCP conformance coverage.

Disposition: partially resolved.

Resolved items:

  • JSON-RPC params matching now fails closed on literal dotted keys / flattened selector ambiguity.
  • Forward-proxy JSON-RPC parse errors now force-deny before generic L7 policy evaluation.
  • Unsupported json_rpc.on_parse_error / json_rpc.batch_policy YAML fields are now rejected instead of silently accepted.
  • MCP conformance coverage has been added, and the direct conformance runner command is wired in the expected client-mode shape.

Remaining items:

  • Blocking: crates/openshell-sandbox/data/sandbox-policy.rego still calls object.get on request.jsonrpc without first proving it is an object. The Rust L7 evaluator serializes non-JSON-RPC requests as "jsonrpc": null, so object.get(request, "jsonrpc", {}) can still return null and then object.get(jsonrpc, "method", null) can hit the same OPA type error seen in the prior E2E logs. Please guard with is_object(jsonrpc) and add a regression test for the actual "jsonrpc": null input shape.
  • Warning: .github/workflows/mcp-conformance.yml still checks out modelcontextprotocol/conformance by mutable tag v0.1.11 and runs npm ci / npm run build in a privileged Docker-socket job with job-level secrets. Please pin the upstream checkout to an immutable commit SHA for that release. Also consider narrowing/removing job-level credentials from the third-party build step.

Checks: current branch and E2E checks are queued or pending for this head, with test:e2e still applied.

Next state: gator:in-review

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Blocked

Gator is blocked because GitHub reports this PR is not mergeable against main (mergeable_state: dirty) at head 3516a0be911c2717d5789fde4ed6a70ea1a6a9c0.

Next action: @krishicks, please rebase or merge main and resolve the conflicts so review and CI monitoring can continue. The earlier JSON-RPC review feedback and the current CI status still need follow-up after the merge conflict is resolved.

@johntmyers johntmyers added gator:blocked Gator is blocked by process or repository gates and removed gator:in-review Gator is reviewing or awaiting PR review feedback labels Jun 15, 2026
@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from 3516a0b to 6d61408 Compare June 15, 2026 19:25
Comment thread .github/workflows/mcp-conformance.yml Outdated
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
repository: modelcontextprotocol/conformance
ref: v0.1.11

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.

My last note on this got disappeared from history.

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Author and Reviewer Updates

I re-evaluated latest head 6d614085a82404c0491de0af757e77f234d6631c after the new author commit and @ddurst-nvidia's 2026-06-15 review comment about MCP conformance coverage.

Disposition: still blocked. GitHub still reports this PR is not mergeable against main (mergeable_state: dirty), so review and CI monitoring cannot advance until the merge conflict is resolved.

Remaining items:

  • Blocker: @krishicks, please rebase or merge main and resolve the conflicts.
  • Review feedback: @ddurst-nvidia noted that the workflow still uses the older MCP conformance v0.1.11 release while v0.1.16 is available. Please address that or get a maintainer decision that the older release is intentional.
  • Earlier review feedback should be re-checked after the conflict is resolved and the final diff is stable.

Checks: required branch, Helm, DCO, Rust, Python, and license/header checks are passing for this head. OpenShell / E2E is currently pending with test:e2e applied, including the new MCP conformance job.

Next state: gator:blocked

@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from 6d61408 to 9af3648 Compare June 15, 2026 20:31
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Author Update

I re-evaluated latest head 9af36485ca1b8d81799338db56df340075b3b75b after @krishicks force-pushed new commits on 2026-06-15.

Disposition: still blocked. GitHub still reports this PR is not mergeable against main (mergeable_state: dirty), so review and CI monitoring cannot advance until the merge conflict is resolved.

Remaining items:

  • Blocker: @krishicks, please rebase or merge main and resolve the conflicts.
  • Review feedback to re-check after the conflict is resolved: the JSON-RPC Rego matcher still appears to call object.get on request.jsonrpc without proving it is an object, so the prior "jsonrpc": null regression concern may still apply.
  • Maintainer review feedback: the workflow now uses modelcontextprotocol/conformance v0.1.16, but the checkout is still by mutable tag rather than an immutable commit SHA.

Checks: branch, DCO, Rust, Python, license/header, and Helm checks are currently passing or skipped for this head. OpenShell / E2E is still in progress with test:e2e applied, but gator remains blocked on mergeability first.

Next state: gator:blocked

@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from 9af3648 to 7101a37 Compare June 15, 2026 21:26
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After CI Log Review

I re-evaluated latest head 6e4e05e721495595aff7213510115ca5b1b5abe8 after reading the failed OpenShell / E2E logs for run https://github.com/NVIDIA/OpenShell/actions/runs/27967323709.

Disposition: not resolved. The PR still has unresolved review feedback, and the required E2E gate is failing with an actionable L7 regression.

Remaining blocking items:

  • protocol: json-rpc endpoints can still use access: full or access: read-write, which expands into REST-shaped method/path rules and can authorize JSON-RPC traffic without evaluating rpc_method / params. Please reject access on JSON-RPC endpoints, require explicit rules with rpc_method such as "*", and gate REST allow/deny Rego clauses away from JSON-RPC endpoints as defense in depth.
  • Response-only JSON-RPC POSTs and mixed response/request batches still need to fail closed for client-to-server response frames unless explicit response authorization or request/response correlation is implemented.
  • The MCP conformance job still needs credential-scope hardening: set persist-credentials: false, remove secret-bearing environment from job scope, and pass registry/GitHub credentials only to steps that actually need them.
  • CI: OpenShell / E2E is failing because e2e / E2E (rust-podman-rootless) failed in tests/forward_proxy_graphql_l7.rs. The failing assertion is graphql_l7_enforces_allow_and_deny_rules_on_forward_and_connect_paths: forward_chunked_query_allowed expected 200 but returned 403 with policy_denied for POST graphql-l7.openshell.test:8000/graphql. The aggregate Core E2E result then fails because the core E2E matrix result is failure.

Additional follow-up:

  • Update docs/reference/policy-schema.mdx so JSON-RPC access presets are not documented as allowing HTTP requests without method constraints.
  • Clarify that rpc_method: "*" is an OpenShell glob wildcard, not a literal JSON-RPC method name, or document how a literal * method is represented.

Next state: gator:in-review

@copy-pr-bot

copy-pr-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Author Update

I re-evaluated latest head 0e4d7af7722fbedce2307d571b0c937a1eb3250f after @krishicks pushed the 2026-06-22 fixes for the prior JSON-RPC policy, response-frame, MCP conformance credential, and docs feedback. I also ran the required independent reviewer pass against the focused current diff.

Disposition: partially resolved.

Resolved items:

  • JSON-RPC access presets are now rejected by L7 policy validation, and REST-shaped allow/deny clauses are gated away from protocol: json-rpc endpoints.
  • Response-only JSON-RPC POSTs and mixed request/response batches are denied in the Rust relay/evaluation path and in Rego.
  • The MCP conformance checkout is pinned to an immutable commit and uses persist-credentials: false.
  • Docs now state that JSON-RPC endpoints must use explicit rpc_method rules and clarify wildcard behavior.

Remaining items:

  • Blocking: crates/openshell-supervisor-network/src/l7/jsonrpc.rs still checks for method before checking whether result or error is also present. A mixed object such as {"jsonrpc":"2.0","id":1,"method":"initialize","result":{}} can be treated as an allowed request instead of failing closed as a client-to-server response frame. Please reject messages that contain both method and result/error, and add a regression test.
  • Blocking: .github/workflows/e2e-test.yml still leaves credentials in scope for the MCP conformance path. MISE_GITHUB_TOKEN is set on the generic Run tests step for every matrix entry, including matrix.suite == 'mcp', and Docker login state remains available before the external modelcontextprotocol/conformance runner executes. Please split the MCP run so it does not receive MISE_GITHUB_TOKEN, and logout/remove Docker auth before invoking third-party conformance code.
  • Warning: crates/openshell-supervisor-network/data/sandbox-policy.rego JSON-RPC rpc_method allow/deny matching does not explicitly require endpoint.protocol == "json-rpc" and request.method == "POST". Please add those guards or reject non-POST JSON-RPC request bodies in Rust.

Checks: current required gates are pending because the copy-pr mirror is waiting for validation at this head. I am posting the bare /ok to test command separately so CI can run, but the PR remains in review until the blocking items above are addressed.

Next state: gator:in-review

@johntmyers

Copy link
Copy Markdown
Collaborator

/ok to test 0e4d7af

@rpelevin

Copy link
Copy Markdown

One boundary I would keep explicit after the latest fixes: a JSON-RPC object that contains method plus result or error should be classified before method allow-list evaluation. If the parser checks method first, a mixed client-to-server frame can look like an allowed request even though it also carries response semantics. I would make this a hard invalid-envelope case and add negative tests for method-plus-result and method-plus-error.

For the MCP conformance job, I would treat the third-party runner as untrusted code until proven otherwise: no generic token env on the MCP matrix step, checkout with persisted credentials disabled, and clear any Docker or registry auth before invoking the external conformance build/run. The useful invariant is that conformance may observe behavior through OpenShell, but it should not inherit repository credentials or ambient container-registry auth.

One smaller Rego guard would make the policy side easier to reason about: JSON-RPC rpc_method matching should only run when the selected endpoint is JSON-RPC and the request method is POST. Everything else should stay in the non-JSON-RPC path or fail closed before those clauses can authorize it.

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Reviewer Update

I re-evaluated latest head 0e4d7af7722fbedce2307d571b0c937a1eb3250f after @rpelevin's 2026-06-22 comment about JSON-RPC mixed envelopes, MCP conformance credential scope, and Rego guard boundaries.

Disposition: not resolved. The comment confirms the remaining review concerns from the latest gator pass, and there has not been a newer author commit or maintainer waiver after that feedback.

Remaining items:

  • Blocking: reject JSON-RPC objects that contain method plus result or error before method allow-list evaluation, and add negative regression tests for both mixed-envelope shapes.
  • Blocking: split or harden the MCP conformance E2E path so third-party conformance code does not inherit MISE_GITHUB_TOKEN or ambient Docker/registry auth.
  • Warning: guard JSON-RPC rpc_method Rego matching to endpoint.protocol == "json-rpc" and POST requests, or fail closed for non-POST JSON-RPC request bodies before those clauses can authorize traffic.

Checks: test:e2e remains applied. Current required branch, Helm, DCO, docs preview, Rust, Python, and build checks are passing; OpenShell / E2E is still pending for this head.

Next state: gator:in-review

krishicks added 10 commits June 22, 2026 15:18
Add a reusable MCP conformance workflow that runs upstream client scenarios
through an OpenShell sandbox.

Add a client image, wrapper, policy template, and expected-failures baseline
for expanding MCP conformance coverage.

Remove stale JSON-RPC e2e policy fields that are no longer accepted.

Signed-off-by: Kris Hicks <khicks@nvidia.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
MCP elicitation sends JSON-RPC result messages from the client back to the
server without a method field. Treat those as valid response-only POSTs once
the endpoint has already matched so method allowlists keep applying to request
calls.

Also wire the MCP conformance runner into the e2e task and keep known upstream
client gaps in the expected-failure list.

Signed-off-by: Kris Hicks <khicks@nvidia.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
The upstream MCP conformance runner is untrusted node, so run it in a plain
Docker container on the e2e network instead of on the host. Unlike an
OpenShell sandbox (egress-only, so an inbound test server is unreachable),
the container has a routable address the client sandbox can reach, fixing
the scenario connection timeout.

The runner holds no gateway credentials: its client command posts the test
server URL back to a host-side bridge that runs the real MCP client in an
OpenShell sandbox and returns the result. That callback is the trust
boundary that keeps gateway access off the untrusted runner.

- host-bridge.py: host HTTP server that launches the client in a sandbox
- runner-shim.mjs: the runner's client command; calls back to the host bridge
- render-policy.py: policy renderer shared with client-through-openshell.sh
- build dist in the client image and run `node dist/index.js` so scenarios
  do not pay the tsx transpile cost on every run
- drop the host Node.js/npm build (and the CI Node.js setup step) and the
  DROP_DOCKER_AUTH knob, both unnecessary now that node runs in a container

Signed-off-by: Kris Hicks <khicks@nvidia.com>
@krishicks krishicks force-pushed the hicks/push-nvuozlywzuwu branch from 0e4d7af to fb9c396 Compare June 22, 2026 22:36
Comment thread docs/reference/policy-schema.mdx Outdated
max_body_bytes: 131072
rules:
- allow:
rpc_method: initialize

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

similar to the mcp review, could this just be method?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🤔 I think so. That would mean it could be any method as applied to whatever the protocol is, which seems fair to me, particularly if we have a json-rpc protocol now and a more strict mcp protocol later.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ah, L7AllowDef already has method, I see. That simplifies things nicely.

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Re-check After Author Update

I re-evaluated latest head fb9c39644c178e3d55da1976d1b65ebf387d9b69 after the new author update and ran the required independent reviewer pass for this cycle.

Disposition: partially resolved. The core JSON-RPC enforcement issues from the previous head appear addressed, but one blocking MCP conformance bridge trust-boundary issue remains.

Resolved items:

  • JSON-RPC objects that mix method with result or error are now rejected before method allow-list evaluation.
  • JSON-RPC parse errors and client-to-server response frames now fail closed before policy evaluation can allow them through.
  • JSON-RPC endpoints now reject REST-shaped access/rule forms and require explicit rpc_method rules.
  • The MCP conformance checkout is pinned and the runner no longer gets direct gateway credentials or Docker socket access.

Remaining items:

  • Blocking: e2e/mcp-conformance/host-bridge.py trusts server_url and env supplied by the untrusted runner container, and e2e/mcp-conformance/render-policy.py renders that URL into a sandbox policy whose template allows broad private address ranges. A compromised or malicious conformance runner could POST to the bridge and make the host reconfigure the client sandbox to reach arbitrary private/internal targets. Please constrain bridge requests so the target URL host must match the requesting runner container IP, and only forward an explicit allowlist of MCP conformance environment variables while stripping token-bearing host env from the subprocess.

Additional follow-up:

  • docs/reference/policy-schema.mdx explains literal * escaping for JSON-RPC deny rules, but the JSON-RPC allow-rule table should mirror that guidance for rpc_method: "*" as well.

Checks: test:e2e remains applied. Current branch, Helm, DCO, docs preview, Rust, Python, build, and image jobs are passing; OpenShell / E2E is still pending for this head.

Next state: gator:in-review

Signed-off-by: Kris Hicks <khicks@nvidia.com>
@rpelevin

Copy link
Copy Markdown

The remaining host-bridge issue looks like a confused-deputy boundary, not just a test-runner hardening detail.

I would make the bridge derive the allowed target from host-owned state, not from runner-supplied request fields. A safe shape is: mint a per-run bridge capability on the host, bind it to the runner container identity and expected upstream endpoint, require each bridge request to present that capability, and reject any target host that does not match the container identity and host-issued endpoint binding.

For environment forwarding, I would invert the default: the bridge starts from an empty env, copies only named MCP conformance variables, and fails closed if a token-shaped or registry credential variable is present outside the allowlist. That keeps the conformance runner able to observe OpenShell behavior without giving it a host-side network reconfiguration primitive or an ambient credential channel.

Two regression tests would make the boundary durable: one where the runner asks the bridge to render a policy for a different private host, and one where the runner supplies a token-bearing env key. Both should be denied before policy rendering or subprocess launch.

Signed-off-by: Kris Hicks <khicks@nvidia.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Status

Validation: This PR remains project-valid because it implements the JSON-RPC/MCP method-level policy work tracked by #1793, with docs and E2E coverage updates for the current schema.
Head SHA: e8a3b302e02e5cc2002856b768ca8adc74fa3554

Review findings:

  • Blocking: crates/openshell-supervisor-network/src/proxy.rs still treats JSON-RPC response frames differently on the forward-proxy path than on the CONNECT relay path. l7_parse_error_reason() only hard-denies parse errors, so force_deny is false for jsonrpc.has_response; evaluate_l7_request() returns denied, but an audit-mode JSON-RPC endpoint maps that to audit and forwards the request. Please include JSON-RPC response-frame detection in the forward-proxy hard-deny path, matching relay_jsonrpc(), and add a forward-proxy regression test for a client-to-server response body such as {"jsonrpc":"2.0","id":1,"result":{}} under audit/default enforcement.
  • Blocking: e2e/mcp-conformance/host-bridge.py still enforces the trust boundary only on the runner side. The host bridge accepts runner-supplied server_url and env, starts from os.environ.copy(), then invokes host-side OpenShell commands. A direct request from the untrusted runner can still ask the host to render policy for an arbitrary private target via render-policy.py / policy-template.yaml, or override host-side operational env other than MISE_GITHUB_TOKEN. Please enforce the allowlist in the bridge itself, start subprocesses from a minimal host-owned env, and bind each request to a host-issued capability plus the expected runner container/source address before rendering policy or launching the client.

Additional follow-up:

  • Consider tightening JSON-RPC receive-stream GET handling so a bodyless GET on a JSON-RPC endpoint is either validated as the expected MCP SSE receive stream shape or made explicit in policy. The current Rego allows the matched JSON-RPC path without an explicit allow rule for that direction.

Docs: Fern docs are present for the JSON-RPC policy schema and sandbox policy behavior.

Checks: branch checks, Helm, DCO, docs preview, Rust, Python, build, and image jobs are passing for this head. OpenShell / E2E remains pending with test:e2e applied.

Next state: gator:in-review

Signed-off-by: Kris Hicks <khicks@nvidia.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Status

Validation: This PR remains project-valid because it implements the JSON-RPC/MCP method-level policy work tracked by #1793, with docs and E2E coverage updates for the current schema and conformance path.
Head SHA: 407a4ec882c68a7df377f45bcf792b68080184e1

Review findings:

  • No blocking findings remain from the current independent review.
  • Warning: e2e/mcp-conformance/host-bridge.py now binds bridge requests to a per-run token, runner source IP, runner server host, and an env allowlist, which resolves the prior trust-boundary blocker. As follow-up hardening, the bridge should authenticate and source-check before reading the request body, and cap Content-Length, so unauthorized peers cannot consume bridge memory or worker threads with oversized bodies.
  • Suggested coverage: add focused host-bridge negative tests for bad token, wrong server_url host, unsupported env keys, non-string env values, and oversized or negative Content-Length.

Docs: Fern docs are present for JSON-RPC policy schema, directional enforcement, params matching, and current response/SSE limitations.

Checks: test:e2e remains applied. Current branch and E2E gates are still pending for this head.

Next state: gator:watch-pipeline

@johntmyers johntmyers added gator:watch-pipeline Gator is monitoring PR CI/CD status and removed gator:in-review Gator is reviewing or awaiting PR review feedback labels Jun 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gator:watch-pipeline Gator is monitoring PR CI/CD status test:e2e Requires end-to-end coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support method-level governance for MCP tool calls (JSON-RPC) in sandbox policy

4 participants