Release 0.8.9 → prod - #176
Merged
Merged
Conversation
…ve old path for checking modified files.
…hook `rafter hook pretool/posttool` read stdin with a 5s timeout, but on Node the timeout only bounded OUTPUT latency, not process exit: a piped stdin with no EOF stays in flowing mode and keeps the event loop alive, so after emitting its decision at 5s the process hung indefinitely. Reproduced via `sleep 30 | rafter hook pretool` (outer guard had to SIGKILL it). The fix pauses stdin and removes listeners on the timeout/end/error paths so the loop drains and the process exits (~5s, fail-open). Python already exited (daemon reader thread) — unchanged in behavior. Adds RAFTER_HOOK_STDIN_TIMEOUT_MS (ms, default 5000) as an operator safety valve and to keep the regression tests fast. Both implementations parse it identically: finite and > 0, else fall back to the default — so Infinity / nan / 0 / negative / garbage can never re-create the unbounded wait (Python guarded with math.isfinite to match Node's Number.isFinite, preventing join(timeout=inf)). Regression tests spawn the real hook with a never-closing stdin and assert bounded exit + fail-open decision (Node), and assert the bound + env parsing including non-finite rejection (Python). CLI_SPEC.md documents the bounded read. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(hook): bound stdin read so a never-closing stdin can't wedge the hook
… of #171) Review fixes for the added/modified-line secret scanner: 1. Parity blocker — Python parsed the diff with str.splitlines(), which also breaks on bare CR, form-feed, NEL and U+2028/2029. An added line whose content contained any of those was split onto a token that no longer starts with '+', so the secret tail was silently dropped on Python while Node (which splits on /\r?\n/) caught it. Switch Python to re.split(r"\r?\n", patch). 2. Added-line false-negative (both impls) — a line whose CONTENT starts with '++' serializes as '+++...' and was dropped by the `!startsWith("+++")` guard; a line whose content starts with '++ ' serializes as '+++ ...' and was misread as a file header, corrupting the current file + line counter. Headers only appear before the first @@ (newLine<=0), so guard '+++ '/'--- ' header detection on newLine<=0 and treat any '+' line inside a hunk as content. Adds regression tests in both suites: '++'/'++ ' content captured, CRLF endings, form-feed not split (parity), and rename-with-no-content ignored. Co-Authored-By: Alessio Toniolo <atoniolo76@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update scan.ts to run secret dection on added/modified lines and remove old path for checking modified files.
…+Python parity (sable-7g7) (#170) * feat(audit-skill): opt-in --deep engine via skill-scanner (PoC, sable-7g7) Rebased onto current main; fix CliRunner stderr access in missing-tool test. * feat(audit-skill): complete --deep engine — Node parity, installer, dir support (sable-7g7) Brings the opt-in skill-scanner deep engine to merge-ready per Rome's three design decisions: 1. Node↔Python parity — node/src/scanners/skill-scanner.ts mirrors the Python wrapper (same offline-safe argv + FORBIDDEN_FLAGS invariant, severity map, exit floor, and the cross-runtime deepScan JSON shape). --deep/--engine wired into the Node audit-skill command. 2. Directory targets — audit-skill now accepts a skill file OR directory; the deep engine scans the whole directory (its most thorough mode), quick scan reads SKILL.md. Both runtimes. 3. Managed installer — `agent update-skill-scanner` + `agent init --with-skill-scanner` (both runtimes). Isolated, version-pinned `uv tool install cisco-ai-skill-scanner==2.0.11` (pip --user fallback), list-form subprocess, no shell, no elevation. Opt-in only (not in --all). The zero-dependency quick scan stays the default — this couples, not swaps. Offline guarantee (no --use-llm/--use-virustotal/--use-aidefense/--use-behavioral) is test-enforced in both suites. Docs: deepScan schema + --deep/--engine + offline guarantee + update-skill-scanner in CLI_SPEC.md; README deep-engine note; INTEGRATION_NOTES status updated. Version 0.8.7 -> 0.8.8 (node + python matched). Tests: node tests/skill-scanner.test.ts (14 + 3 binary-gated); python tests/test_agent_audit_skill_deep.py (13 + 4 binary-gated) green. Also fixed a pre-existing CliRunner stderr-access bug in the missing-tool test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(skill review): bring --deep to the current command + easier onboarding (sable-7g7) Moves the deep engine onto `rafter skill review` (the non-deprecated command), up to date with current architecture, and makes it easier to use. - `rafter skill review <path|dir|github:/gitlab:/npm:|--installed> --deep` (alias --engine skill-scanner), both runtimes. Attaches the same `deepScan` block; actionable (medium+) deep findings escalate severity/worst and the exit code. Works across single-skill, multi-skill, and --installed modes (per-skill deepScan), and over remote-fetched temp dirs. - Easier onboarding: when --deep is requested and the engine isn't installed, rafter now OFFERS TO INSTALL IT interactively (TTY) via the managed installer; non-interactive / --json falls back to the install hint + exit 2. - `audit-skill --deep` stays as a deprecated back-compat alias. Shared helpers ensureSkillScanner / ensure_skill_scanner + deepSeverityTier in the scanner modules. Offline-flag invariant unchanged and still test-enforced. Docs: CLI_SPEC `skill review` gains --deep/--engine + deepScan schema; README points --deep at `skill review`. Tests: node tests/skill-review-deep.test.ts + python tests/test_skill_review_deep.py (engine selection always; real deep scans binary-gated). Fixed skill-review-remote.test.ts for the now-async runSkillReview. Validated end-to-end with skill-scanner 2.0.11 installed: a planted malicious skill flags prompt_injection + data_exfiltration (critical, exit 1) identically on both runtimes; benign findings (FILE_MAGIC_MISMATCH etc.) map consistently. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(skill-scanner): add `rafter agent remove-skill-scanner` (easy uninstall) The inverse of update-skill-scanner. Cleanly removes the optional deep engine. - SkillScannerInstaller.uninstall() (both runtimes): tries `uv tool uninstall` first (how install prefers to set it up), then `pip uninstall -y` fallback — we don't durably record which path installed it. Idempotent: a success no-op when it isn't installed. List-form subprocess, fixed package name (no injection), 120s timeouts. - `rafter agent remove-skill-scanner` command (Node + Python). Exit 0 removed / already absent; exit 1 if still on PATH afterward (installed by another tool). - Skills + Rafter's own deps untouched. Tests: build_uninstall_argv (uv + pip forms) and idempotent-when-absent, both runtimes. Docs: CLI_SPEC + README. Dogfooded end-to-end (uninstall → reinstall). update/install success message now points at `rafter skill review --deep`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…wire backend.apiKey (sable-q9to) (#173) * fix(config): harden RAFTER_API_KEY handling — 0600, redaction, wire backend.apiKey (sable-q9to) Three credential-handling gaps (both Node + Python; intended env/flag path was already fine): 1. Plaintext, world-readable. ConfigManager.save() wrote ~/.rafter/config.json with the default umask (typically 0644). Now writes 0600 (dir 0700) and chmods an existing looser file on the next write — it can hold a backend key. 2. Unredacted echo. `config show`/`get`, the `config set` confirmation, and the MCP get_config tool + rafter://config / rafter://policy resources serialized the full config verbatim, handing any stored key to the terminal / MCP client. Added redactConfigSecrets / maskSecretValue (mask values under keys matching api_?key|token|secret|password|credential → "abcd****"); applied at every render path. Pure — the stored config is never mutated. 3. Dead credential path. backend.apiKey existed in the schema but resolveKey never read it. Wired it as the LOWEST-precedence source: --api-key flag > RAFTER_API_KEY env > global config backend.apiKey. Read only from the global ~/.rafter/config.json (load(), never loadWithPolicy/.rafter.yml — verified it does not merge backend.*), so a hostile repo cannot inject a key that redirects scans to another account. Secure-design pass recorded on the bead: it's a bearer token, so 0600 + display redaction is the right boundary (encrypting a local config just moves a KEK next to the ciphertext); precedence mirrors gh/aws/npm. Tests (both suites): redaction helpers, 0600 on fresh + tightened-on-rewrite, and resolveKey precedence flag>env>config. Node 67 / Python 19 config tests pass; broader config+mcp+api suites green. CLI_SPEC.md documents the resolution order, redaction, perms, and the global-only trust boundary. Note: Node key is backend.apiKey, Python is backend.api_key (dataclass snake_case) — same value, per-language path; redaction regex covers both. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(mcp): fix stale config-manager mock after redaction exports (sable-q9to) The redaction work added redactConfigSecrets/isSecretConfigKey/maskSecretValue to config-manager, which server.ts now imports — but the vi.mock in mcp-server-integration.test.ts only stubbed ConfigManager, so those imports resolved to undefined and 4 get_config/resource tests threw. Spread the real module (keeping the pure redaction helpers real) and override only ConfigManager. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Patch bump for the opt-in --deep skill-review engine (sable-7g7) and the RAFTER_API_KEY hardening (sable-q9to). node + python versions matched 0.8.8 -> 0.8.9; CHANGELOG [0.8.9] section added. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Raftersecurity
approved these changes
Jun 20, 2026
The rafter-security-skill.md frontmatter version (Node + Python copies) was left at 0.8.7 through the 0.8.8 and 0.8.9 package bumps, failing the validate-release 'ClawHub skill version matches package version' gate on the main→prod PR (#176). More importantly, publish-clawhub uses this frontmatter version as the ClawHub release version — so deploying as-is would ship the skill at a stale 0.8.7 while npm/PyPI publish 0.8.9 (cross-registry skew). Bump both to match the package version. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n-0.8.9 fix(release): bump ClawHub skill resource versions to 0.8.9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes
main→prod(0.8.7 → 0.8.9). 9 commits; node + python versions matched at0.8.9.What ships
--deepskill-review engine (skill review --deep: couple skill-scanner as opt-in deep engine, Node+Python parity (sable-7g7) #170, sable-7g7) —rafter skill review … --deepcouples Cisco AI Defense'sskill-scannerfor prompt-injection / taint-dataflow / YARA /.pycreview. Offline analyzers only, not bundled (interactive install-offer +update-skill-scanner/remove-skill-scanner), Node+Python parity.audit-skill --deepkept as a deprecated alias.RAFTER_API_KEYhardening (fix(config): harden RAFTER_API_KEY handling — 0600 perms, redaction, wire backend.apiKey (sable-q9to) #173, sable-q9to) —0600config perms, secret redaction at every config render path (incl. MCP), andbackend.apiKeywired as lowest-precedence (global-only) key source.scan/hooknow scan only added/modified lines with corrected diff-parser parity + edge cases.hook pretool.CHANGELOG [0.8.9].Notes
main; this is the promotion.rafter run(remote SAST) on this branch if the prod pipeline hasRAFTER_API_KEY.🤖 Generated with Claude Code