1734 Commits

  • test(hooks): regression coverage for round 1 review fixes
    9 new cases locking in the behavior added by the previous two
    commits. Each was verified to fail before the fix and pass after.
    
    Greptile — quote-aware depth counting:
      - blocks $(echo ")"; (npm run dev))
      - blocks (echo ")"; npm run dev)
      - allows $(echo "(npm run dev)") — () inside double-quoted body is literal
    
    Greptile — brace groups:
      - blocks { npm run dev; }
      - blocks echo hi && { npm run dev; }
      - allows {npm run dev} — bash brace-group syntax requires a space after {
    
    CodeRabbit — missing package-manager variants:
      - blocks yarn run dev (yarn 1.x convention)
      - blocks bun dev (bun bare form)
    
    CodeRabbit nitpick — symmetric quote test:
      - blocks echo "$(npm run dev)" — double-quoted substitution still substitutes
    
    The `{npm run dev}` allow case is intentional: bash treats `{` as
    a reserved word only when followed by whitespace. The pre-fix code
    already passed this through, but until now we never asserted it,
    so a future change to brace handling could silently start blocking
    literal `{npm` tokens.
  • fix(hooks): cover brace groups + yarn-run/bun-bare dev variants
    Two false-negatives surfaced in PR #1889 review:
    
    1. Brace-group bypass (Greptile).
       `{ npm run dev; }` evaluates the dev command in the *current*
       shell — semantically distinct from `( ... )` but with the same
       effect for this hook. `splitShellSegments` correctly cleaves the
       group at `;` into `["{ npm run dev", "}"]`, but the first segment's
       leading token under `readToken` is the bare `{`, which was not in
       `DEV_COMMAND_WORDS`, so the dev-pattern check was skipped.
    
       Fix: treat `{` and `}` as no-op tokens in `getLeadingCommandWord`
       so we keep walking to the real command word. Matches how shell
       itself parses brace groups (the braces are reserved words, not
       commands). Bash requires a space after `{` and a terminator before
       `}` for an actual group, so `{npm run dev}` correctly remains
       allowed (single token `{npm`, not in `DEV_COMMAND_WORDS`).
    
    2. Missing yarn-run / bun-bare variants (CodeRabbit).
       Both `yarn dev` *and* `yarn run dev` are valid (the latter is what
       `package.json` actually wires `dev` to under yarn 1.x). The same
       `(run )?` symmetry applies to bun. The previous `DEV_PATTERN` only
       matched `yarn\s+dev` and `bun\s+run\s+dev`, allowing the cross
       forms to pass through silently.
    
       Fix: `yarn(?:\s+run)?\s+dev` and `bun(?:\s+run)?\s+dev` — same
       shape `pnpm(?:\s+run)?\s+dev` was already using.
    
    Verified after this commit (every form now exits 2):
    
      { npm run dev; }
      { npm run dev ; }
      echo hi && { npm run dev; }
      ({ npm run dev; })
      $( { npm run dev; } )
      yarn run dev
      bun dev
    
    Verified still allowed (no regression):
    
      echo "{ npm run dev; }"   # literal inside double quotes
      {npm run dev}             # not a brace group per bash syntax
  • fix(lib): track quote state inside command-substitution depth counters
    Greptile flagged a bypass in PR #1889: `$(echo ")"; (npm run dev))`
    threaded the depth-counting loops in `extractCommandSubstitutions`
    and `extractSubshellGroups` to terminate early, because a literal `)`
    inside double quotes was treated as a real closing paren. The
    truncated body then ended in a dangling `"` that toggled `inDouble`
    in the outer scan, masking the subsequent `(npm run dev)` group from
    extraction.
    
    Reproduced (before this commit) by piping the synthetic PreToolUse
    payload `{"tool_input":{"command":"$(echo \")\"; (npm run dev))"}}`
    into `scripts/hooks/pre-bash-dev-server-block.js` and observing
    exit 0 (allow) where the dev pattern is clearly present.
    
    Fix: each `$(...)` and `(...)` body loop now tracks its own
    single/double quote state and only treats `(` / `)` as depth
    delimiters when outside quotes. The quoted `)` no longer closes
    the group early, the body now extends to the real closing paren,
    and the outer scan's quote state remains untouched.
    
    After this commit:
      $ echo '{"tool_input":{"command":"$(echo \")\"; (npm run dev))"}}' \
          | node scripts/hooks/pre-bash-dev-server-block.js; echo $?
      2
    
    The symmetric form `$(echo "(npm run dev)")` correctly remains
    allowed (bash does not honor `(...)` inside double quotes).
  • test(hooks): regression coverage for dev-server-block subshell bypass
    Lock in the behavior added by the previous commit. Each new case was
    verified to fail before the fix and pass after.
    
    Bypasses now blocked (exit 2):
    - \$(npm run dev)              command substitution
    - \`npm run dev\`              backtick substitution
    - echo \$(npm run dev)         substitution inside an argument
    - (npm run dev)               plain subshell group
    - \$(echo a; npm run dev)      substitution containing a sequenced segment
    - (pnpm dev)                  plain subshell group, alt package manager
    
    Allow cases — explicitly proven NOT to regress so the fix doesn't
    over-block legitimate uses:
    - (tmux new-session -d -s dev "npm run dev")   tmux launcher inside ()
    - git commit -m '(npm run dev)'                literal in single quotes
    - echo "(npm run dev)"                         literal in double quotes
      (bash does NOT subshell () inside double quotes)
    - git commit -m '\$(npm run dev) fix'          literal in single quotes
    
    Single- and double-quote allow cases are important: they distinguish a
    real subshell construct from one that's just text inside a string,
    which is what `extractSubshellGroups` / `extractCommandSubstitutions`
    quote-awareness is for.
  • fix(hooks): close subshell bypass in pre-bash-dev-server-block
    Before this commit the dev-server-block hook ran the leading-command
    and dev-pattern check only against the top-level segments returned by
    `splitShellSegments`, which doesn't split on `$(...)`, backticks, or
    plain `(...)`. That left the policy bypassable by wrapping a dev
    command in any of those constructs:
    
      $(npm run dev)
      `npm run dev`
      echo $(npm run dev)
      (npm run dev)
    
    Each verified by piping a synthetic PreToolUse payload into the hook
    on this branch: every form above returned exit 0 (allow) where a plain
    `npm run dev` correctly returned exit 2 (block).
    
    Fix: expand the check space before running the leading-command rule.
    A small BFS walks the raw command, harvesting bodies from
    `extractCommandSubstitutions` (`$(...)` and backticks) and from
    `extractSubshellGroups` (plain `(...)`), then splits each harvested
    body through `splitShellSegments` and feeds the result into the
    existing `isBlockedDevSegment` check.
    
    This preserves every existing allow case (`tmux new-session -d -s dev
    "npm run dev"`, quoted-string mentions like `git commit -m "npm run
    dev fix"`, `echo hi`) because the leading-command rule is unchanged —
    only the set of segments it runs against grew.
    
    Known limitation, not fixed here: `eval "$(echo npm run dev)"` still
    slips through because the substitution body's leading command is
    `echo`, and statically modeling echo's output to recover the executed
    command is out of scope. The same class affects `gateguard-fact-force`
    (via `eval "$(echo rm -rf /)"` etc.) and is best addressed in both
    hooks together as a follow-up rather than as a one-off here.
  • feat(lib): add extractSubshellGroups for plain (...) subshells
    `extractCommandSubstitutions` only walks `$(...)` and backticks — the two
    shell constructs whose bodies are captured as strings. Bash also has
    plain `(...)` subshells (e.g. `(npm run dev)`), where the body executes
    in a child shell but is not value-captured. Our PreToolUse hooks need
    to peer inside those too, because a `(...)` group bypasses the
    top-level segment splitter just like `$(...)` does.
    
    This commit adds a sibling extractor with the same conventions as
    `extractCommandSubstitutions`:
    
    - single quotes literal — `'(npm run dev)'` is a string, ignored
    - double quotes literal for parens — `"(npm run dev)"` is a string
      (bash only honors `$(...)`, not bare `(...)`, inside double quotes)
    - skips `$(...)` and backtick spans so we don't double-extract
      bodies the other helper already handles
    - recurses into its own bodies for nested groups
    
    No consumer yet; the next commit wires both extractors into
    `scripts/hooks/pre-bash-dev-server-block.js` to close the subshell
    bypass surface.
  • feat(lib): extract shell command-substitution parser to shared lib
    Extract the `extractCommandSubstitutions` function originally
    introduced in scripts/hooks/gateguard-fact-force.js (PR #1853
    round 2) into scripts/lib/shell-substitution.js so other PreToolUse
    hooks can reuse the same single-quote-aware, double-quote-aware,
    nested-subshell-aware parser without duplicating it.
    
    No behavior change in this commit — the function body is copied
    verbatim and exposed via `module.exports`. The next commit wires it
    into scripts/hooks/pre-bash-dev-server-block.js to close that hook's
    own subshell-bypass holes.
    
    gateguard-fact-force.js still defines its own private copy of the
    function; consolidating both call sites onto this shared lib is a
    follow-up worth doing once this PR lands, but is intentionally out
    of scope here to keep the diff focused on the dev-server-block fix.
  • docs: sync roadmap after AgentShield corpus expansion
    Records AgentShield PR #82 and moves the next AgentShield roadmap slice to hosted evidence-pack workflow depth.
  • docs: sync roadmap after AgentShield remediation workflows
    Records AgentShield PR #81 and advances the next AgentShield roadmap slice after remediation workflow phases landed.
  • docs: sync roadmap after AgentShield corpus recommendations
    Syncs the ECC 2.0 GA roadmap after AgentShield PR #80 landed corpus accuracy recommendations.
  • docs: sync AgentShield adapter roadmap
    Record AgentShield #68/#69 in the ECC GA roadmap and update the next enterprise slice.
  • ci: gate observability on release safety evidence
    Add release-safety evidence coverage to observability readiness and refresh rc.1 publication gate docs.
  • docs: gate ECC progress sync readiness
    Make the ECC 2.0 GitHub/Linear/handoff/roadmap progress-sync model part of the local observability readiness gate instead of leaving it as roadmap prose only.
    
    - add `docs/architecture/progress-sync-contract.md` for GitHub, Linear, handoff, roadmap, and work-items sync
    - add a `Tracker Sync` check to `scripts/observability-readiness.js`
    - update observability tests with passing and missing-contract coverage
    - update observability and GA roadmap docs so the local readiness gate is now 18/18 and records #1848 supply-chain hardening evidence
    
    Validation:
    - node tests/scripts/observability-readiness.test.js (9 passed, 0 failed)
    - npm run observability:ready -- --format json (18/18, ready true)
    - npx markdownlint-cli 'docs/architecture/progress-sync-contract.md' 'docs/architecture/observability-readiness.md' 'docs/ECC-2.0-GA-ROADMAP.md'
    - git diff --check
    - node tests/docs/ecc2-release-surface.test.js (18 passed)
    - node tests/run-all.js (2378 passed, 0 failed)
    - GitHub CI for #1849 green across Ubuntu, Windows, and macOS
    
    No release, tag, npm publish, plugin tag, marketplace submission, or announcement was performed.
  • docs: add supply-chain incident response playbook
    Add a repo-level supply-chain incident response playbook for npm/GitHub Actions package-registry incidents, anchored on the May 2026 TanStack compromise and prior Shai-Hulud-style npm incidents.
    
    - add `docs/security/supply-chain-incident-response.md` with exposure checks, immediate response steps, workflow rules, publication rules, and escalation triggers
    - link the playbook from `SECURITY.md`
    - reject `pull_request_target` workflows that restore or save shared dependency caches
    - add a regression test for the new `pull_request_target + actions/cache` guardrail
    
    Validation:
    - node tests/ci/validate-workflow-security.test.js (12 passed, 0 failed)
    - node scripts/ci/validate-workflow-security.js (validated 7 workflow files)
    - npx markdownlint-cli 'SECURITY.md' 'docs/security/supply-chain-incident-response.md'
    - npx markdownlint-cli '**/*.md' --ignore node_modules
    - git diff --check
    - node tests/run-all.js (2377 passed, 0 failed)
    - GitHub CI for #1848 green across Ubuntu, Windows, and macOS
    
    No release, tag, npm publish, plugin tag, marketplace submission, or announcement was performed.
  • docs: refresh rc1 release readiness evidence
    Add the May 13 rc.1 publication evidence refresh and update the release-readiness/GA roadmap gates after #1846.
    
    - record current queue, security-gate, harness audit, adapter, observability, Node, markdownlint, release-surface, npm publish-surface, and ecc2 Rust evidence
    - update the publication-readiness checklist with the May 13 evidence artifact
    - normalize zh-CN CLAUDE list markers so markdownlint passes
    
    Validation:
    - node tests/docs/ecc2-release-surface.test.js
    - node tests/docs/harness-adapter-compliance.test.js
    - node tests/docs/stale-pr-salvage-ledger.test.js
    - npx markdownlint-cli '**/*.md' --ignore node_modules
    - git diff --check
    - node tests/run-all.js (2376 passed, 0 failed)
    - npm run harness:audit -- --format json (70/70)
    - npm run harness:adapters -- --check
    - npm run observability:ready -- --format json (16/16)
    - node tests/scripts/npm-publish-surface.test.js
    - cd ecc2 && cargo test (462 passed, 0 failed)
    
    No release, tag, npm publish, plugin tag, marketplace submission, or announcement was performed.
  • ci: require npm audit signature checks
    Require npm registry signature verification wherever workflow npm audit checks run.
    
    - add npm audit signatures to CI Security Scan and maintenance security audit jobs
    - teach the workflow security validator to reject npm audit without signature verification
    - keep the repair and Copilot prompt tests portable across Windows path/case and CRLF frontmatter behavior
    
    Validation:
    - node tests/run-all.js (2376 passed, 0 failed)
    - CI current-head matrix green on #1846
  • feat: add GitHub Copilot prompt support
    Adds GitHub Copilot VS Code instruction and prompt files for ECC workflows, with VS Code prompt frontmatter/settings aligned to current docs and tests covering the surface.
    
    Co-authored-by: Girish Kanjiyani <girish.kanjiyani5040@gmail.com>
    Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
  • docs: tighten agent capability posture
    Remove shell access from two agents that do not need it and reword PyTorch autograd guidance that AgentShield flagged as encoded-payload-like text. AgentShield remains B/75 while findings drop 316->310 and high findings drop 26->21. Local tests passed 2369/2369; full GitHub Actions matrix green.
  • fix: close block-no-verify bypass holes
    Backport Jamkris's fix for case-insensitive core.hooksPath overrides and the git commit -tn template-path false positive. Verified locally on current main with 25/25 block-no-verify tests and node tests/run-all.js passing 2369/2369.
  • docs: add prompt defense baselines
    Add compact prompt-defense baselines to active ECC prompt surfaces and copied CLAUDE examples. AgentShield prompt-defense findings are now zero; local tests passed 2366/2366.
  • ci: harden workflow install boundaries
    - run non-test workflow installs with npm ci --ignore-scripts where lifecycle scripts are not needed\n- reject plain npm ci in workflows with write permissions\n- reject actions/cache in id-token: write workflows to reduce OIDC publish cache-poisoning risk
  • feat: add homelab config skills (VLAN segmentation, Pi-hole DNS, WireGuard VPN) (#1838)
    * feat: add homelab config skills (VLAN, Pi-hole, WireGuard)
    
    Adds three homelab configuration skills, extracted from the stale PR #1413
    with the same safety treatment applied to the previously accepted batch:
    
    - homelab-vlan-segmentation: IoT/guest/trusted/server VLAN design for UniFi,
      pfSense/OPNsense, and MikroTik. All firewall rules add isolation, not remove
      protections. Added change-window guidance and AP trunk port clarification.
    
    - homelab-pihole-dns: Pi-hole install, blocklists, DNS-over-HTTPS, local DNS
      records, troubleshooting. Docker is now the lead install method; bare-metal
      uses inspect-first pattern before running the installer script.
    
    - homelab-wireguard-vpn: WireGuard server, peer config, split tunnel, DDNS.
      Replaced broad iptables FORWARD ACCEPT with scoped directional rules
      (wg0→eth0 forward + established return only). Credentials moved to env
      files with explicit notes against inline secrets and version control.
    
    Continues the contribution from PR #1413; the eight skills/agents from
    that PR are already in main via #1729 and #1731.
    
    * docs: harden homelab skill pack
    
    ---------
    
    Co-authored-by: Affaan Mustafa <affaan@dcube.ai>