Skip to content

fix(sandbox,server): fix chunk merge duplicates and OPA variable collision with overlapping policies#571

Merged
johntmyers merged 6 commits intomainfrom
fix/chunk-merge-and-opa-duplicate-policies
Mar 24, 2026
Merged

fix(sandbox,server): fix chunk merge duplicates and OPA variable collision with overlapping policies#571
johntmyers merged 6 commits intomainfrom
fix/chunk-merge-and-opa-duplicate-policies

Conversation

@johntmyers
Copy link
Collaborator

@johntmyers johntmyers commented Mar 24, 2026

Summary

Fix two related bugs where approving a draft network rule creates duplicate policy entries for the same host:port, causing the OPA engine to crash with duplicated definition of local variable.

Stacked on #570 — this PR targets the fix/implicit-allowed-ips-for-ip-host branch. Merge #570 first, then rebase this onto main.

Related Issue

Refs #567

Changes

Bug 1: Chunk merge creates duplicates (grpc.rs)

  • merge_chunk_into_policy now scans all network_policies entries for a host:port endpoint match before falling back to rule_name lookup
  • When a match is found, merges allowed_ips, binaries, and endpoints into the existing entry, preserving the user's original rule name
  • Only inserts a new entry when no existing rule covers the same host:port

Bug 2: OPA Rego variable collision (sandbox-policy.rego)

  • Refactored allow_request to isolate endpoint iteration inside a _policy_allows_l7 helper function, scoping the ep variable per-policy evaluation
  • Refactored _matching_endpoint_configs to use a _policy_endpoint_configs helper with distinct variable names (cfg vs ep) to avoid regorus collisions
  • Both changes prevent the duplicated definition of local variable ep error when multiple policies cover the same host:port

E2E tests

Root Cause

Bug 1: merge_chunk_into_policy looked up existing policy entries by chunk.rule_name (auto-generated as allow_{host}_{port} by the mechanistic mapper), which never matches user-authored names like test_server. The merge fell through to insertion, creating a duplicate.

Bug 2: The Rego allow_request rule used some ep; ep := policy.endpoints[_] directly in a complete rule body. When two policies matched the same host:port, regorus saw ep bound to different values across iterations and errored. The existing _matching_endpoint_configs comprehension had the same latent issue.

Testing

  • mise run pre-commit passes
  • Unit tests added for merge logic (name-mismatch merge, new host:port insertion)
  • Unit tests added for OPA overlapping policies (allow, deny, endpoint config queries)
  • Full cargo test -p openshell-sandbox passes (315 tests)
  • Full cargo test -p openshell-server passes (197 tests)
  • E2E tests added (OVL-1, OVL-2) and FWD-2 updated
  • E2E FWD-2 requires fix(sandbox): treat literal IP in policy host as implicit allowed_ips #570 to be merged first

Checklist

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

@johntmyers johntmyers requested a review from a team as a code owner March 24, 2026 16:54
@johntmyers johntmyers self-assigned this Mar 24, 2026
@johntmyers johntmyers added the test:e2e Requires end-to-end coverage label Mar 24, 2026
@johntmyers johntmyers changed the base branch from main to fix/implicit-allowed-ips-for-ip-host March 24, 2026 17:50
@johntmyers johntmyers force-pushed the fix/chunk-merge-and-opa-duplicate-policies branch from 2df59b9 to 4318fe3 Compare March 24, 2026 17:50
drew
drew previously approved these changes Mar 24, 2026
Base automatically changed from fix/implicit-allowed-ips-for-ip-host to main March 24, 2026 20:17
@johntmyers johntmyers dismissed drew’s stale review March 24, 2026 20:17

The base branch was changed.

…ision with overlapping policies

Two related bugs triggered when a draft rule approval creates a second
policy entry for the same host:port:

1. merge_chunk_into_policy looked up existing rules by chunk.rule_name
   (auto-generated as allow_{host}_{port}), which never matched the
   user's original rule name.  Now scans all network_policies entries
   for a host:port endpoint match before falling back to insertion,
   and merges allowed_ips into the existing endpoint.

2. The Rego allow_request rule and _matching_endpoint_configs
   comprehension used 'some ep; ep := policy.endpoints[_]' which
   caused regorus to error with 'duplicated definition of local
   variable ep' when multiple policies covered the same host:port.
   Refactored to isolate endpoint iteration inside helper functions
   (_policy_allows_l7, _policy_endpoint_configs) so variables are
   scoped per-policy evaluation.

Refs: #567
… allowed_ips

- Update FWD-2 (test_forward_proxy_denied_without_allowed_ips ->
  test_forward_proxy_allows_private_ip_host_without_allowed_ips):
  literal IP host no longer requires explicit allowed_ips, expects 200.

- Add OVL-1: overlapping L4 policies for same host:port must not crash
  OPA and should allow forward proxy connections.

- Add OVL-2: overlapping L7 policies for same host:port must not crash
  OPA and should allow CONNECT tunnel establishment.

Refs: #567
SSRF-6: Private IP with literal IP host now gets implicit allowed_ips
from PR #570, so CONNECT returns 200 instead of 403.

SSRF-3: Loopback is still blocked but via the always-blocked path
(implicit allowed_ips is synthesized, then resolve_and_check_allowed_ips
catches it). Log message says 'always-blocked' instead of 'internal
address'.
@johntmyers johntmyers force-pushed the fix/chunk-merge-and-opa-duplicate-policies branch from 5996c22 to c73845d Compare March 24, 2026 20:18
…arget port

When the SSRF check passes but nothing listens on the target port,
recv() returns empty bytes. Use 'assert 403 not in' (matching SSRF-4
pattern) instead of 'assert 200 in'.
PR #569 changed credential redaction from clearing the map to
replacing values with 'REDACTED'. Update e2e assertions to expect
credential keys with REDACTED values instead of an empty map.
@johntmyers johntmyers merged commit 256f7fc into main Mar 24, 2026
9 checks passed
@johntmyers johntmyers deleted the fix/chunk-merge-and-opa-duplicate-policies branch March 24, 2026 21:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:e2e Requires end-to-end coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants