* fix(skill-comply): stop a failed step supplying evidence downstream
`_check_temporal_order` fell back to the raw classifier output whenever
the referenced step was absent from `resolved`. A step only enters
`resolved` once it passes, so "failed" and "not graded yet" were the
same thing to that lookup, and a dependant could pass on an event
belonging to a prerequisite that had failed its own ordering check.
With three steps C, A (before C) and B (after A) and events C@T0,
A@T1, B@T2, A fails and B passed on A's classified event: 2/3 instead
of 1/3. It compounds down a chain, so one failed prerequisite could
leave a five-step workflow reading 4/5.
The grader now tracks which steps have been graded at all. A referenced
step that was graded and is missing from `resolved` failed, and its
events are refused with a reason that says so. A step not graded yet is
a forward reference to a step declared later, and the fallback stays as
it was: that is what makes an out-of-order declaration work, and the
existing regression for it goes red if the fallback is removed instead.
`before_step` deliberately keeps the old fallback. The two fail in
opposite directions: an `after_step` fallback can only turn a failure
into a pass, a `before_step` one can only turn a pass into a failure,
so dropping it would relax a constraint because some other step failed.
* fix(skill-comply): revoke a pass that rested on a later-failing prerequisite
Review of #3109 found the mirror image of the case that PR fixes. `graded`
only catches a prerequisite that had already failed when its dependant was
graded. A step declared *before* its `after_step` is graded against the
classifier's raw events for a step that has not run yet — the fallback that
makes an out-of-order declaration work — and nothing revisited it once that
step went on to fail its own checks.
Add a pass after grading that demotes any detected step whose `after_step`
ended up failing, repeated to a fixed point: one demotion can invalidate
whatever depended on it, in either declaration order. Demotion only removes
passes, so it terminates. `compliance_rate` is computed from the demoted
results.
Also from review: build `graded` and the chain test's step list as new
objects rather than mutating (AGENTS.md immutability rule), and annotate the
injected mocks in the tests this PR owns.
* 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. Fixesaffaan-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.
The temporary HTTP server that export-pdf.sh spins up to render a deck
joined the decoded request path onto SERVE_DIR without checking the
resolved path, and listened on all interfaces. A request such as
`/..%2f..%2f..%2fetc%2fpasswd` (raw, or issued by a malicious deck via
fetch() while it renders) returned files outside the deck directory, and
any host on the network could hit the port while an export was running.
- resolve the decoded path against SERVE_ROOT and answer 403 when the
result is not inside it (handles ../, %2e%2e, %2f and query strings)
- answer 400 on malformed percent-encoding instead of throwing
- listen on 127.0.0.1 only; the only client is the local headless browser
Verified with raw HTTP requests against the handler: `/` and
`/index.html` -> 200, `/../x`, `/..%2f..%2fx`, `/%2e%2e/x`,
`/deck/../../x` -> 403, unknown file -> 404, `/%zz` -> 400, and
server.address() reports 127.0.0.1.
Fixes#3101
scripts/plan-canvas.js and scripts/control-pane.js both define their own
openBrowser helpers for launching the user's default browser. The two
implementations have diverged:
- plan-canvas.js dispatches across darwin/win32/linux but its
try/spawn try/catch does not catch async child errors (ENOENT/EACCES
on hosts without the launcher command). Returns a bare true/false.
- control-pane.js is darwin-only and silently returns early on
Windows/Linux, so the two scripts behave inconsistently across
platforms.
When the launcher command is missing (e.g. headless CI without xdg-open)
the JSON output still says 'browser: opened', lying to the agent.
Changes:
- scripts/lib/platform-launch.js: shared openBrowser helper that
dispatches per platform, wires child 'error' so ENOENT/EACCES
propagate, and returns {opened, reason} instead of a bare boolean.
- scripts/plan-canvas.js: drops its local helper, imports the shared
one, and adds browserReason to its JSON output.
- scripts/control-pane.js: replaces its darwin-only branch with the
shared helper and logs the structured reason on failure.
- tests/lib/platform-launch.test.js: 7 node:test cases covering
opener-command selection, invalid-URL guard, structured-result
shape, and a smoke run for the actual spawn.
Verification: node --test tests/lib/platform-launch.test.js → 7/7 pass.
Co-authored-by: auyua9 <auyua9@users.noreply.github.com>
The Claude Code docs page at code.claude.com/docs/en/mcp-overview no
longer exists (404). The current MCP page is
https://code.claude.com/docs/en/mcp ("Connect Claude Code to tools via
MCP"). Update the link in the English, zh-CN, and tr versions of the
shortform guide.
Co-authored-by: kuishou68 <kuishou68@users.noreply.github.com>
Add focused security regression for sdk-cli allowlisting and document
IOC scan + allowlist probe output under .pr/security-evidence-3171.md.
Signed-off-by: Frank_zhu <58329837+Frank-zhu0404@users.noreply.github.com>
Windows: compare repo identity via normalizeRepoPath/sameRepoIdentity (8.3 short names, case, separators; inode fallback). All nine windows-latest jobs green on 52587005.