Commit Graph
524 Commits
Author SHA1 Message Date
haelyra 0cfce78d35 maint: integrate reviewed contribution #3568 2026-10-29 11:28:21 -07:00
haelyra 14ba1e211f maint: integrate reviewed contribution #3777 2026-10-29 11:27:40 -07:00
haelyra 291635f7f9 maint: integrate reviewed contribution #3776 2026-10-29 11:27:01 -07:00
haelyra ff9095f6f7 maint: integrate reviewed contribution #3775 2026-10-29 11:26:59 -07:00
haelyra 65d83f9290 maint: integrate reviewed contribution #3669 2026-10-29 11:25:30 -07:00
haelyra c7db9b410f maint: integrate reviewed contribution #3657 2026-10-29 11:25:29 -07:00
haelyra cbdd9be209 maint: integrate reviewed contribution #3778 2026-10-29 11:23:21 -07:00
haelyra 9aed578c67 fix: integrate symlink-aware GateGuard exemptions from #3726 2026-10-29 11:20:48 -07:00
haelyra 8e00137b04 fix: integrate observer identity and lease repair with canonical root matching 2026-10-29 11:19:43 -07:00
Brian La Plume 4a76f78411 fix(gateguard): expand unique sudo long-option prefixes
getopt_long accepts a unique prefix, so sudo --use root rm -rf build is
--user root and the resolver read root as the command. Expand unique
prefixes from sudo's long option table before the value/non-exec checks.
--help joins the non-executing modes.
2026-10-28 12:28:55 -07:00
Brian La Plume a46e36d488 fix(gateguard): sudo env assignments between options, missing value flags
sudo accepts VAR=value between its options (parse_args.c is_envar), so
sudo FOO=1 -u root rm -rf resolved -u as the command. Keep reading options
past assignments. Add sudo -R/--chroot, -a/--auth-type, -c/--login-class
and doas -a as value flags, and stop a short-cluster scan at any unknown
letter, so sudo -R/live rm -rf no longer reads the l in /live as -l.
doas -C only checks config, so it is non-executing; the existing doas -C dd
case moves to the passive list.
2026-10-28 12:13:25 -07:00
Brian La Plume 9f05c6fe01 test(run-with-flags): pin hook enablement env in GateGuard truncation tests 2026-10-28 11:55:06 -07:00
Brian La Plume fbb9d34fd1 test(config-protection): missing protected target before an existing one 2026-10-28 11:54:16 -07:00
Brian La Plume 7253c8a261 fix(gateguard): treat sudo list/validate/edit modes as non-executing
sudo -l/--list, -v, -K, -V and -e/--edit (and doas -L) do not run the
command operand, so sudo -l rm -rf build was denied as destructive. The
privilege-wrapper resolver now reports no executable command for those
modes. Short clusters stop at the first value flag, so sudo -ul rm -rf
(user l) stays destructive.
2026-10-28 11:54:13 -07:00
Muhammad Zubair b3b13350d1 test: isolate HOME in the test files that write to the real home directory 2026-10-28 12:20:21 +05:00
Wen 7d44734328 fix(continuous-learning-v2): redact numeric DSN passwords cut at the truncation boundary 2026-10-27 16:52:44 -03:00
Wen 64f28b6096 fix(continuous-learning-v2): redact tokens after JSON escapes and truncated DSNs
Dict tool I/O is JSON-encoded before scrubbing, so a newline before a bare
GitHub token or AWS key id became a literal \n and the leading \b never
matched. Replace it with a lookbehind that also accepts a preceding JSON
escape (\b \f \n \r \t or \uXXXX).

Capture truncation (5000 chars, 2000 in the parse-error fallback) can cut
a DSN right before its '@', which defeated the (?=@) password lookahead.
Let the password run to end of string, except a bare trailing host:port.

Applied to both matcher copies. Regression tests cover structured output
with tokens after escaped whitespace and DSNs cut before '@' on the
string, structured input/output and fallback paths. Refresh registryDigest.

Signed-off-by: Wen <jwcastillo@gmail.com>
2026-10-27 16:52:44 -03:00
Wen 4d17020f49 fix(continuous-learning-v2): redact cloud keys, DSNs and PEM blocks in observations
The observation scrubber in observe.sh only redacted a value placed
directly after a bare keyword (token=, secret:, ...). These reached
observations.jsonl in plain text:

- AWS_SECRET_ACCESS_KEY=... / aws_secret_access_key = ... / SECRET_KEY=
- scheme://user:password@host DSN passwords
- PEM private key blocks
- ghp_/gho_/ghu_/ghs_/ghr_ and github_pat_ tokens, AKIA/ASIA key ids

Both identical _SECRET_RE copies (parse-error fallback and main path)
now accept an optional _access_key/_key suffix on the keyword, and a
new _SECRET_VALUE_RE redacts the value-shaped secrets above. Every
branch is anchored on a literal prefix with bounded classes, keeping
the linear-time constraint from #2278.

The existing redaction test gains canaries for each case on both the
input and output paths, plus a negative case that ordinary code,
credential-free URLs and SSH remotes are left untouched.

registryDigest in skill-triggers@1.json is refreshed, which
validate-context-profiles requires after any skills/ change.

Signed-off-by: Wen <jwcastillo@gmail.com>
2026-10-27 16:52:44 -03:00
Brian La Plume 646b4d1442 fix(gateguard): classify rm/Git behind sudo/doas/env/command wrappers
isDestructiveRm/isDestructiveGit keyed on argv[0], so sudo rm -rf,
command rm -rf and env git reset --hard read as calls to the wrapper and
were allowed once the routine shell gate was satisfied. Run both checks on
the unwrapLeadWrappers argv too, the resolution the dd and sh -c paths
already use. Overlaps #2832, which also reworks sh -c parsing and has
conflicts with main; this is the minimal rm/Git part.
2026-10-27 11:06:20 -07:00
Brian La Plume 1d55d690b9 fix(run-with-flags): fail closed for oversized Bash GateGuard input
Fixes #3762. pre:bash:gateguard-fact-force was missing from
FAIL_CLOSED_ON_TRUNCATION_HOOKS, so a Bash payload over the stdin cap
exited 0 while the Edit and PowerShell GateGuard ids blocked. Share one
GateGuard id set for the fail-closed list and the off-switch branch, and
add runner-level tests for all three ids plus each off switch.
2026-10-27 11:03:36 -07:00
Brian La Plume ed2f02dd06 fix(config-protection): check MultiEdit edits[].file_path targets
Fixes #3761. config-protection only read tool_input.file_path, so a MultiEdit
with the config path inside edits[] edited an existing ruff.toml/.eslintrc
without the block. Check every effective target and block if any is an
existing protected config.
2026-10-27 11:03:02 -07:00
haelyra e10f72413f test(hooks): isolate Git fixtures from inherited env 2026-10-25 19:41:21 -04:00
haelyra 0a7a9a718e fix(hooks): use session repository for console warnings 2026-10-25 19:32:16 -04:00
haelyra 605e006137 Integrate L4XB PR #3662 2026-10-25 19:30:50 -04:00
haelyra 9c8a66b696 Integrate L4XB PR #3664 2026-10-25 19:30:50 -04:00
haelyra 0e7588cec5 Integrate L4XB PR #3667 2026-10-25 19:30:50 -04:00
haelyra 6ddc4ea007 Integrate L4XB PR #3666 2026-10-25 19:30:50 -04:00
Gaurav Dubey b98bff4a45 fix(observer): keep project context and leases aligned after remote changes
Integrates #3697 on current main. Prefer the observer project ID, retain a sole legacy ID until the new observer folder exists, and release the ending session lease across same-root aliases. Replace the unsafe live merge hint with read-only project inspection, and validate alias IDs before path use. #3711 independently identified the computed-ID selection gap; its early switch without observer data and unchanged SessionEnd path were not adopted.
2026-10-25 17:54:47 -04:00
Gaurav Dubey dcb947a4e1 fix(gateguard): judge exempt globs against real target location
Port #3656 onto current main, preserving both the contributor fix and the unrelated current changelog entry. Includes the follow-up glob normalization from 2c11fd74.
2026-10-25 17:47:30 -04:00
Gaurav DubeyandClaude Opus 5.5 ce37e4d16d test(hooks): pin hook flags in the held-open stdin helper
The held-open subprocess cases inherited ECC_HOOKS_ENABLED and
ECC_DISABLED_HOOKS from the caller, so disabling block-no-verify or all
hooks made them fail even though the dispatcher behaved correctly. Pin
both flags in the spawned environment.

Refs #3665

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 00:52:59 +00:00
Gaurav DubeyandClaude Opus 5.5 707e0e1956 test(hooks): cover a complete Bash request on held-open stdin
The held-open subprocess cases only sent partial JSON. Add a complete
`git commit --no-verify` request with stdin left open and assert the
dispatcher reaches block-no-verify instead of reporting incomplete input.

Refs #3665

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 00:33:46 +00:00
Gaurav DubeyandClaude Opus 5.5 2600c70298 fix(hooks): fail closed on incomplete input in bash-hook-dispatcher
- bash-hook-dispatcher: honor the reader's truncated flag. pre-Bash
  blocks (exit 2) because safety checks need the complete request;
  post-Bash skips its hooks and does not echo a partial payload.
- resolveStdinIdleTimeout: treat whitespace-only values as unset instead
  of Number('  ') === 0 silently disabling the deadline.
- isCompleteJsonObject: do not accept a JSON array as a hook payload.
- tests: wait for the child's close event so stdio is drained before
  asserting; cover both bash-hook-dispatcher modes.
- hooks/README.md: keep the example below the 5 s per-hook timeout.

Refs #3665

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 23:18:47 +00:00
Gaurav DubeyandClaude Opus 5.5 943eb826b5 fix(hooks): clamp the stdin idle deadline to the timer maximum
setTimeout fires after 1 ms for any delay above 2147483647 ms, so a large
ECC_HOOK_INPUT_IDLE_TIMEOUT_MS turned the deadline into ~1 ms and marked
every hook input incomplete. Clamp to the maximum with a diagnostic, and
surface the invalid-value diagnostic when readStdinRaw resolves the env.

Refs #3665

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 22:54:14 +00:00
Gaurav DubeyandClaude Opus 5.5 0ca9eeb3ce fix(hooks): stop hook stdin reads when the caller never sends EOF
readStdinRaw() only settled on end, error or close. If the parent kept
stdin open without writing EOF, every hook that reads through it waited
until the harness killed it (an 8-10 s stall per call).

Add an idle deadline (default 3000 ms, ECC_HOOK_INPUT_IDLE_TIMEOUT_MS,
0 disables). It is reset on each chunk, unref()ed, and on expiry detaches
from and pauses the stream so a held-open pipe cannot keep the hook alive.
Input that already parses as a complete JSON object is accepted; anything
else is marked truncated so fail-closed safety hooks still block.

bash-hook-dispatcher.js now reads through the shared reader instead of its
own end/error-only loop.

Fixes #3665

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 22:39:06 +00:00
L4XB 10e7c5800f fix(hooks): recognize Prettier's TypeScript and JSON5 config files
Prettier's config searcher also reads .prettierrc.json5, .prettierrc.ts/.mts/.cts and prettier.config.ts/.mts/.cts. resolve-formatter knew none of them, so a project configured only through prettier.config.ts was never formatted by post-edit-format, quality-gate or the Stop batch. config-protection's rc pattern stopped at .yaml/.toml, so an existing .prettierrc.ts or .prettierrc.json5 could be edited freely.
2026-10-22 23:51:32 +02:00
L4XB cd1f5b74e2 fix(hooks): match tsc diagnostics by the file they name
The Stop typecheck reported every tsc line that contained the edited path anywhere. A root index.ts has the relative path `index.ts`, which every `src/index.ts(...)` diagnostic contains, so a clean root file was reported with the errors of src/index.ts. The same happened to src/utils.ts with web/src/utils.ts, and to any file named in another file's message.

Compare the path that starts the diagnostic (`path(line,col): `), keeping the slash normalization for Windows.
2026-10-22 23:42:21 +02:00
L4XB b02e02dcd2 fix(hooks): list modified files from the repository root even with diff.relative
With diff.relative=true, `git diff --name-only` reports paths relative to the working directory, so joining them to the repository root pointed at files that do not exist, and a session in a subdirectory again got no console.log warning. getGitModifiedFiles now runs `git -c diff.relative=false diff --name-only HEAD`. The -c form also works on Git versions older than --no-relative.
2026-10-22 23:01:26 +02:00
L4XB 5311485e94 fix(hooks): keep a backslash in double quotes as bash does in the dev-server block
readToken dropped every backslash inside double quotes, while bash drops it only before $, `, ", \\ or a newline. `"echo ready\;"` therefore reached the tmux check as `echo ready;`, read as a command separator, and `tmux send-keys -t dev "echo ready\;" "npm run dev" Enter` was blocked although it types into a pane.
2026-10-22 22:45:28 +02:00
L4XB adf435d36f fix(hooks): find console.log in modified files from a subdirectory session
check-console-log took the paths from `git diff --name-only HEAD`, which are relative to the repository root, and checked them with fs.existsSync from the hook's working directory. In a session started in a subdirectory, such as a monorepo package, no path existed from there, so the hook never warned. Resolve each path from the repository root. The exclusion patterns and the warning still use the root-relative path.
2026-10-22 22:42:08 +02:00
L4XB eee00776be fix(hooks): end a tmux command at a semicolon that ends an argument
tmux also separates commands at a `;` that ends an argument, so `new-session -d 'npm run dev;' run-shell 'npm run dev'` runs the second dev server without a pane. Split there as well. An escaped `\;` at the end of an argument stays literal, as in tmux.
2026-10-22 22:32:33 +02:00
L4XB 87945aac1f fix(hooks): read tmux option clusters and command sequences in the dev-server block
Two forms got the pane exemption although they run the dev server without a pane.

`tmux -vL new-session run-shell ...`: in a cluster, the last option that takes a value (f, L, S, T) takes the next token, here the socket name `new-session`. A `c` anywhere in a cluster (`-c'npm run dev'`) runs a shell command without a pane.

`tmux new-session -d \; run-shell "npm run dev"`: only the first command of a sequence was checked. The sequence is now split at `;`, and it is exempt only when every command in it that runs the dev server is a pane command. This also closes the same hole for the plain `tmux new-session ... \; run-shell`, which the old regex exempted.
2026-10-22 21:59:23 +02:00
L4XB 99aa85dde9 fix(hooks): let the dev-server block see a tmux launch past tmux options
The block allowed a dev server only when the segment began with `tmux new`, `new-session`, `new-window` or `split-window`. It therefore blocked dev servers that do start in a tmux pane, the way its own message asks: `tmux -L work new-session ...` and `tmux -f conf new-session ...` (tmux options before the command), `/opt/homebrew/bin/tmux new-session ...` and `env TERM=... tmux new-session ...`, the aliases `neww` and `splitw`, and `tmux send-keys -t dev 'npm run dev' Enter` or `respawn-pane` for a session that exists.

Read the tmux command from the parsed segment instead, after the leading command word and tmux's own options, and allow the commands that run their shell command in a pane. `tmux run-shell` and `tmux -c` run it without one and stay blocked.
2026-10-22 21:15:25 +02:00
yyx db29f112ff fix(hooks): preserve usage records at transcript tail boundaries 2026-10-21 14:58:50 +08:00
KET ⚡ 35d7c698e9 fix(hooks): read BOM-prefixed managed hook preferences 2026-10-15 23:22:53 +05:30
haelyra 1abc625de9 fix: integrate six focused workflow repairs with canonical registry identity 2026-10-11 11:19:21 -04:00
haelyra c4b8325945 fix: prepare six focused ECC reliability repairs
Preserve complete source ancestry for PRs #3512, #3513, #3515, #3516, #3517 and #3518. Repair bounded recovery, alias compatibility and evaluator packaging gaps while keeping registry reuse inside the canonical loader. Verified actual hook, setup, dashboard, evaluator, sandbox and profile workflows plus full npm, Python, lint and supply-chain checks. ECC-043 preparation only; integration remains with the delivery owner.
2026-10-11 11:10:53 -04:00
haelyra a73e04cc6d fix: recover failed ledger batches and preserve project state 2026-10-11 09:42:05 -04:00
haelyra 8a404dbbdc fix: preserve story projections and blank provider defaults 2026-10-11 08:56:29 -04:00
chenhz01 cffcf05e7a hooks: sed labels end at semicolon — commands after b/t/T/: are still scanned
CodeRabbit security finding on the merged sed work (CWE-693, verified live
on main via #3495): GNU sed labels for b, t, T and : end at ';' or newline —
verified against GNU sed 4.9 ('bar;e cmd' parses label 'a' and still runs
cmd; '}' is not a label terminator). Reading those operands to end of line
let 'sed :a;e git commit --no-verify' pass the hook while sed executed the
e command.

Labels are now read up to ';' or newline and command parsing resumes after
them. Text, filename and quit operands (a, c, i, r, w, q, Q) keep the
end-of-line reading: nothing after them on the line executes.

5 regression tests pin the three bypass paths and the benign boundary
(a bare label and a bare branch stay data). Suite: 817/817.
2026-10-11 18:52:44 +08:00
haelyra a194883e0d test(windows): pin native Bash and normalize executable paths 2026-10-11 05:31:15 -04:00