Skip to content

Restore SecurityValidator pattern shipping and yaml manifest (#158)#159

Merged
virtualian merged 1 commit intomainfrom
fix/158-security-validator-noop
Apr 26, 2026
Merged

Restore SecurityValidator pattern shipping and yaml manifest (#158)#159
virtualian merged 1 commit intomainfrom
fix/158-security-validator-noop

Conversation

@virtualian
Copy link
Copy Markdown
Owner

Summary

Closes #158. SecurityValidator has been silently fail-open since v4.0.0+ due to two independent regressions: (a) the yaml package was not resolvable from ~/.pai/hooks/, so parseYaml permanently returned null; and (b) patterns.example.yaml was no longer shipped, so loadPatterns() returned the no-patterns fail-open config. Either alone leaves the hook permissive; both must be fixed.

This PR is the runtime/Releases backport scope (option 1 of three considered). It restores both shipping artefacts so that future installs ship the right files, and adds a behaviour-test harness so the regression cannot return silently. Installer wiring (the new step that would copy package.json to ~/.pai/ and run bun install there) is deferred to a follow-up issue, since the existing installer architecture has no PAI/-tree materialization mechanism today.

Changes

  • Releases/v4.0.3+/.claude/PAI/PAISECURITYSYSTEM/patterns.example.yaml — restored from Releases/v3.0/.claude/skills/PAI/PAISECURITYSYSTEM/patterns.example.yaml (verbatim, version 1.0). Path mirrors codePath('PAI', 'PAISECURITYSYSTEM', 'patterns.example.yaml') under the v4 two-root layout.
  • Releases/v4.0.3+/.claude/package.json — minimal manifest declaring "yaml": "^2.0.0". Lands at ~/.pai/package.json (under the deferred installer wiring); bun install then materializes ~/.pai/node_modules/ which Bun's parent-directory module resolution finds from ~/.pai/hooks/SecurityValidator.hook.ts.
  • Tools/verify-security-validator.sh — behaviour-test harness. Verifies prerequisites and exercises four hook scenarios (block, zero-access, allow, confirm). Payloads are routed via tmpfile stdin so the script's own command line never contains a blocked-pattern substring (otherwise the hook trips on the wrapper invocation itself).

Not changed

Test plan

  • Manually run bash Tools/verify-security-validator.sh after install — expect PASS=8 FAIL=0 exit 0.
  • Confirm ~/.pai/PAI/PAISECURITYSYSTEM/patterns.example.yaml is present.
  • Confirm ~/.pai/package.json is present and ~/.pai/node_modules/yaml/ exists.
  • Trigger a known-blocked Bash pattern through Claude Code and confirm the hook blocks.

Follow-up

A separate issue will track the installer wiring (idempotent step that copies ~/.claude/package.json~/.pai/package.json, runs bun install in ~/.pai/, and materializes ~/.claude/PAI/PAISECURITYSYSTEM/~/.pai/PAI/PAISECURITYSYSTEM/).

Two independent regressions had silently fail-opened SecurityValidator since v4.0.0+:

1. yaml package not resolvable from ~/.pai/hooks/ — Bun resolves dynamic
   await import('yaml') relative to the script file. PR #156 silenced the
   import-time crash with lazy-load + try/catch, but parseYaml permanently
   resolved to null, so loadPatterns() returned the empty fail-open config.

2. patterns.example.yaml was no longer shipped under Releases/v4.0.3+/.
   Last canonical copy lived under Releases/v3.0/.claude/skills/PAI/.
   Even with yaml resolvable, getPatternsPath() returned null and
   loadPatterns() fell through to the no-patterns fail-open branch.

This commit is the runtime/Releases-backport scope (option 1 of three).
Restores both shipping artefacts and adds a behaviour-test harness so the
regression cannot return silently. Installer wiring (a new step that copies
package.json to the runtime root and runs bun install there, plus
materializing the PAI/PAISECURITYSYSTEM/ subtree) is deferred to a follow-up
issue.

Files:
- Releases/v4.0.3+/.claude/PAI/PAISECURITYSYSTEM/patterns.example.yaml
  (restored verbatim from Releases/v3.0/, version 1.0; path mirrors
  codePath('PAI', 'PAISECURITYSYSTEM', ...) under the v4 two-root layout)
- Releases/v4.0.3+/.claude/package.json (declares yaml ^2.0.0)
- Tools/verify-security-validator.sh (8 checks: 4 prerequisites, 4 hook
  scenarios — block, zero-access, allow, confirm)

Verified locally: harness reports PASS=8 FAIL=0; filesystem-destruction
patterns block with exit 2, Read of zero-access SSH key paths blocks with
exit 2, safe commands continue, force-push patterns prompt for
confirmation. Fail-open paths from #156 still degrade gracefully when
yaml or patterns.example.yaml is missing.

SecurityValidator.hook.ts itself is unchanged.

Refs #156 #157
@virtualian virtualian merged commit fa01c81 into main Apr 26, 2026
@virtualian virtualian deleted the fix/158-security-validator-noop branch April 26, 2026 23:07
virtualian added a commit that referenced this pull request Apr 27, 2026
…) (#161)

Closes the regression chain #156#157#158#159#160. New
`migratePaiRuntime` helper copies `~/.claude/{package.json,bun.lock}`
and `~/.claude/PAI/PAISECURITYSYSTEM/` into `~/.pai/`, then runs
`bun install` if `node_modules/yaml/` is absent or the manifest was
just refreshed. Adds `tryExecAt` (structured cwd, no shell) to
`exec.ts`. Tracks `Releases/v4.0.3+/.claude/bun.lock` for reproducible
installs (pins yaml@2.8.3).

Soft-fails per sub-routine — failures surface via
`Tools/verify-security-validator.sh` rather than aborting the install.
After this lands, a fresh-machine install passes the verify script
PASS=8 FAIL=0 with no manual setup.

- New: `Releases/v4.0.3+/.claude/PAI-Install/engine/pai-runtime-migration.ts`
- New: `Releases/v4.0.3+/.claude/bun.lock`
- Edit: `actions.ts` — invoke from `runRepository` after
  `migratePerPackCommands`, both fresh-install and upgrade paths
- Edit: `exec.ts` — add `tryExecAt` for shell-free subprocess calls
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.

SecurityValidator silently a no-op since v4.x — restore patterns.example.yaml shipping AND make yaml resolvable from ~/.pai/hooks/

1 participant