Skip to content

feat(security): add OBSERVE + BASH_HIGH coverage for outbound curl writes (#1280) - #1302

Merged
griffinwork40 merged 2 commits into
mainfrom
afk/iso-issue-1280-5-m6l4xx
Aug 26, 2026
Merged

feat(security): add OBSERVE + BASH_HIGH coverage for outbound curl writes (#1280)#1302
griffinwork40 merged 2 commits into
mainfrom
afk/iso-issue-1280-5-m6l4xx

Conversation

@griffinwork40

Copy link
Copy Markdown
Owner

Summary

Adds safety coverage for outbound HTTP write commands (curl -X POST, curl -d, wget --post-data, etc.) that previously passed through every guard layer in normal sessions.

Problem

The readonly-bash classifier already blocks these patterns, but only runs for read-only skill forks. In normal sessions, an agent can make arbitrary POST/PUT/PATCH/DELETE requests — including through localhost proxies that forward to production APIs — with no approval, no recording, and no hooks firing.

Contributing factor in the 2026-08-24 incident where curl -X POST to a localhost review server proxied to production Substack.

Changes

OBSERVE-tier patterns (safe-destruct-patterns.ts):

  • curl-write-method — matches -X POST/PUT/PATCH/DELETE
  • curl-data-flag — matches -d/--data/-F/--form + wget --post-data/--post-file

Records the event without hard-blocking — legitimate dev-server testing still works.

BASH_HIGH classification (risk-classifier.ts):

  • 7 new entries trigger the AFK-mode approval gate for autonomous sessions

Tests

  • 7 new BASH_HIGH assertions in risk-classifier tests
  • 8 new OBSERVE pattern-matching rows + 3 hook-approve rows in safe-destruct tests
  • 187 tests pass (up from 169)

Design decision

OBSERVE + BASH_HIGH, not BLOCK — per the issue's recommendation. Hard-blocking curl -X POST everywhere would break legitimate agent workflows that test local dev servers during implementation.

Companion to #1281 (prompt-layer) and #1279/#1298 (launchctl/systemctl BLOCK patterns).

Closes #1280.

…ites (#1280)

- safe-destruct-patterns.ts: add curl-write-method and curl-data-flag OBSERVE
  entries covering `curl -X POST/PUT/PATCH/DELETE`, `curl -d`/`--data`/
  `-F`/`--form`, and `wget --post-data`/`--post-file`
- risk-classifier.ts: add BASH_HIGH entries for the same curl/wget shapes so
  AFK-mode autonomous sessions gate outbound HTTP writes behind approval
- Tests: 11 new assertions in risk-classifier.test.ts (curl -X POST/PUT/PATCH/
  DELETE, curl --data, curl -d, wget --post-data) and 11 new assertions in
  safe-destruct-detect.test.ts (pattern matching + hook approve decisions)
- All 187 tests pass; lint (tsc --noEmit) clean

Closes #1280
@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-afk-docs Ready Ready Preview Aug 26, 2026 10:38pm

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dbcb56e2e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agent/risk-classifier.ts Outdated
Comment on lines +75 to +81
'curl -X POST',
'curl -X PUT',
'curl -X PATCH',
'curl -X DELETE',
'curl --data',
'curl -d ',
'wget --post-data',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Handle intervening options in the curl high-risk gate

In autonomous AFK sessions, these literal substrings only classify writes when the mutating flag immediately follows curl; common valid commands such as curl -H 'Content-Type: application/json' -X POST URL -d '{}' or curl URL -X DELETE remain medium, so afk-mode-gate.ts allows them without approval. The same gap affects curl -F and wget --post-file, which this commit explicitly recognizes as writes in the OBSERVE detector. Match curl/wget invocations and their write flags independently of option ordering rather than matching only these exact command prefixes.

Useful? React with 👍 / 👎.

Comment on lines +209 to +211
id: 'curl-write-method',
re: /\bcurl\b[^|;&]*\s-X\s+(POST|PUT|PATCH|DELETE)\b/i,
tier: 'observe',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recognize curl's long request option

Normal sessions do not record curl --request POST https://api.example.com because this regex accepts only the short -X spelling. This is not an exotic alternate syntax: curl --help all documents the option as -X, --request <method> Specify request method to use. Consequently an equivalent POST/PUT/PATCH/DELETE command produces no safe-destruct observation or audit event; include the long form, and preferably the other accepted argument spellings, in this pattern.

Useful? React with 👍 / 👎.

@griffinwork40 griffinwork40 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🤖 Automated review (hourly sweep), generated by the /review tool — a maintainer will follow up.

Review: PR #1302feat(security): add OBSERVE + BASH_HIGH coverage for outbound curl writes (#1280)

Target: afk/iso-issue-1280-5-m6l4xxmain · ref dbcb56e2 · +68/−0 · 4 files
Regime: light (hotfix, ~68 lines)
Stated intent: PR #1302 title+body — spec-compliance assessed.


Findings

1 · low · blocking:false · confidence:high · correctness · src/agent/safe-destruct-patterns.ts:210 + src/agent/risk-classifier.ts:75–81 · ref:dbcb56e2 · file-state (verified via live regex execution)

curl -XPOST (no-space short form) misses both the safe-destruct regex and the BASH_HIGH substring. curl -XPOST https://api.example.com is valid curl syntax. The curl-write-method regex requires \s-X\s+ (whitespace both before and after -X), so -XPOST (flag and method attached with no space) does not match. The BASH_HIGH substring 'curl -X POST' also requires the space. Live regex test: re.test('curl -XPOST https://api.example.com') → false. The companion readonly-bash.ts (curlSegmentMutating) explicitly handles this form via /^-X(POST|PUT|PATCH|DELETE)$/i.

// safe-destruct-patterns.ts:210 — requires whitespace between -X and method
re: /\bcurl\b[^|;&]*\s-X\s+(POST|PUT|PATCH|DELETE)\b/i
// 'curl -XPOST https://...' → false (no space between -X and POST)

suggestion: Change \s-X\s+ to \s-X\s* in the regex. Add 'curl -XPOST', 'curl -XPUT', 'curl -XPATCH', 'curl -XDELETE' to BASH_HIGH (or change 'curl -X POST' to 'curl -X' + add the 4 no-space variants). The no-space form is uncommon in model-generated shell but valid in CI scripts and human-authored commands.


2 · low · blocking:false · confidence:high · correctness · src/agent/risk-classifier.ts:80 · ref:dbcb56e2 · file-state (verified via live substring test)

BASH_HIGH 'curl -d ' (trailing space) misses the attached-value form curl -d'data'. classifyBash uses cmd.includes(p) for matching. The trailing space in 'curl -d ' ensures the substring does not false-positive on curl -data-binary, but it also means curl -d'{"x":1}' https://api.example.com (no space between -d and the value) is classified medium instead of high. Live test: 'curl -d"data" https://api.example.com'.includes('curl -d ') → false. The safe-destruct regex layer still catches this form via -d\b, so the OBSERVE recording works — only the AFK-mode BASH_HIGH approval gate is bypassed.

// risk-classifier.ts:80 — trailing space required
'curl -d ',
// 'curl -d"data" https://...' → includes('curl -d ') → false

suggestion: Change 'curl -d ' to 'curl -d' (remove trailing space). The false-positive risk (curl -data-binary) is negligible — -data-binary is not a valid curl flag (the correct flag is --data-binary), and even if it were, classifying it high is the conservative direction.


3 · low · blocking:false · confidence:high · correctness · src/agent/risk-classifier.ts:75–81 · ref:dbcb56e2 · file-state (verified via grep)

wget --post-file absent from BASH_HIGH — asymmetric with safe-destruct coverage. The curl-data-flag regex in safe-destruct-patterns.ts:215 correctly includes --post-file\b in the wget alternation, so the OBSERVE layer records wget --post-file=... commands. However, BASH_HIGH contains only 'wget --post-data'wget --post-file bypasses the AFK-mode approval gate, classifying as medium instead of high.

// risk-classifier.ts:81 — only --post-data
'wget --post-data',
// safe-destruct-patterns.ts:215 — covers both
\bwget\b[^|;&]*\s(--post-data\b|--post-file\b)

suggestion: Add 'wget --post-file' after 'wget --post-data' in BASH_HIGH.


4 · low · blocking:false · confidence:high · test-coverage · src/agent/safe-destruct-detect.test.ts:97–116 · ref:dbcb56e2 · file-state (verified via grep: zero matches for curl -X GET in test file)

No negative test for curl -X GET in the benign commands table. The curl-write-method regex correctly excludes GET via the exhaustive alternation (POST|PUT|PATCH|DELETE) — live test confirms re.test('curl -X GET https://...') → false. The benign-commands test section (lines 97–116) includes curl https://…/health and curl -s https://…/status but not curl -X GET, leaving this boundary undocumented.

suggestion: Add ['curl -X GET https://api.example.com/items'] to the does not flag benign table.


Spec-compliance

Stated intent: PR #1302 title+body — spec-compliance assessed.

Requirement Status
curl-write-method OBSERVE pattern (-X POST/PUT/PATCH/DELETE) ✅ Implemented (safe-destruct-patterns.ts:208–211)
curl-data-flag OBSERVE pattern (-d/--data/-F/--form/wget --post-data/--post-file) ✅ Implemented (safe-destruct-patterns.ts:213–216)
7 BASH_HIGH entries for curl/wget outbound writes ✅ 7 entries added (risk-classifier.ts:75–81)
OBSERVE, not BLOCK — per issue recommendation ✅ Both patterns use tier: 'observe'
Tests: 7 BASH_HIGH assertions + 8 OBSERVE pattern rows + 3 hook-approve rows ✅ All present
Companion to #1281 (prompt-layer) and #1279/#1298 ✅ Referenced in body
Closes #1280 ✅ Linked

No unmet intent. No scope creep detected.

Dimensions with no issues

  • Security — no issues found. The new patterns correctly extend the safe-destruct OBSERVE layer and the BASH_HIGH classification without introducing any new bypass paths. The two-layer design (OBSERVE records + BASH_HIGH gates in AFK-mode) is consistent with the existing architecture. Read: safe-destruct-patterns.ts, risk-classifier.ts.
  • API-compat — no issues found. DESTRUCTIVE_PATTERNS is a readonly array — new append-only entries are backward compatible. Pattern ids (curl-write-method, curl-data-flag) are new stable identifiers. No exported type or function signatures changed. Read: safe-destruct-patterns.ts, risk-classifier.ts.
  • Perf-observability — no issues found. 7 new substring entries in the BASH_HIGH table (linear includes scan), 2 new regex patterns in the DESTRUCTIVE_PATTERNS array — negligible additions to existing linear scans. [^|;&]* character classes are bounded by the delimiter set and cannot catastrophically backtrack. Read: risk-classifier.ts, safe-destruct-patterns.ts.

What was not checked

  • Citations verified inline against branch HEAD dbcb56e2e5407d4ec70949905b9f163dd8083e91 via git show and live Node.js regex/substring execution.
  • Stated intent: PR #1302 title+body — spec-compliance assessed.
  • Not checked: runtime test execution — static review only. CI shows all checks passing (Lint & Build ✅, Tests ✅ ubuntu + macos).
  • Not checked: interaction between the new BASH_HIGH entries and the existing readonly-bash.ts curl-segment classification (the two systems run in different contexts: readonly-bash.ts runs only for read-only skill forks; risk-classifier.ts runs in normal sessions).
  • Not checked: whether -F and --form patterns in the safe-destruct regex overlap with any existing pattern that handles multipart form uploads differently.

Severity arithmetic: 0 critical, 0 high, 0 medium → MERGE; 4 low.

Decision: MERGE — 0 blocking (4 low).

Clean, well-scoped security hardening that correctly adds OBSERVE + BASH_HIGH coverage for outbound HTTP write commands. The four low findings (no-space -XPOST form, trailing-space -d form, missing wget --post-file in BASH_HIGH, missing curl -X GET negative test) are edge cases that leave narrow gaps in the BASH_HIGH gate while the safe-destruct OBSERVE layer still catches most of them — follow-up hardening opportunities, not regressions.

… review

Address all 6 review findings (Codex P1/P2 + AFK /review 1-4):

safe-destruct-patterns.ts:
- curl-write-method regex: \s-X\s+ → \s(-X\s*|--request\s+) to catch
  both the no-space -XPOST form and the --request long form

risk-classifier.ts:
- BASH_HIGH: prefixless -X/-XPOST/--request variants so intervening
  flags (curl -H '...' -X POST) still match
- BASH_HIGH: 'curl -d ' trailing space → 'curl -d' (catches -d"data")
- BASH_HIGH: add 'curl -F' (multipart form upload)
- BASH_HIGH: add 'wget --post-file' (symmetric with safe-destruct)

Tests:
- 8 new risk-classifier assertions (no-space, long form, attached -d,
  -F, --post-file, intervening flags)
- 5 new safe-destruct OBSERVE rows + 3 benign-commands negative tests
  (curl GET, plain curl, silent curl)
- 3 new hook-approve rows (no-space, long form, --post-file)

@griffinwork40 griffinwork40 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🤖 Automated review (hourly sweep), generated by the /review tool — a maintainer will follow up.

Review: PR #1302feat(security): add OBSERVE + BASH_HIGH coverage for outbound curl writes (#1280)

Target: afk/iso-issue-1280-5-m6l4xxmain · ref 19c6d14 · +130/−0 · 4 files
Regime: light (hotfix — security hardening)
Stated intent: PR #1302 title+body — spec-compliance assessed.


Prior rounds

1 prior automated review on this PR (on commit dbcb56e2). Current commit 19c6d14 ("fix(security): close curl/wget BASH_HIGH and OBSERVE gaps from PR #1302 review") addresses all 4 findings from the prior round. This round confirms the fixes and reviews the final state.

Prior findings — resolution status

Prior finding Status at 19c6d14
curl -XPOST (no-space form) misses safe-destruct regex Fixed — regex `(-X\s*
curl -d trailing space misses -d"data" form Fixed — BASH_HIGH entry is 'curl -d' (no trailing space)
wget --post-file absent from BASH_HIGH Fixed'wget --post-file' added
curl -X GET benign negative test missing Fixed['curl -X GET https://api.example.com/items'] added to benign table

All four prior findings are resolved.


Findings

1 · nit · blocking:false · confidence:high · correctness · src/agent/safe-destruct-patterns.ts:210,215 · ref:19c6d14 · file-state (verified)

New curl patterns use [^|;&]* (no \n) vs existing convention [^|&;\n]*. All pre-existing patterns in the file (rm-recursive-force-long, git-push-force, find-delete, docker-destructive) use [^|&;\n]* to stop at both shell delimiters and newlines. The two new curl patterns use [^|;&]* — same set of delimiter characters but omitting \n. Functionally equivalent for single-command strings (which is what detectDestructiveCommands receives), but the convention divergence is visible to a reader.

// Existing style (e.g. line 82):
re: /\brm\s+[^|&;\n]*--recursive\b/i
// New patterns (lines 210, 215):
re: /\bcurl\b[^|;&]*\s(-X\s*|--request\s+)/i

suggestion: Normalize to [^|&;\n]* for consistency with the rest of the file.


Spec-compliance

Stated intent: PR #1302 title+body — spec-compliance assessed.

Requirement Status
curl-write-method OBSERVE pattern (-X POST/PUT/PATCH/DELETE + --request + no-space) (-X\s*|--request\s+)(POST|PUT|PATCH|DELETE) covers all forms
curl-data-flag OBSERVE pattern (-d/--data/-F/--form/wget --post-data/--post-file) ✅ Implemented with word-boundary anchors
BASH_HIGH entries for curl/wget outbound writes ✅ 17 entries: 4 spaced -X, 4 no-space -X, 4 --request, curl --data, curl -d, curl -F, wget --post-data, wget --post-file
OBSERVE, not BLOCK — per issue recommendation ✅ Both patterns use tier: 'observe'
Prefixless -X POST / --request POST catch intervening flags -X POST as a bare substring in BASH_HIGH matches regardless of preceding flags
Tests: 15 BASH_HIGH assertions ✅ All present (7 original + 8 hardening)
Tests: 13 OBSERVE pattern rows ✅ All present (8 original + 5 hardening)
Tests: 6 hook-approve rows (OBSERVE returns approve) ✅ All present
Tests: 3 benign-command negative tests (curl GET, curl -s, curl -X GET) ✅ All present
Prior review findings (4 lows) all addressed ✅ All 4 resolved in commit 19c6d14
Companion to #1281 (prompt-layer), #1279/#1298 (service-registration) ✅ Referenced in body
Closes #1280 ✅ Linked

No unmet intent. No scope creep detected.

Dimensions with no issues

  • Security — no issues found. The new patterns correctly extend the safe-destruct OBSERVE layer and the BASH_HIGH classification. Both layers fire independently: OBSERVE records the event in all agent contexts; BASH_HIGH gates behind the AFK-mode approval prompt. Defense-in-depth is preserved. The regex [^|;&]* boundary stop prevents cross-pipeline misattribution. Read: safe-destruct-patterns.ts, risk-classifier.ts.
  • API-compat — no issues found. DESTRUCTIVE_PATTERNS is a readonly array — new append-only entries are backward compatible. Pattern ids (curl-write-method, curl-data-flag) are new stable identifiers. BASH_HIGH is a module-private readonly string[]. No exported type or function signatures changed.
  • Correctness — no issues found. The curl-write-method regex correctly handles all curl method-specification forms: spaced (-X POST), no-space (-XPOST), long-form (--request POST). The \b terminal anchor prevents POST from matching POSTFIX or similar. The curl-data-flag regex uses \b word boundaries to prevent -d from matching -data-binary and similar. The [^|;&]* character class correctly scopes the match to a single shell command segment.
  • Test-coverage — no issues found. All new patterns and BASH_HIGH entries have corresponding positive test assertions. Benign commands (curl GET, curl -s) confirmed to not trigger. The two-tier hook integration tests confirm OBSERVE patterns return approve, not block. Coverage gap from prior review (curl -X GET negative test) is closed.
  • Perf-observability — no issues found. 17 new substring entries in the BASH_HIGH table (linear includes scan), 2 new regex patterns in the DESTRUCTIVE_PATTERNS array — negligible additions to existing linear scans. [^|;&]* character classes are bounded and cannot catastrophically backtrack.

What was not checked

  • Citations verified inline against branch HEAD 19c6d14228dc7e3bb681492f57a69364ddcbb962 via git show.
  • Stated intent: PR #1302 title+body — spec-compliance assessed.
  • Not checked: runtime test execution — static review only. CI has not yet run on this commit (only Vercel preview checks are present).
  • Not checked: interaction between the new BASH_HIGH entries and the existing readonly-bash.ts curl-segment classification (the two systems run in different contexts).
  • Not checked: whether the \b word boundary after DELETE prevents matching DELETE_ALL or similar — confirmed by inspection that no such curl method exists, but not runtime-verified.

Severity arithmetic: 0 critical, 0 high, 0 medium → MERGE; 1 nit.

Decision: MERGE — 0 blocking (1 nit).

Clean, well-scoped security hardening that correctly addresses all 4 findings from the prior review. The curl-write-method regex now handles all three method-specification forms (-X POST, -XPOST, --request POST), and BASH_HIGH has comprehensive substring coverage including prefixless variants that catch intervening flags. The sole nit (\n in character class) is a cosmetic convention divergence with no functional impact.

@griffinwork40
griffinwork40 merged commit d3f3ff7 into main Aug 26, 2026
3 checks passed
@griffinwork40
griffinwork40 deleted the afk/iso-issue-1280-5-m6l4xx branch August 26, 2026 23:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

safe-destruct: add OBSERVE/BASH_HIGH coverage for outbound curl writes

1 participant