Skip to content

Security hardening: 30 exploitable vulnerabilities (batches 1–7)#38

Merged
jkyberneees merged 9 commits into
mainfrom
fix/next-security-vulnerabilities-tdd
Jun 13, 2026
Merged

Security hardening: 30 exploitable vulnerabilities (batches 1–7)#38
jkyberneees merged 9 commits into
mainfrom
fix/next-security-vulnerabilities-tdd

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

Summary

This PR closes 30 exploitable vulnerabilities discovered in the odek agent runtime through a TDD-driven hardening pass. Each fix is paired with a RED regression test; the branch is kept green with the full suite plus the race detector.

Scope

The hardening spans file tools, browser/network tools, performance/parallel tools, the Web UI server, sub-agent handling, config loading, and the @-resource resolver. Every change is minimal and follows the existing code style.

Batches

Batch Commit Focus
1 9a1eaa6 Browser history cap, search/multi_grep caps, file-size limits, odek serve CSRF / clickjacking / security headers, untrusted-content wrappers for head_tail / diff / sort / tr / json_query
2 67e3da7 Shell / parallel_shell output caps, browser request timeout, batch_patch file-size + diff wrapping, transcribe file-size + output caps, tree directory-width cap
3 73582b1 patch file-size limits + file-mode preservation, glob match cap, sub-agent task-file cap, session_search get/list wrapping and caps
4 09e4372 @-resource / --ctx prompt wrapping, resource file-size cap, delegate_tasks summary cap, patch / batch_patch output-expansion cap
5 db47d8f write_file content cap, file_info CWD confinement + path wrapping, WebSocket max payload, session file size cap, SKILL.md size cap
6 8dbba3d tree path wrapping, head_tail output cap, AGENTS.md / IDENTITY.md size caps, search_files symlink hardening
7 68078c4 base64 file-mode wrapping, browser title/element-text wrapping, browser interactive-element cap, config file size cap, FileResolver.Search symlink metadata hardening

Key security themes

  • Untrusted-content boundary: file-reading tools (read_file, search_files, multi_grep, batch_read, head_tail, diff, sort, tr, json_query, glob, file_info, tree, base64 file mode), shell, browser, transcribe, session_search, @-resources, --ctx files, and MCP tools now wrap external output in a per-call nonce wrapper.
  • DoS / OOM caps: search results, shell output, browser history/elements, perf-tool inputs/outputs, write_file content, sub-agent task/summary sizes, session/skill/config/resource files, and WebSocket payloads are all bounded.
  • Symlink/metadata hardening: search_files and FileResolver.Search use Lstat to avoid following symlinks for metadata.
  • Network hardening: browser and http_batch re-classify redirect hops; browser enforces a request timeout and caps extracted elements.
  • Serve hardening: CSRF-on-localhost protection, security headers, and WebSocket payload limits.

Testing

# Full suite
go test ./... -count=1

# Race detector on affected packages
go test -race ./cmd/odek ./internal/config ./internal/resource -count=1

Both pass.

Documentation

  • AGENTS.md updated with the new defenses and constants.
  • docs/SECURITY.md updated with the untrusted-content wrapper table, config-size-cap section, and resource-resolver symlink note.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
odek 47fae17 Commit Preview URL

Branch Preview URL
Jun 13 2026, 12:41 PM

Comment thread internal/resource/resource.go Dismissed
vprotocol finding (axis 2.1/2.9): the maxHeadTailTotalBytes comment
claimed a "total across all files" guarantee that the code enforces
per-file. Correct the comment to state the cap is per-file and that the
~10 MiB aggregate bound comes from the separate 10-file-per-call limit.

Add TestHeadTail_CapsOutputSizeMultiFile to lock both the per-file cap
and the aggregate bound across the max 10 files. Additive only — no
behavior change to security logic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jkyberneees

Copy link
Copy Markdown
Contributor Author

🔏 Verification Certificate — AI Verification Protocol v5.2.7

Ran the AI Verification Protocol against this PR's diff (8 commits, ~1,790 insertions across 30 files).

Classification: KnownGroundTruth — human-authored security fixes, each with a TDD test for the exact change (generator_identity absent; Ci_source: human_floor)
Pipeline diversity: single-model run (Opus 4.8). Provider diversity unavailable → monoculture caveat (§0.1). B/C/D/E roles played over a static diff; no live replay sandbox (§6.2-F not run — signal f skipped, weight redistributed per §3.5).
Untrusted-input pre-scan: clean — no verdict-affecting strings in the diff.

Axes Summary

Axis Status Key finding
2.1 Semantic Correctness ⚠️→✅ maxHeadTailTotalBytes comment claimed a "total across all files" guarantee the code enforces per-file. Fixed (comment + test).
2.2 Behavioral Contract Caps/wrapping match documented intent in AGENTS.md / SECURITY.md.
2.3 Security Surface CSRF requireLocalOrigin fails closed on unknown methods; Origin-present → localhost-only; empty-Origin allowed (standard pattern). Symlink hardening via Lstat. WS/file/config size caps sound.
2.4 Structural Integrity limitWriter clean; no new coupling.
2.5 Behavioral Exploration -race on concurrent paths (limitWriter, parallel shell, delegate_tasks) clean. transcribe temp file properly defer-removed (no leak).
2.6 Dependency Integrity No new dependencies.
2.7 Generator Provenance ⚠️ (info) Human-authored; no generator_identity. Non-penalizing note.
2.8 Adversarial Surface Untrusted-content wrapping extended to title/elements, tree/glob/file_info paths, session_search, @-resources, ctx files. unwrapUntrusted correctly newline-trims.
2.9 Documentation Coverage Every new cap documented in AGENTS.md + docs/SECURITY.md; new §15 config-cap section.

Signals (qualitative)

  • o (oracle agreement): very high — 49 tests, ≥1 per change (now 50 with the added multi-file test).
  • b (branch coverage): high on changed lines; both branches of caps/wrap exercised.
  • t (static depth): go vet + go build clean.
  • m / f: not independently run (no mutation framework / live sandbox in this static pass) → redistributed per §3.5.

Findings & Remediations (auto-applied, additive — §7.1)

# Axis Type Files Auto-applied
1 2.1 / 2.9 Doc accuracy — corrected the head_tail cap comment to state the cap is per-file, with the ~10 MiB aggregate bound coming from the separate 10-file-per-call limit cmd/odek/perf_tools.go
2 2.1 (oracle) Added TestHeadTail_CapsOutputSizeMultiFile locking the real per-file + aggregate bound cmd/odek/next_security_vulnerabilities_test.go

ℹ️ The two remediations above are committed and pushed as 016e091 — additive (one comment, one test). Per §7.4/§7.1, no behavior-changing source logic was modified — all security logic is exactly as authored.

Repair Gate (§7.5): ✅ build passes · ✅ full cmd/odek suite passes · ✅ -race clean · ✅ go vet clean · ✅ additive/reversible · ✅ no new warnings.

Verdict: AutoApprove (single-reviewer caveat → HumanReviewRecommended under a multi-provider policy)

The PR is genuinely high quality: comprehensive DoS/size caps, consistent untrusted-content wrapping at every filesystem/network boundary, symlink hardening (Lstat/O_NOFOLLOW), REST CSRF + clickjacking headers, and strict TDD coverage. No exploitable defects found — only one stated-vs-actual documentation inaccuracy (now fixed) plus a coverage-strengthening test.

Rationale: all 9 axes pass after remediation; the only binding gate is the §0.1 monoculture caveat — this was a single-model verification pass with no live replay sandbox. A human auditor requiring a fully independent (multi-provider) pipeline should treat this as HumanReviewRecommended.

🤖 Generated via the AI Verification Protocol with Claude Code

CodeQL flagged uncontrolled user data (the @-reference query) reaching a
path expression in FileResolver.Search. A query like "../../etc/passwd"
makes filepath.Join(root, query) clean to a path outside root, so
filepath.Glob matched files the workspace must not expose and os.Lstat
leaked their metadata (size, etc.) — the same metadata-disclosure class
this PR hardens elsewhere.

Add a separator-aware withinRoot() confinement primitive and apply it to
every Search match before touching the filesystem. Reuse it in Load,
replacing the prior HasPrefix(absTarget, absRoot) check that also fixed a
boundary bug ("/foo" matching "/foobar").

TestFileResolver_SearchOutsideRoot fails against the pre-fix code
(leaks @../secret.txt) and passes with the guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jkyberneees jkyberneees merged commit 19b6986 into main Jun 13, 2026
7 checks passed
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.

2 participants