Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .egg/phase-permissions.json
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,10 @@
"**/*.spec.js",
"**/*.spec.jsx",
"**/conftest.py",
".github/",
"sandbox/scripts/"
".github/"
],
"blocked_reason": "Coder cannot modify tests, docs, pipeline-state, CI workflows, or credential shims; see #1901.",
"description": "TODO(#1903): keep in sync with CODER_PATTERNS / TESTER_PATTERNS / DOCUMENTER_PATTERNS in shared/egg_restrictions/patterns.py. Coder: everything except tester, documenter, .egg-state/, .github/, and sandbox/scripts/ scopes."
"blocked_reason": "Coder cannot modify tests, docs, pipeline-state, or CI workflows; see #1901. sandbox/scripts/ is writable — credential-shim modifications are reviewed by reviewer_security (see shared/prompts/security-review-criteria.md), not blocked at this layer.",
"description": "TODO(#1903): keep in sync with CODER_PATTERNS / TESTER_PATTERNS / DOCUMENTER_PATTERNS in shared/egg_restrictions/patterns.py. Coder: everything except tester, documenter, .egg-state/, and .github/ scopes."
},
{
"role": "tester",
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/sdlc-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ Agents are organized into five categories (execution, analysis, review, utility,

| Role | Category | Purpose | File Access |
|------|----------|---------|-------------|
| **Coder** | Execution | Implements code changes | All files except docs, tests, `.egg-state/`, `.github/`, `sandbox/scripts/` (blocklist-complement; see [Agent Roles Reference](../reference/agent-roles.md#coder)) |
| **Coder** | Execution | Implements code changes | All files except docs, tests, `.egg-state/`, `.github/` (blocklist-complement; see [Agent Roles Reference](../reference/agent-roles.md#coder)) |
| **Tester** | Execution | Finds gaps, writes tests, runs linters and reports issues to coder | Test files and infrastructure only: `tests/`, `test/`, `**/test_*.py`, `**/*_test.go`, `**/*.test.{ts,tsx,js,jsx}`, `**/*.spec.{ts,tsx,js,jsx}`, `**/conftest.py` (see [Agent Roles Reference](../reference/agent-roles.md#tester)) |
| **Documenter** | Execution | Updates documentation | Documentation and markdown only: `docs/`, `**/*.md`, `**/README.md` (see [Agent Roles Reference](../reference/agent-roles.md#documenter)) |
| **Autofixer** | Utility | Auto-fixes lint/format/type-check issues | Source and config files (no docs or contracts) |
Expand Down
10 changes: 6 additions & 4 deletions docs/reference/agent-roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,12 @@ each surface so reviewers know to keep them in sync.
(`**/*_test.py`, `**/test_*.py`, `**/*_test.go`, `**/test_*.go`,
`**/*.test.{ts,tsx,js,jsx}`, `**/*.spec.{ts,tsx,js,jsx}`),
`**/conftest.py` (tester's scope); `.egg-state/` (pipeline state);
plus defense-in-depth blocks on `.github/` (CI workflows and
CODEOWNERS — preserves the branch-protection invariant) and
`sandbox/scripts/` (gateway credential shims — preserves the
credential-routing invariant).
plus a defense-in-depth block on `.github/` (CI workflows and
CODEOWNERS — preserves the branch-protection invariant).
`sandbox/scripts/` is **writable** — the gateway is the sole egress
chokepoint, so credential-shim modifications are reviewed by
`reviewer_security` rather than blocked at the role-pattern layer.
See [security-review-criteria.md](../../shared/prompts/security-review-criteria.md).
- Block exemptions (always writable, overriding the blocks above):
`.egg-state/agent-outputs/` (coder's handoff output),
`.egg-state/agent-anchors/` (per-agent anchor state, scoped by
Expand Down
2 changes: 1 addition & 1 deletion gateway/tests/test_agent_restrictions_comprehensive.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ class TestThreeRoleBehavior1901:
".egg-state/agent-outputs/coder.json", # carved-back exempt
"skills/my-skill/SKILL.md", # skills exempt
"sandbox/agent-config/rules/foo.md", # rules exempt
"sandbox/scripts/gh", # credential shim — gateway is the chokepoint
],
)
def test_coder_allowed_blocklist_complement(self, path):
Expand All @@ -592,7 +593,6 @@ def test_coder_allowed_blocklist_complement(self, path):
".egg-state/secrets/key", # future subdir
".github/workflows/ci.yml", # branch-protection invariant
".github/CODEOWNERS", # branch-protection invariant
"sandbox/scripts/gh", # credential-routing invariant
],
)
def test_coder_blocked_blocklist_complement(self, path):
Expand Down
16 changes: 9 additions & 7 deletions gateway/tests/test_agent_restrictions_patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,13 +359,15 @@ def test_allows_sandbox_egg(self, pattern):
def test_allows_sandbox_bin_egg_health_inspect(self, pattern):
assert pattern.can_write("sandbox/bin/egg-health-inspect") is True

def test_blocks_sandbox_scripts_gh(self, pattern):
"""sandbox/scripts/ is blocked (credential-routing invariant)."""
assert pattern.can_write("sandbox/scripts/gh") is False

def test_blocks_sandbox_scripts_git_credential_helper(self, pattern):
"""sandbox/scripts/ is blocked (credential-routing invariant)."""
assert pattern.can_write("sandbox/scripts/git-credential-github-token") is False
def test_allows_sandbox_scripts_gh(self, pattern):
"""sandbox/scripts/ is writable; the gateway is the sole egress
chokepoint, so credential-shim modifications are reviewed by
reviewer_security rather than blocked at the role-pattern layer."""
assert pattern.can_write("sandbox/scripts/gh") is True

def test_allows_sandbox_scripts_git_credential_helper(self, pattern):
"""sandbox/scripts/ is writable; see test_allows_sandbox_scripts_gh."""
assert pattern.can_write("sandbox/scripts/git-credential-github-token") is True

def test_blocks_github_workflows(self, pattern):
""".github/ is blocked (branch-protection invariant)."""
Expand Down
13 changes: 13 additions & 0 deletions gateway/tests/test_phase_filter_restrictions.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,19 @@ def test_coder_allowed_for_extensionless_script(self):
result = check_file_restrictions("coder", ["bin/egg"])
assert result.allowed is True

def test_coder_allowed_for_sandbox_scripts(self):
"""sandbox/scripts/ is writable by the coder at the gateway
file_restrictions layer (#2133). Credential-shim modifications
are reviewed by reviewer_security rather than blocked at the
role-pattern layer; this test locks the synchronization between
.egg/phase-permissions.json and shared/egg_restrictions/patterns.py
so the two layers do not drift apart. Mirrors the patterns.py
layer coverage of both `gh` and `git-credential-github-token`."""
result = check_file_restrictions("coder", ["sandbox/scripts/gh"])
assert result.allowed is True
result = check_file_restrictions("coder", ["sandbox/scripts/git-credential-github-token"])
assert result.allowed is True

# --- tester role ---

def test_tester_blocked_from_contracts(self):
Expand Down
11 changes: 0 additions & 11 deletions shared/egg_restrictions/patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,6 @@ def _matches_pattern(file_path: str, pattern: str) -> bool:
# Defense-in-depth: CI workflows and CODEOWNERS — preserves the
# branch-protection invariant.
".github/",
# Defense-in-depth: gateway credential shims — preserves the
# credential-routing invariant.
"sandbox/scripts/",
],
block_exempt_patterns=[
# Coder's handoff directory — the only .egg-state/ subdir the coder
Expand All @@ -257,14 +254,6 @@ def _matches_pattern(file_path: str, pattern: str) -> bool:
"sandbox/agent-config/commands/*.md",
# Top-level skills directory (skill definitions are functional code)
"skills/",
# Jira sandbox wrapper — new in #1556. ``sandbox/scripts/`` is
# blocked wholesale to keep agents from retargeting the existing
# ``gh`` / ``git`` credential shims; this exemption lets the coder
# land the NEW ``jira`` wrapper alongside them. The file is
# functionally identical in shape (bash wrapper that POSTs to a
# gateway REST endpoint — never holds credentials locally) and is
# the only addition permitted under the broader block.
"sandbox/scripts/jira",
],
)

Expand Down
59 changes: 56 additions & 3 deletions shared/prompts/security-review-criteria.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,59 @@ Verification recipe:
3. Flag broken symlinks (`ls -l <link>` returns a missing target) as a
blocking finding regardless of code quality.

### 5. Secret leakage
### 5. Credential-shim modifications under `sandbox/scripts/`

The wrappers under `sandbox/scripts/` (`gh`, `git`, `jira`, …) are the
sandbox's **only** egress path to credential-bearing services. The
gateway sidecar is the actual security boundary — these wrappers hold
no credentials and any request they emit is independently re-validated
by the gateway against its policy. So a compromised wrapper cannot
bypass the gateway. **But** a compromised wrapper can still:

- **Mislead the agent calling it** — return fake success on a request
the gateway rejected, swallow error output, or print misleading
diagnostics that hide a failed operation.
- **Smuggle data into request bodies** — append attacker-chosen
fields the gateway happens to forward verbatim (e.g. PR body text,
issue comments, commit messages) to an external system.
- **Re-route to a different gateway endpoint** that has a more
permissive policy than the one the wrapper's name implies (e.g.
`gh` quietly POSTing to a `/jira/` route).
- **Exfiltrate session-scoped state** the wrapper has access to
(`EGG_SESSION_TOKEN`, environment, stdin) by including it in a
request the gateway *would* allow.

The role-level write filter does **not** block writes under
`sandbox/scripts/` — the credential-routing invariant is enforced by
this lens, not by `patterns.py`. Treat any diff that touches
`sandbox/scripts/*` as a trust-boundary change.

Verification recipe:

1. Enumerate every changed file under `sandbox/scripts/`. For each,
confirm it is a thin bash/POSIX wrapper that POSTs to a
`/api/v1/<service>/*` gateway route — no inline secrets, no calls
to the real `gh`/`git`/`jira` binaries, no network calls outside
the gateway URL, no writes outside the wrapper's documented stdout.
2. Confirm the wrapper's gateway route matches its name (a wrapper
named `gh` POSTs to `/api/v1/github/*`, not `/api/v1/jira/*`).
3. Confirm output handling is faithful: a non-2xx gateway response
surfaces as a non-zero exit and an error on stderr; the wrapper
does not silently swallow errors or fabricate success output.
4. For NEW wrappers, confirm a corresponding gateway route exists
(or is added by the same diff) and is itself reviewed for policy
correctness — a permissive new route is a real security finding
even if the wrapper looks innocuous.

Any deviation from the documented wrapper shape is a **mandatory NACK**
— do not silently approve a credential-shim diff that fails the recipe
above. Note that the security lens is advisory today (see *Scope* at the
top of this file), so the NACK is recorded as a finding on the approval
matrix rather than deadlocking consensus; promotion to a critical
(deadlock-capable) reviewer for `sandbox/scripts/*` diffs is tracked in
[#1997](https://github.com/jwbron/egg/issues/1997).

### 6. Secret leakage

Any new code path that may emit secrets, tokens, credentials, or
identity-bearing tokens to:
Expand All @@ -108,7 +160,7 @@ Pay special attention to redaction-bypass patterns: a regex that
redacts `password=...` but not `passwd=`; a sanitiser applied to one
log channel but not another.

### 6. Cross-file OWASP top-10 patterns
### 7. Cross-file OWASP top-10 patterns

OWASP top-10 patterns where the source and the sink live in **different
changed files**. Common shapes:
Expand All @@ -134,7 +186,8 @@ flag them.
the reach, you have not found the bug.
3. Trust-boundary changes get extra scrutiny: any new public endpoint,
any change to a decorator stack, any new file in `gateway/` or
`auth/`, any change to allowlists or regex patterns.
`auth/`, any change to allowlists or regex patterns, and any change
under `sandbox/scripts/` (the credential-shim wrappers).
4. Cross-reference [`code-review-criteria.md`](./code-review-criteria.md)
for the base review rules — your verdict format, severity classification,
and BRC ACK/NACK lifecycle inherit from there.
Expand Down
16 changes: 9 additions & 7 deletions shared/tests/test_egg_restrictions.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,15 @@ def test_allows_sandbox_egg_script(self):
def test_allows_sandbox_bin_egg_health_inspect(self):
assert CODER_PATTERNS.can_write("sandbox/bin/egg-health-inspect")

def test_blocks_sandbox_scripts_gh_shim(self):
"""sandbox/scripts/ is blocked (credential-routing invariant)."""
assert not CODER_PATTERNS.can_write("sandbox/scripts/gh")

def test_blocks_sandbox_scripts_git_credential_helper(self):
"""sandbox/scripts/ is blocked (credential-routing invariant)."""
assert not CODER_PATTERNS.can_write("sandbox/scripts/git-credential-github-token")
def test_allows_sandbox_scripts_gh_shim(self):
"""sandbox/scripts/ is writable; the gateway is the sole egress
chokepoint, so credential-shim modifications are reviewed by
reviewer_security rather than blocked at the role-pattern layer."""
assert CODER_PATTERNS.can_write("sandbox/scripts/gh")

def test_allows_sandbox_scripts_git_credential_helper(self):
"""sandbox/scripts/ is writable; see test_allows_sandbox_scripts_gh_shim."""
assert CODER_PATTERNS.can_write("sandbox/scripts/git-credential-github-token")

def test_blocks_github_workflows(self):
""".github/ is blocked (branch-protection invariant)."""
Expand Down
Loading