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: 7 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
# quota on generated docs, bot PRs, and per-push incremental re-reviews.

reviews:
# Go has no docstrings — godoc comments (enforced by the linters we
# already run) are the Go idiom. The Python-centric docstring-coverage
# gate false-positives on every Go diff, so it is disabled.
pre_merge_checks:
docstrings:
mode: "off"

auto_review:
enabled: true
# Review once when the PR opens; no re-review on every push.
Expand Down
14 changes: 7 additions & 7 deletions docs/security/ENVIRONMENT_HARDENING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ but absent from the process environment entirely.
└───────────────────────────────────────────────────────────┘
│
▼
internal/shell/shell.go → NewShell()
pkg/shell/shell.go → NewShell()
(the single enforcement point for all shell surfaces)
```

## Layer 2: Environment Filtering

**Location:** `internal/shell/env.go`, `internal/shell/shell.go`
**Location:** `pkg/shell/env.go`, `pkg/shell/shell.go`

When `NewShell` is called without an explicit `Env` slice, the process
environment (`os.Environ()`) is filtered through an allowlist. Only variables
Expand Down Expand Up @@ -122,7 +122,7 @@ sandbox.

## Layer 3: Command Deny List

**Location:** `internal/shell/run.go` (blockHandler), `internal/agent/tools/bash.go` (blockFuncs)
**Location:** `pkg/shell/run.go` (blockHandler), `pkg/agent/tools/bash.go` (blockFuncs)

The `blockHandler` middleware intercepts external command execution in the
mvdan/sh interpreter chain. Each `BlockFunc` in the list is tested against
Expand Down Expand Up @@ -243,7 +243,7 @@ built-in list. Built-in entries cannot be removed via configuration.

## Layer 1: Sandbox Markers

**Location:** `internal/shell/shell.go` (`PhosphorEnvMarkers`)
**Location:** `pkg/shell/shell.go` (`PhosphorEnvMarkers`)

Every shell spawned by Phosphor receives the following environment markers:

Expand Down Expand Up @@ -438,8 +438,8 @@ echoing them reveals nothing.

## Testing

Comprehensive tests are in `internal/shell/env_test.go` and
`internal/agent/tools/bash_test.go`:
Comprehensive tests are in `pkg/shell/env_test.go` and
`pkg/agent/tools/bash_test.go`:

- `TestFilterEnv_Basic` — basic allowlist filtering
- `TestFilterEnv_CaseInsensitive` — Windows case-insensitive matching
Expand All @@ -454,5 +454,5 @@ Comprehensive tests are in `internal/shell/env_test.go` and
Run with:

```bash
go test ./internal/shell/ ./internal/agent/tools/ -run TestFilterEnv|TestSafeDefaultEnv|TestPhosphorEnvMarkers|TestNewShell|TestBashTool_ConfigAwareBannedCommands
go test ./pkg/shell/ ./pkg/agent/tools/ -run 'TestFilterEnv|TestSafeDefaultEnv|TestPhosphorEnvMarkers|TestNewShell|TestBashTool_ConfigAwareBannedCommands'
```
32 changes: 16 additions & 16 deletions docs/security/SECURITY_VALIDATION_TEST_SUITE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Tests map to the six-layer defense architecture documented in [WORKSPACE_HARDENI

## Test Files

### `internal/agent/tools/workspace_hardening_test.go`
### `pkg/agent/tools/workspace_hardening_test.go`

Validates that every filesystem-touching tool blocks out-of-workspace paths with a `Security violation: path ... is outside workspace` error.

Expand All @@ -72,7 +72,7 @@ Validates that every filesystem-touching tool blocks out-of-workspace paths with
| `TestWorkspaceHardening_BashWorkingDirBlocksOutsidePaths` | bash | out-of-workspace `working_dir` parameter |
| `TestWorkspaceHardening_AllToolsAllowInWorkspace` | all | positive control — in-workspace paths are allowed |

### `internal/shell/environment_hardening_test.go`
### `pkg/shell/environment_hardening_test.go`

Validates shell-level security: command deny list, environment filtering, and sandbox markers.

Expand All @@ -86,7 +86,7 @@ Validates shell-level security: command deny list, environment filtering, and sa
| `TestEnvironmentHardening_FilterEnvWithAllowedEnv` | Env filtering | AWS_SECRET_ACCESS_KEY invisible when AllowedEnv=[PATH,HOME]; PATH visible |
| `TestEnvironmentHardening_EmptyAllowedEnv_FiltersEverything` | Env filtering | Empty allowlist filters ALL environment variables including PATH |

### `internal/agent/tools/compositional_chain_test.go`
### `pkg/agent/tools/compositional_chain_test.go`

Validates that chaining individually allowed actions cannot produce forbidden outcomes.

Expand All @@ -97,7 +97,7 @@ Validates that chaining individually allowed actions cannot produce forbidden ou
| `TestCompositionalChains_WorkingDirEscapeInChain` | Out-of-workspace working_dir blocked even in chains |
| `TestCompositionalChains_ChainTriggersPermission` | Plain `ls` does NOT trigger permission; `ls && echo` DOES trigger permission |

### `internal/agent/tools/agentjacking_test.go`
### `pkg/agent/tools/agentjacking_test.go`

Validates that data from "trusted" sources (MCP servers, context files) cannot be used to smuggle malicious instructions or bypass security.

Expand All @@ -109,7 +109,7 @@ Validates that data from "trusted" sources (MCP servers, context files) cannot b
| `TestAgentjacking_ZerWidthCharsInPath` | Zero-width characters (\u200B, \uFEFF, \u2060) in paths do not bypass bounds checks |
| `TestAgentjacking_HTMLCommentsInPath` | HTML comments (`<!-- SYSTEM: ... -->`) in paths do not bypass bounds checks |

### `internal/agent/tools/path_obfuscation_test.go`
### `pkg/agent/tools/path_obfuscation_test.go`

Validates that IsInside and SmartJoin handle complex path resolutions robustly.

Expand Down Expand Up @@ -137,7 +137,7 @@ Validates network egress hardening: the `securityTransport` interceptor, IP-bloc
| `TestWebFetchHardeningUserPromptVerified` | Unknown host triggers TUI prompt — user granted access |
| `TestWebFetchHardeningUserPromptDenyVerified` | Unknown host triggers TUI prompt — user denied access → request blocked |

### `internal/agent/tools/memory_poisoning_test.go`
### `pkg/agent/tools/memory_poisoning_test.go`

Validates that persistent state (context files like AGENTS.md, PHOSPHOR.md) cannot override hardcoded security policies.

Expand All @@ -149,7 +149,7 @@ Validates that persistent state (context files like AGENTS.md, PHOSPHOR.md) cann
| `TestMemoryPoisoning_BashToolSecurity` | Bash tool still blocks banned commands and out-of-workspace working_dir even with malicious GEMINI.md |
| `TestMemoryPoisoning_EditToolSecurity` | Edit tool still blocks out-of-workspace edits even with malicious AGENTS.md |

### `internal/agent/tools/fuzzing_test.go`
### `pkg/agent/tools/fuzzing_test.go`

Validates that all security guards **fail-closed** (reject input) rather than **fail-open** (panic/crash). A crash in a security check is itself a vulnerability — if `IsInside` panics, the caller may silently proceed. These tests feed random garbage into every guard and assert `NotPanics`, ensuring robustness under adversarial or malformed input.

Expand All @@ -165,16 +165,16 @@ Validates that all security guards **fail-closed** (reject input) rather than **

```bash
# Individual test suites
go test ./internal/agent/tools/ -run 'TestWorkspaceHardening' -count=1
go test ./internal/shell/ -run 'TestEnvironmentHardening' -count=1
go test ./internal/agent/tools/ -run 'TestCompositionalChains' -count=1
go test ./internal/agent/tools/ -run 'TestAgentjacking' -count=1
go test ./internal/agent/tools/ -run 'TestPathObfuscation' -count=1
go test ./internal/agent/tools/ -run 'TestMemoryPoisoning' -count=1
go test ./internal/agent/tools/ -run 'TestFuzzing' -count=1
go test ./pkg/agent/tools/ -run 'TestWorkspaceHardening' -count=1
go test ./pkg/shell/ -run 'TestEnvironmentHardening' -count=1
go test ./pkg/agent/tools/ -run 'TestCompositionalChains' -count=1
go test ./pkg/agent/tools/ -run 'TestAgentjacking' -count=1
go test ./pkg/agent/tools/ -run 'TestPathObfuscation' -count=1
go test ./pkg/agent/tools/ -run 'TestMemoryPoisoning' -count=1
go test ./pkg/agent/tools/ -run 'TestFuzzing' -count=1

# All new security tests together
go test ./internal/agent/tools/ ./internal/shell/ \
go test ./pkg/agent/tools/ ./pkg/shell/ \
-run 'TestWorkspaceHardening|TestEnvironmentHardening|TestCompositionalChains|TestAgentjacking|TestPathObfuscation|TestMemoryPoisoning|TestFuzzing' \
-count=1
```
Expand All @@ -199,5 +199,5 @@ go test ./internal/agent/tools/ ./internal/shell/ \
- Error message assertions use substring matching (`Contains`) to catch wording regressions without being fragile.
- The `securityViolationMsg` constant (`"Security violation: path"`) is shared across workspace hardening tests.
- When adding new tools, follow the existing pattern: resolve path with `SmartJoin`, call `IsInside`, return `fantasy.NewTextErrorResponse` on failure.
- When adding new banned commands, update both `internal/agent/tools/bash.go:bannedCommands` and the relevant environment hardening tests.
- When adding new banned commands, update both `pkg/agent/tools/bash.go:bannedCommands` and the relevant environment hardening tests.
- **Fail-closed principle**: Security guards must never panic. Fuzzing tests (`TestFuzzing_*`) use `require.NotPanics()` to verify that garbage input is rejected (returns error/false) rather than crashing. A crash in a security check is a fail-open vulnerability.
Loading
Loading