Commit Graph
308 Commits
Author SHA1 Message Date
Jeffrey MontoyaandSulimanAbdulrazzaq e98a9da288 feat(gateguard): improve first-touch signal quality and session efficiency
- Ask class-specific first-touch questions (instruction, test, prose,
  config, code); code questions are unchanged.
- Credit a completed Glob/Grep/LS or shell search from the current human
  turn that covered the target; Read and exclusion globs never count.
- Collapse same-class new-file creations in one directory within a turn.
- Add opt-in GATEGUARD_FACT_FORCE_MAX_DENIALS session cap (from #2755).
- Always gate sensitive targets, judged on lexical and real paths.
- Canonicalize checked-path keys.

Closes #2608

Co-authored-by: SulimanAbdulrazzaq <suliman9000a@gmail.com>
2026-09-29 21:49:46 -04:00
affaan-m 93fca4a604 Merge reviewed Biome-filename correction from PR2835
Source-PR: https://github.com/affaan-m/ECC/pull/2835
Source-Head: 7ffea7091a
2026-09-28 07:07:45 -04:00
affaan-m e10bbaa492 Merge reviewed append-assignment correction from PR2965
Source-PR: https://github.com/affaan-m/ECC/pull/2965
Source-Head: eca846248d
2026-09-28 07:07:45 -04:00
affaan-m 7ffea7091a fix(hooks): distinguish Biome configs from results files
Preserve the original contributor histories and apply the exact independently reviewed repair.

Source-Parent: 74023e5689
Review-Manifest-SHA256: 208a6350119857df7c1e5b83a46cdba0b7f7399f58ed60167397920c7a21b454
2026-09-28 07:07:07 -04:00
affaan-m eca846248d fix(hooks): track append assignments in Git override checks
Preserve the original contributor histories and apply the exact independently reviewed repair.

Source-Parent: dee884656e
Review-Manifest-SHA256: 0c7eeef7c2722a605d049daf2dc3c2473c10dd616005afcba21049fc036d8ce9
2026-09-28 07:05:14 -04:00
affaan-m 807f07a687 Merge reviewed Git environment scope correction for PR2965
Source-PR: https://github.com/affaan-m/ECC/pull/2965
Source-Head: dee884656e
2026-09-28 05:39:46 -04:00
affaan-m dee884656e fix(hooks): preserve Git configuration across shell command scopes
Preserve contributor history while applying the exact reviewed current-main repair.

Source-PR: https://github.com/affaan-m/ECC/pull/2965
Source-Parent: ec0753bc7f
Review-Manifest-SHA256: 0172e3e695e672305f9bc41b0d385d2a299d8ec520c763dd9d7f6da743f76971
2026-09-28 05:38:05 -04:00
affaan-m da2edcb740 Merge reviewed PR2835 qualified configuration protections
Preserve exact-file protections, all shared assertions and private cleanup.

Source-PR: https://github.com/affaan-m/ECC/pull/2835
2026-09-28 05:01:22 -04:00
affaan-m 78dee5c829 Merge reviewed PR2845 configuration filename protections
Source-PR: https://github.com/affaan-m/ECC/pull/2845
2026-09-28 04:58:30 -04:00
affaan-m 74023e5689 fix(config-protection): protect qualified shared linter configurations
Preserve contributor history while applying the exact reviewed current-main repair.

Source-PR: https://github.com/affaan-m/ECC/pull/2835
Source-Parent: e58bb26647
Integration-Main: d3b8a3e908
Review-Manifest-SHA256: 2b591ee5fc545e0d552a58dd6f473f196ca773a194c33b87359f5c0447187e26
2026-09-28 04:50:09 -04:00
affaan-m 339db5b0be fix(config-protection): guard current config and ignore filenames
Preserve contributor history while applying the exact reviewed current-main repair.

Source-PR: https://github.com/affaan-m/ECC/pull/2845
Source-Parent: e19e74a7fc
Integration-Main: d3b8a3e908
Review-Manifest-SHA256: 02ef909e8553e93554ec8fb615b9299fd3ee6a0d7dc250ee2375ce80ce538060
2026-09-28 04:49:31 -04:00
affaan-m 442179e7d6 Merge exact reviewed PR2829 follow-up
Source-PR: https://github.com/affaan-m/ECC/pull/2829
Source-Head: 2b9164c042
Review-Manifest-SHA256: 16b9e4c854b6cfc38efc99d0913fe7f3018a68b9b1c194b50f4a1e06511975cd
2026-09-28 04:07:07 -04:00
affaan-m f4528c48a7 Merge exact reviewed PR2965 follow-up
Source-PR: https://github.com/affaan-m/ECC/pull/2965
Source-Head: ec0753bc7f
Review-Manifest-SHA256: 53ead3eb859a4c6c13d6751b1fe4f9a94f1a5d35a37430f6b8524c50e903490b
2026-09-28 04:07:07 -04:00
affaan-m 2b9164c042 fix(gateguard): inspect SQL clients behind supported launchers
Preserve contributor history while applying the exact reviewed current-main repair.

Source-PR: https://github.com/affaan-m/ECC/pull/2829
Source-Parent: c4b18b452d
Review-Manifest-SHA256: 16b9e4c854b6cfc38efc99d0913fe7f3018a68b9b1c194b50f4a1e06511975cd
2026-09-28 03:56:57 -04:00
affaan-m ec0753bc7f fix(hooks): preserve shell boundaries and detect literal hook bypasses
Preserve contributor history while applying the exact reviewed current-main repair.

Source-PR: https://github.com/affaan-m/ECC/pull/2965
Source-Parent: de480c65fd
Review-Manifest-SHA256: 53ead3eb859a4c6c13d6751b1fe4f9a94f1a5d35a37430f6b8524c50e903490b
2026-09-28 03:56:48 -04:00
affaan-m 609427cacc Merge commit 'de480c65fdbbdf8b0b6ffce0f16c0968791c5fbd' into affaan/integrate-reviewed-followups-20260928 2026-09-28 02:27:57 -04:00
affaan-m de480c65fd fix(hooks): distinguish literal data from executable shell contexts
Preserve original contributor history and all current-main assertions while bounding quote, heredoc, shell-option and deferred-scan handling.

Source-PR: https://github.com/affaan-m/ECC/pull/2965
Source-Parent: 5a878131de
Main-Parent: d3b8a3e908
Review-Manifest-SHA256: 5d23f47e60516b66733805aec0ebcd47f19265c964529fa0b1220c8aa6903add
2026-09-28 02:27:11 -04:00
affaan-m c4b18b452d fix(gateguard): classify standard launcher and GNU time options
Preserve contributor history and current-main behavior while resolving the exact reviewed follow-up.

Source-PR: https://github.com/affaan-m/ECC/pull/2829
Source-Parent: d811349224
Review-Manifest-SHA256: a7209685b2a5726777f7450a5983c28add8759654969b09ba3e4b803fcfaf8bd
2026-09-28 01:06:31 -04:00
affaan-m d811349224 fix(gateguard): detect stdin dd writes and launcher-wrapped shells 2026-09-27 22:25:29 -04:00
affaan-m a74c743370 fix(gateguard): classify literal dd execution without matching passive text
Preserve the contributor repair and current main protections. Handle operand ordering, bounded literal command wrappers, executable containers and find actions while keeping quoted prose and option values passive.

Source-PR: https://github.com/affaan-m/ECC/pull/2829
Source-Head: c88b3f879a
Integration-Main: d3b8a3e908
2026-09-27 21:50:09 -04:00
xsfandGitHub bf70150eb2 fix(gateguard): sanitize dangerous invisible unicode in denial paths (#3103)
* fix(gateguard): sanitize dangerous invisible unicode in denial paths

sanitizePath only stripped control characters and bidi overrides, so
the 394 codepoints that the repo-wide unicode safety policy classifies
as dangerous-invisible (zero-width U+200B-200D, U+2060, U+2061-2064,
variation selectors U+FE00-FE0F, tag block U+E0000-E007F, Hangul
fillers, U+FEFF, U+180E, U+3164) plus the U+2028/U+2029 line and
paragraph separators passed verbatim into Edit/Write denial messages.
Invisible characters in a quoted file path let a malicious path look
clean to a human reviewer.

Align sanitizePath with the CI policy list and cover it with a
hook-output regression test that asserts no dangerous invisible
character survives into the denial reason while visible path text
stays intact.

* refactor(gateguard): name the sanitizePath unicode policy constants

Address review feedback: move the code points and ranges out of
sanitizePath into descriptive constants mirroring
scripts/ci/check-unicode-safety.js, so the policy is auditable in one
place. Also seed the regression test input with U+2029 so the
paragraph-separator assertion is exercised rather than vacuous.

* fix(gateguard): sanitize C1 control characters in denial paths

Follow-up on the sanitizePath policy alignment: the Unicode C1 control
block (U+0080..U+009F) is invisible in every renderer like the ASCII
controls below 0x20, but the strip only covered the ASCII range. Add
C1_CONTROLS to the policy constants and seed U+0091 into the
regression test input.
2026-09-21 14:40:16 -04:00
Varun GoreandGitHub 7b7dfc6412 fix(gateguard): detect destructive SQL passed quoted to SQL clients (#3107)
* fix(gateguard): detect destructive SQL passed quoted to SQL clients

Quoted SQL like psql -c "drop table users" was stripped before the DESTRUCTIVE_SQL_DD regex ran, making it unreachable. Check dequoted tokens for known SQL clients (psql, mysql, sqlite3, ...) so real invocations are gated while git commit -m / echo mentions stay allowed. Fixes affaan-m/ECC#3024.

* fix(gateguard): unwrap sudo/env flags and ignore SQL string literals

Address review: sudo -u/env VAR= wrappers now resolve to the real SQL client; SQL single/double/dollar-quoted literals are stripped before the destructive regex so SELECT 'drop table' stays allowed. Adds deny/allow regression tests for quoted SQL, wrappers, literals, and non-SQL clients.

* test(gateguard): cover env PGPASSWORD wrapper variant

* fix(gateguard): close wrapper sh-c, env dir, tagged literal gaps

Review 5189686662: recurse sh -c from unwrapped wrapper index (sudo/doas/env + sh -c now denied); consume env -C/--chdir separate values; strip tagged dollar-quoted literals ($tag$...\baseline). Tests: 200 passed.
2026-09-21 14:39:17 -04:00
Sedat DağandGitHub c056ae7df2 fix(hooks): support python style comments in pre-commit quality checks (#3194)
* fix(hooks): support python style comments in pre-commit quality checks

fix(hooks): support python style comments in pre-commit quality checks

* fix: reject whitespace-only TODO messages

* fix: reject whitespace-only TODO messages

* test: add cases for hash-prefixed comments and string limitations
2026-09-21 14:33:19 -04:00
Affaan MustafaandGitHub fc9273e5e0 Merge pull request #3126 from VarunGore36/fix/reviewer-followups
fix(security): security follow-ups for worker, installer, claw, hooks
2026-09-19 19:58:43 -04:00
Affaan MustafaandGitHub 0a5207b6f2 Merge pull request #3102 from wellkilo/perf/metrics-tail-read
perf(metrics): cache cumulative session costs
2026-09-19 17:00:05 -04:00
Affaan MustafaandGitHub 07756cee15 fix(gateguard): gate ref- and history-destroying git commands (#3154, #3151) (#3170)
branch -D, stash drop/clear, reflog expire/delete, update-ref -d, restore (except --staged alone), and force-with-lease pushes to shared branches now hit the destructive gate. 238 hook tests pass; 49 CI checks green.
2026-09-19 02:58:33 -04:00
Affaan MustafaandGitHub f6eb800474 fix(session-start): scope summary lookup to repository identity (#3160) (#3168)
Windows: compare repo identity via normalizeRepoPath/sameRepoIdentity (8.3 short names, case, separators; inode fallback). All nine windows-latest jobs green on 52587005.
2026-09-19 02:08:50 -04:00
Affaan MustafaandGitHub 4007e25b58 Merge pull request #3157 from Frank-zhu0404/fix/issue-2886-heredoc-gateguard
fix(gateguard): ignore heredoc prose for tee and path-qualified sinks
2026-09-18 21:10:25 -04:00
Affaan MustafaandGitHub 09ae8be2bd Merge branch 'main' into fix/issue-2886-heredoc-gateguard 2026-09-18 21:03:40 -04:00
Affaan MustafaandGitHub 66462a5bf1 Merge branch 'main' into fix/3136-gateguard-batch-consistency 2026-09-18 21:03:31 -04:00
He DongandGitHub 8bf16ccfec fix(hooks): keep silent hook paths silent (#2987)
* fix(hooks): keep silent hook paths silent

* fix(hooks): harden stream failure handling

* fix(hooks): settle interrupted input streams

* test(hooks): name stream input limits

* test(hooks): isolate PostToolUse dispatcher fixtures
2026-09-18 18:42:20 -04:00
Affaan Mustafa db61d1c76a fix(gateguard): warn that parallel-batch siblings may already be applied (#3136)
A first-touch Edit/Write denial marks the file checked so the retry
passes. Sibling edits to the same file in the same parallel batch are
therefore judged against post-denial state and silently apply, leaving
the file in a state neither version intended.

Hooks see tool calls one at a time, so a batch-wide lock is not
possible. Instead make the partial application explicit: the Edit,
Write, MultiEdit, and condensed denials now name the file and warn
that other edits from the same batch may already have been applied,
and SKILL.md tells agents to send dependent edits sequentially and
re-read the file after a gated batch.
2026-09-18 18:37:01 -04:00
Frank_zhu 7cfc9b3608 fix(gateguard): ignore heredoc prose for tee and path-qualified sinks (#2886)
Expand proven-passive heredoc recognition beyond bare `cat` so documentation
writes via `tee`, `/bin/cat`, and `command cat` no longer trip the destructive
command detector on body text, while still failing closed for shells and pipes.
2026-09-17 17:29:26 +00:00
Geronimo d5dee31321 fix(security): stderr skip, independent audit, home MCP trust, dry-run conflict check
- pre-push: send skip diagnostic to stderr (not stdout) so consumers
  relying on stderr for warnings receive the message
- pre-push: move ECC_PREPUSH_AUDIT outside RUN_CHECKS gate so audit-only
  configurations still check dependencies
- mcp-health-check: classify home config paths as trusted before applying
  workspace opt-in gate; when cwd == home, ~/.claude.json was incorrectly
  blocked as untrusted workspace config
- install-global-git-hooks: check conflicting global core.hooksPath in
  dry-run mode too, so dry-run accurately reflects what apply would do
2026-09-15 00:29:23 +05:30
Geronimo a3c24818bb fix(security): provider error taxonomy, tool leak hardening, hook gate, atomic cleanup
- ollama: connection failures -> LLMError(connection_error), unknown -> LLMError(provider_error); auth only on real 401
- executor: generic model-facing tool failure, diagnostics to logs; add execute_async/all_async; ReActAgent returns structured provider_error
- run-with-flags.js: gate require() on concrete run-export syntax, not bare word match
- codex-legacy-sync: remove tmp file on atomicWriteJson failure
- tests: async tool + ReActAgent awaitable regression coverage
2026-09-14 13:49:18 +05:30
Geronimo 27667bc746 fix(security): harden worker approval, hook traversal, MCP exec, install scripts, git hooks
- orchestrate-codex-worker: drop yolo, default never approval, worktree containment
- run-with-flags-shell: add path traversal containment mirroring JS guard
- mcp-health-check: gate workspace probe, denylist dangerous env, shell-free reconnect with opt-in
- install.sh/ps1: add --ignore-scripts to block postinstall RCE
- git hooks: refuse global hooksPath clobber, remove file disable bypass, gate pre-push repo script execution
- claw.js: remove Windows shell:true, validate model token
- tests: opt into new secure defaults, quote-aware reconnect parsing
2026-09-14 13:24:58 +05:30
wellkilo 987c1e103f fix(metrics): bound incremental snapshot recovery
Cap JSONL line buffering and per-hook catch-up work, persist discard cursors for oversized records, report retention failures, normalize malformed token totals, and strengthen bounded-read regression fixtures.
2026-09-13 04:55:52 +08:00
wellkilo 1b12e19c63 fix(metrics): address incremental snapshot review
Replace global log-signature invalidation with per-session byte-offset cursors, preserve unterminated rows until committed, bound snapshot retention, and persist warning deduplication with atomic cross-process claims. Add regression coverage for concurrent writers, malformed data, rewrites, retention, UTF-8 boundaries, and concurrent warning emission.
2026-09-13 03:41:08 +08:00
wellkilo e1805d7deb perf(metrics): cache cumulative session costs 2026-09-13 01:52:26 +08:00
ZaalandGitHub 4f37387420 fix(hooks): block-no-verify handles stuck optional values and long-option prefixes (#3073)
Two cases the word-level rewrite still got wrong. Short options that take an optional stuck value (-u[mode], -S[keyid]) end the cluster scan, so git commit -uno and -Sn are allowed while -nu stays blocked. Git accepts any unambiguous long-option prefix, so --no-veri and --no-verif on commit, push, merge and rebase are now blocked; --no-verbose stays allowed. Quoted data such as -m "--no-verify" is still treated as data. Independent exact-head review probed 34 commands in-process and against real git with no bypass and no false positive; hook test 35/35, eslint clean, CI 44/44 at the head.
2026-09-12 03:07:29 +01:00
DanteandGitHub 2083c9839a fix(hooks): support Windows linter paths and ESLint 9 (#3076)
pre-bash-commit-quality spawned Windows .cmd/.bat linters unquoted, so a spaced path failed, and passed --format compact, which ESLint 9 removed (#3075). Batch executables now run through cmd.exe with each argument carried in an env token and quoted, with quote, NUL, CR and LF rejected before spawn; non-batch Windows and POSIX paths keep direct argv spawn with shell false. ESLint uses its bundled default formatter, present on 8, 9 and 10. Regression tests cover the batch, non-batch and POSIX branches and the formatter change. Independent exact-head review passed with no P0/P1; CI 44/44 at the head.
2026-09-12 01:46:38 +01:00
haelyra 17b70f56fc Merge reviewed PowerShell enforcement fixes for 2.2.1
# Conflicts:
#	tests/hooks/gateguard-fact-force.test.js
2026-09-07 16:38:41 -04:00
haelyra e0252df02f fix: scope GateGuard exemptions to the project
Address #2921 and complete the segment-anchoring direction in #2979. Preserve explicit absolute exemptions while denying accidental matches in unrelated projects.
2026-09-07 16:27:01 -04:00
haelyraandjackie-cqz 20b1ba423e fix(hooks): preserve complete bounded passthrough payloads
Forward-port #2925 for #2924 and verify ASCII and multibyte over-limit input suppression. Supersedes the overlapping direct-entrypoint fix in #2978.

Co-authored-by: jackie-cqz <2557911191@qq.com>
2026-09-07 16:26:10 -04:00
Nguyen Thanh Dat 79d3fefad1 fix(gateguard): detect dd by command word, not by text match
Review on #2829 found the regex fix widened a false positive: matching
`\bdd\s+if=` against the whole flattened line gated `echo dd if=/dev/zero`
and `grep dd if=/dev/zero file`, neither of which runs dd. That class was
already present before — `echo dd if=x` matched the old arm too — but the
boundary fix extended it to the slash and dot spellings, so the arm now
decides on text position rather than on what is being executed.

dd moves to isDestructiveDd(tokens), next to isDestructiveRm and
isDestructiveGit, and DESTRUCTIVE_SQL_DD goes back to SQL only. The
per-segment loop already tokenizes every executable body, so the check runs
where the command word is known.

This resolves four things the text match could not:

  dd if=/dev/zero of=/dev/sda    was allowed  -> denied  (the reported bug)
  sudo dd if=/dev/zero           was allowed  -> denied
  dd of=/dev/sda if=/dev/zero    was allowed  -> denied  (operands are order-free)
  echo dd if=x                   was denied   -> allowed (pre-existing false positive)

Leading sudo/doas/env, their flags, and VAR=value assignment prefixes are
skipped so a wrapped invocation still resolves to dd; flags are only skipped
once a wrapper has been seen, so the scan cannot walk into an unrelated
command's arguments.

Tests: 6 fail on upstream main, 155 pass with this change.

Refs #2642
2026-09-07 15:48:02 +07:00
Nguyen Thanh Dat 76786616f8 fix(gateguard): deny dd whose input path is not word-initial
DESTRUCTIVE_SQL_DD shared one trailing \b across every alternation arm.
`dd\s+if=` ends in `=`, and a \b after a non-word character only holds when
the NEXT character is a word character, so the arm matched `dd if=x` and
missed every path starting with `/`, `.` or a quote:

  dd if=/dev/zero of=/dev/sda    allowed
  dd if=./disk.img of=/dev/sdb   allowed
  dd if="/dev/zero" of=/dev/sda  allowed

The word-boundary suffix now applies only to the arms that end in a word
character. The split is what keeps the widening bounded: dropping the
trailing \b outright would let `truncate` match `truncated`, and dropping the
leading \b would let `dd if=` match inside `add if=`. Both are covered.

This is the half of #2642 that survived the structural findGitSubcommand()
parser, which already handles `git checkout -- .`.

Not addressed here: `dd of=/dev/sda if=/dev/zero` with the operands reversed
is still allowed, before and after, because the pattern requires `if=`
immediately after `dd`. That is a different defect from the boundary bug and
widening a P0 gate's pattern shape is a maintainer call.

Refs #2642
2026-09-07 15:47:37 +07:00
haelyra 8eeac94af3 fix: preserve PowerShell expansion semantics 2026-09-05 16:32:15 -04:00
haelyra 3ad828db47 fix: address PowerShell review bypasses 2026-09-05 16:12:58 -04:00
5a878131de fix(block-no-verify): split scanner and close quoting bypasses
Merge buildCommentMask into buildScanBoundaries so one pass owns comment mask and scan bounds.
Extract shell-scan primitives to scripts/hooks/lib/shell-scan.js; keep policy in the hook.
Use a sticky heredoc regex instead of copying the remaining input on every <<.
Replace the short-circuiting chain test with clean-leading sequences that exercise checkCommand advance.
Add quoted-executable non-leakage coverage for the scanEnd=length path.
Treat $'…' ANSI-C quoting as a quoted executable word.
Join unquoted backslash-newline for the flag scan and heredoc-line bound.

Co-Authored-By: grok (fleet lane) <noreply@fleet.local>

Co-authored-by: groy75 <groy75@users.noreply.github.com>
2026-09-05 03:32:30 +00:00
b3a2d3586b fix(block-no-verify): bound the flag scan to the enclosing quote/heredoc line
The scanner locked onto a `git commit` literal that sat inside a quoted
string of an OUTER command (a python heredoc's string, a VAR="..."
assignment, a printf JSON payload), then hasNoVerifyFlag() re-tokenized from
that inner offset with a fresh quote state. When quote parity flipped, the
"segment" ran past the enclosing string and picked up an unrelated later
`bash -n` / `sed -n` / `grep -n` as `commit -n`. Two legitimate commands were
blocked this way in one working session.

The first attempt at this PR (d5c428c) fixed the false positive by SKIPPING
`git` tokens judged to be in "data" context. That was the wrong lever: data
becomes executable the moment it is piped to a shell (`echo '...' | bash`,
`bash <<< '...'`, `cat <<EOF | bash`), and no regex list of shell invocations
is complete (`bash --noprofile -c` slipped). Three independent reviewers
(codex, Greptile, CodeRabbit) demonstrated executing bypasses; that commit is
squashed away here and none of its behaviour survives.

This version keeps every `git` candidate visible and keeps main's blocking
surface intact. A single O(n) preprocessing pass (buildScanBoundaries)
records, for each enclosed character, the closing quote or the current
heredoc-body line end. Subcommand discovery and flag tokenization are capped
at that boundary, so tokens after the enclosing data can never leak into a
candidate's argument list. Also: comment detection is a single mask pass
instead of a repeated prefix scan; heredoc parsing accepts quoted hyphenated
delimiters and CRLF terminators and preserves the `<<` vs `<<-` tab
distinction; quote-assembled words (`g''it`) are recognized.

Verified (all payloads run through the hook as JSON on stdin):
- red-list, 24 executing forms incl. every reviewer-reported bypass -> exit 2
  on main AND here (CRLF heredoc trailer: 0 on main, 2 here);
- green-list, 16 leak-class false positives -> exit 2 on main, 0 here;
- tests/hooks/block-no-verify.test.js 84/84 (main's 25 retained verbatim,
  d5c428c's 8 removed, 59 table-driven red/green cases added);
  tests/hooks/cursor-block-no-verify.test.js 14/14;
- 204,807-byte command with 8,905 non-command `git` literals: 8.5 ms.

Co-Authored-By: Codex (GPT) <noreply@openai.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EyuZY1LZNuShHiewExekLT
2026-09-04 22:35:09 -04:00