fix: merge latest dev updates into main#24
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- native.ts: add extractContext + formatArgs with matchedField/matchedWord tracing for "Context Sniper" popup — shows dangerous word in context - core.ts: extend evaluatePolicy return with matchedField/matchedWord; per-field scan after dangerous word found; pass through authorizeHeadless - daemon/index.ts: gate SSE broadcast and browser open on browser config flag - LICENSE/package.json/README.md: MIT → Apache-2.0 - .github/workflows/ai-review.yml: add paths-ignore to prevent self-modification - scripts/ai-review.mjs: upgrade to claude-sonnet-4-6, max_tokens 2048 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Wrap diff in <diff>...</diff> markers with untrusted-content notice to mitigate prompt injection - Surface truncation note in posted PR comment when diff exceeds MAX_DIFF_CHARS - Downgrade API errors to warning comments + exit 0 so Anthropic outages don't block PRs - Pin @anthropic-ai/sdk@0.78.0 and @octokit/rest@22.0.1 to prevent supply-chain drift - Add explicit permissions block (contents: read, pull-requests: write) - Exclude dependabot[bot] from triggering review - Add fetch-depth: 0 to checkout step Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… main - ai-review.yml: replace AUTO_PR_TOKEN with GITHUB_TOKEN (permissions block already scopes it correctly — no broad PAT needed) - ai-review.yml: add --ignore-scripts to npm install to block malicious postinstall hooks from transitive dependencies - sync-dev.yml: new workflow — after every push to main, merge main back into dev so release-bot version bumps don't cause recurring README conflicts on the next dev -> main PR Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move @anthropic-ai/sdk and @octokit/rest into devDependencies and switch the ai-review workflow from bare npm install to npm ci --ignore-scripts. This locks all transitive dependencies to the committed lockfile, eliminating supply-chain drift on every CI run. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- context-sniper.ts (new): shared RiskMetadata type, smartTruncate,
extractContext (returns {snippet, lineIndex}), computeRiskMetadata
- native.ts: import from context-sniper, use .snippet on extractContext calls
- core.ts: add tier to evaluatePolicy returns; compute riskMetadata once in
authorizeHeadless; pass it to initNode9SaaS, askDaemon, notifyDaemonViewer
- daemon/index.ts: store and broadcast riskMetadata in PendingEntry
- daemon/ui.html: renderPayload() uses riskMetadata for intent badge, tier,
file path, annotated snippet, matched-word highlight; falls back to raw args
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the Node9 cloud responds with shadowMode:true (org is in shadow mode), print a yellow warning to stderr instead of blocking the agent. The developer sees exactly why it was flagged without being interrupted. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er, and integration tests - Add Zod v3 schema validation (config-schema.ts) with clear error messages for bad config.json — catches literal newlines, invalid regex, unknown keys, bad enums - Fix silent JSON parse fallback in tryLoadConfig: bad config now warns to stderr instead of silently using DEFAULT_CONFIG (which had cloud:true causing unexpected browser/cloud popups when config was invalid) - Fix auditLocalAllow fire-and-forget killed by process.exit: audit mode path now awaits the POST so SaaS receives the event before the process exits - Gate all auditLocalAllow calls on approvers.cloud so cloud:false (privacy mode) never sends data to SaaS - Fix double browser windows when cloud+browser both enabled: RACER 3 (browser) now skips when cloudEnforced, preventing duplicate daemon /check entries - Fix calledFromDaemon guard on terminal status messages to prevent duplicate output - Add check.integration.test.ts: 20 end-to-end tests spawning real node9 check subprocess with isolated HOME dirs and in-process mock SaaS server Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…plied Previously tryLoadConfig warned about invalid fields (e.g. mode:"bad-mode") but still returned the raw object, letting them override valid values from higher-priority config layers. A project-level node9.config.json with mode:"bad-mode" would override the global mode:"audit", bypassing audit mode and triggering the full cloud approval race unexpectedly. sanitizeConfig() now drops top-level keys that fail Zod validation so invalid project configs cannot corrupt the effective merged config. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l test, improve comments - Remove unreliable 200ms sleep from audit+cloud test: auditLocalAllow is awaited before process.exit so the POST is done by the time the subprocess closes; if it ever races here it would be a production bug too - Add task* wildcard test: task_drop_all_tables must be fast-pathed to allow (documents the intentional security trade-off of user-configured wildcards) - Expand runCheck docstring explaining why cwd=tmpHome is needed alongside HOME=tmpHome (avoids inheriting the repo's own node9.config.json) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…noredTools precedence tests - Add `let resolved = false` guard in runCheckAsync to prevent double-resolve when child.kill() is called on timeout (close event fires after kill) - Fix mockServer.close() in afterEach to return a Promise (was fire-and-forget) - Document NODE9_TESTING=1 behavior in file header comment - Add runCheck/runCheckAsync raw string support for malformed payload testing - Add section 10: malformed JSON payload tests (non-JSON, empty, partial JSON) - Add ignoredTools precedence test: task* wildcard + dangerous word in input documents that ignoredTools fast-path bypasses dangerousWords (by design) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The CLI intentionally exits 0 on unparseable JSON (fail-open policy): a transient serialization error must not block the AI session mid-flight. The test was asserting the opposite. Updated all three malformed-payload tests to verify graceful failure (no crash/stack trace) rather than an error exit code. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
core.ts:
- fix review-git-push regex: literal space → \s+ so "git push" can't bypass
- fix getConfig(): environments block was always hardcoded {} and never merged
from global/project config files; now applyLayer() accumulates environments
correctly so strict-mode env overrides actually work
examples/node9.config.json.example:
- remove dangerousWords that caused false positives; keep only mkfs + shred
(catastrophic, unambiguous — everything else handled by smartRules)
- add enterplanmode/enterworktree/exitworktree to ignoredTools
- add execute_query, query, mcp__postgres__*, mcp__github__* to toolInspection
- fix allow-readonly-bash regex: "npm run(build|test)" → "npm run (build|test)"
(was matching "runbuild"/"runtest" instead of "run build"/"run test")
- remove smartRules already covered by built-in defaults:
review-delete-without-where, block-force-push, block-drop-database, review-sudo
- remove "push"/"git" rules entries (match tool *names*, never fire for bash)
- remove non-functional environments block (was silently ignored until above fix)
- add approvalTimeoutMs:30000, version:"1.0", expanded snapshot.tools + ignorePaths
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ule bypass, tests core.ts: - move mergedEnvironments declaration before applyLayer closure so the captured variable is clearly in scope (was hoisted at runtime but misleading to read) - replace unsafe Partial<EnvironmentConfig> spread with field-level validation: only copies requireApproval when it is a boolean, ignoring any other input - add version guard in tryLoadConfig: warns to stderr when a config file declares a version other than "1.0" so future schema changes can be caught early examples/node9.config.json.example: - add notMatches (&&|\|\||;\s*\S) condition to allow-readonly-bash rule so chained commands like "cat /etc/passwd && rm -rf /" are NOT fast-allowed - tighten review-secrets-write file_path regex: add (^|[/\\]) path separator anchor so "notmy.env" no longer matches — only actual dotenv files do tests (advanced_policy.test.ts): - 6 new tests for allow-readonly-bash: verifies safe commands are allowed, &&/||/; chaining is rejected, pipe-only chains remain allowed - 4 new tests for environments merge: project overrides global, type-unsafe values are dropped, multiple envs merge independently Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ets detection - allow-readonly-bash: add notMatches for $() and backtick to prevent command substitution bypass - allow-install-devtools: add notMatches guard for -g/--global flags - review-secrets-write: change to conditionMode any and add path/filename field checks alongside file_path - Add review-command-substitution rule (catches $() and backtick) - Add review-global-install rule (catches npm/yarn/pnpm -g/--global) - Add tests: $() bypass, backtick bypass, npm install -g, --global, review-secrets-write multi-field (path, filename), version mismatch warning/rejection paths Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n guard, secrets rule rename - Restore rm and drop to dangerousWords (security regression from previous cleanup) - Fix allow-readonly-bash notMatches: change ;\s*\S to bare ; so a trailing semicolon with no following content cannot bypass the guard - Rename review-secrets-write → flag-secrets-access to reflect that it covers reads as well as writes; update reason string to match - Add .env.bak test: dotfile backup IS flagged, notmy.env.bak is NOT - Update all test fixtures to use ; instead of ;\s*\S to stay consistent with the corrected example config Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix block-rm-rf-home: use two-condition AND (conditionMode:'all') instead of a single complex regex that never matched rm -rf ~/foo in practice - Narrow review-write-etc to write-indicative ops (tee, cp, mv, >, install) to avoid approval fatigue on read-only commands like cat/grep /etc/* - Use Set for shield.dangerousWords in disable (O(1) vs O(n) lookup) - Add shields.test.ts: 31 tests covering alias resolution, readActiveShields validation/corruption, atomic writes, and regex correctness including known bypass patterns Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode Review: Node9 Proxy — Shields FeatureCorrectness & Edge Cases
Security IssuesNo validation of
The Test Coverage Gaps
The "disable word-protection" test in No test for TypeScript Type Safety
PerformanceNo concerns on the critical path — shields are loaded at startup, not per-request. MinorThe diff is truncated mid-rule (
Automated review by Claude Sonnet |
… file permissions, test correctness
- Fix `remaining` declared twice in disable handler (hoisted before the if block)
- Replace unsafe `as T | undefined` casts with Array.isArray() guards in enable/disable
- Add { mode: 0o600 } to writeActiveShields and writeRawConfig (consistent with credentials/config writes)
- Remove existsSync before mkdirSync in writeActiveShields (recursive:true is idempotent, existsSync added a TOCTOU window)
- Fix tautological word-protection test: use concrete sets to exercise both the "survives" and "removed" branches
- Add second word-protection test: words unique to disabled shield are fully removed
- Add undefined guard in regex test helpers (fixes TS2769 typecheck error)
- Add enable idempotency tests for smartRules and dangerousWords deduplication
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode Review: Node9 Proxy — Shield Templates FeatureCorrectness & Edge CasesTOCTOU in enable/disable flow:
Security IssuesUnvalidated
File mode 0o600 only on the temp file: Race Conditions / AsyncLast-writer-wins acknowledged but not mitigated: The comment admits there's no advisory lock. For a security tool where two concurrent TypeScript Type Safety
The diff truncates Test Coverage Gaps
Minor
Bottom line: The utility functions and tests are well-structured. The two critical issues to fix before merge are the TOCTOU between the two file writes and the unvalidated rule/word round-trip from disk — both are meaningful security gaps for a tool whose entire value proposition is being a trusted security layer.
Automated review by Claude Sonnet |
…n mutation Shields are now applied as a dedicated layer inside getConfig(): readActiveShields() reads ~/.node9/shields.json, maps each name to the in-memory SHIELDS catalog, and appends their smartRules/dangerousWords to the runtime policy. enable/disable now only write shields.json — config.json is never touched. This eliminates the two-file TOCTOU, the merge/unmerge complexity, and the read-modify-write race condition. Shield rules also update automatically when the catalog changes in a new binary release. Deleted: SHIELD_CONFIG_PATH, readRawConfig, writeRawConfig, all merge/unmerge logic. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode Review: Node9 Proxy — Shield Templates FeatureCorrectness & Edge Cases
Security Issues
Shield rules match on
Race Conditions / Async
TypeScript Type Safety
Test: Test Coverage Gaps
MinorREADME diff is fine — cosmetic roadmap update only. Summary: The main actionable issues are: (1) missing
Automated review by Claude Sonnet |
- github shield: remove block-force-push (exact duplicate of built-in block-force-push) Rename review-delete-branch to review-delete-branch-remote, narrow it to only cover `git push --delete` (the part not already caught by built-in review-git-destructive) - filesystem shield: remove mkfs from dangerousWords (already in built-in DANGEROUS_WORDS) Shields should only add coverage beyond what built-ins already provide. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode Review: Node9 Proxy — Shields FeatureCorrectness & Logic Issues
Non-null assertions after null guard in const name = resolveShieldName(service);
if (!name) { ... process.exit(1); }
const shield = getShield(name!)!; // redundant ! — already narrowedThe Security IssuesShield rules applied after user config "cannot be overridden" — but
Atomic write in Race Conditions / Async
TypeScript Type Safety
Test Coverage Gaps
Minor
Summary: The core logic is sound and the test suite is well-structured, but there are three actionable issues before merge: dangerousWords dedup in
Automated review by Claude Sonnet |
Replace the flat list of 6 config concepts with a clear two-layer model: - Layer 1 (always on): core smart rules protect git, SQL, shell for everyone - Layer 2 (shields): opt-in per-service protection via node9 shield enable Changes: - Add "How Protection Works" section with Layer 1/Layer 2 tables - Quick Start now shows shield enable as step 2 (primary onboarding action) - Rename "Configuration" to "Custom Rules (Advanced)" — signals power-user territory - Remove rules array from config examples (legacy) - Remove "built-in default smart rule" section (internal detail) - Mark Shield Templates as done [x] in roadmap - Simplify node9 explain output example Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode Review: Node9 Proxy — Shields FeatureREADME ChangesThese are documentation-only cleanup changes. The reorganization is clear and the new Layer 1/Layer 2 framing is a genuine usability improvement. No issues. One minor concern: removing the "Resolution Waterfall" section loses documentation about the 5-tier precedence model, which is security-relevant behavior. Users need to understand that cloud locks cannot be bypassed locally. Consider preserving this elsewhere rather than deleting it entirely.
|
…esGlob ops - Remove legacy `rules` (action/allowPaths/blockPaths) from Config, schema, and both evaluators - Add `matchesGlob` / `notMatchesGlob` condition operators using picomatch - Move block-rm-rf-home into built-in DEFAULT_CONFIG smartRules (evaluated first) - Add ADVISORY_SMART_RULES (allow-rm-safe-paths, review-rm) appended last in getConfig() so user-defined rules can override default rm behaviour - Pattern `(^|&&|\|\||;)\s*rm\b` covers chained commands without false-positives on `docker rm` - Update README operators table with matchesGlob / notMatchesGlob - Update all affected tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode ReviewREADME.md ChangesPurely documentation — looks good. The restructuring is cleaner and more user-friendly. The new
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode ReviewREADME.mdChanges look good — cleaner structure, better onboarding flow. The new The removal of the Resolution Waterfall section is a regression in documentation. Users need to understand the 5-tier precedence model to reason about whether their local rules can be overridden — this is security-critical information, not cosmetic.
|
- Add tests proving built-in block rules (block-rm-rf-home, block-force-push) cannot be bypassed by a user-defined allow rule - Restore Configuration Precedence section to README with 5-tier waterfall and note that built-in blocks always fire before user rules Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode ReviewREADME.mdChanges are documentation-only reorganization. Content is clearer and better structured. One minor concern: the new "Configuration Precedence" section says "first-match-wins: built-in defaults → global → project → shields → advisory defaults" — this order implies built-in defaults fire first, which should mean they can't be overridden by project config. But earlier you say "Higher tiers win" with env vars at tier 1. These two statements appear contradictory and could confuse users trying to understand whether project rules can override built-ins. Clarify which direction the precedence actually flows in the source. The removal of the undo example output and the Resolution Waterfall section loses genuinely useful documentation without explanation. At minimum the waterfall concept (now the "Configuration Precedence" table) survives, but the undo diff example was concrete and helpful.
|
DLP Engine (src/dlp.ts): - 7 built-in patterns: AWS key, GitHub token, Slack, OpenAI, Stripe, PEM, Bearer - Recursive scanner with depth limit (5) and string length cap (100 KB) - JSON-in-string detection for agents that stringify nested objects - maskSecret() — only redacted sample stored, full secret never leaves dlp.ts - severity: 'block' for known high-confidence patterns, 'review' for Bearer Core integration (src/core.ts): - DLP check runs before ignoredTools fast-path and audit mode - Hard block for 'block' patterns; 'review' falls through to race engine - DLP step 0 in explainPolicy waterfall - dlp config merged per-layer in getConfig() with enabled/scanIgnoredTools CLI (src/cli.ts): - DLP-specific negotiation message (rotate the key, use env vars, don't retry) - chalk.bgRed.white.bold alarm banner when blockedByLabel includes 'DLP' Cursor fix (src/setup.ts): - Remove hooks.json writing — Cursor does not support this format - Print clear warning that native hook mode is pending Cursor support - Only MCP proxy wrapping is configured Shields fix (src/shields.ts): - Treat empty shields.json as missing (suppress spurious parse warnings in tests) Tests: 353 passing (22 new DLP tests, fake secrets split via concatenation) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode ReviewREADME Changes (Documentation Only)Clean restructuring — clearer onboarding flow, better separation of Layer 1 vs Layer 2 concerns. Minor note: the new "Configuration Precedence" table says "first-match-wins: built-in defaults → global → project → shields" but elsewhere implies higher tiers win. That ordering is counterintuitive and worth a clarifying sentence to avoid user confusion. The removal of the
|
…rity advanced_policy.test.ts: - Add ruleName assertion to allow-rm-safe-paths test (reviewer: vacuous test) - Split compound test into two focused its (block vs allow scenarios) - Add name field to project smartRule fixture so ruleName is assertable core.ts: - Return ruleName on allow verdict (was only returned for block/review) core.test.ts: - Add matchesGlob tests: path matching, boundary patterns, notMatchesGlob - Add notMatches-no-flags tests: no throw + correct evaluation - Fix notMatchesGlob test: add explicit block rule so fallthrough is observable README.md: - Add clarifying note: settings override order (Tier 1 wins) and smart rules evaluation order (defaults first) run in opposite directions — plus note that project block fires before shield block by design Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode Review: Node9 Proxy PRREADME Changes (Low Risk)The documentation restructuring is clean and generally improves clarity. A few observations: Correctness concern — contradictory evaluation order explanation: Missing documentation:
Test Changes (
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewReviewREADME ChangesThese are documentation-only changes — reorganization, simplification, and new shield/roadmap content. The changes are clear and accurate. One minor concern: the new configuration precedence note states "built-in Test ChangesCritical: Tests assert implementation details that aren't tested The new tests assert Missing: Truncated diff The diff is cut off mid-test ( Test isolation concern Tests 1–3 ("No config needed — built-in advisory rule covers...") rely on built-in rules being active with no mocked config. But Coverage gap No test for the rule evaluation ordering boundary: a project Security gap in test semantics The regex in the new test — Summary
Automated review by Claude Sonnet |
… 1 invariant README.md: - Rewrite configuration precedence note as two distinct scannable paragraphs: settings (table/tier order) vs smart rules (concatenated list, first-match-wins) - Add code block showing the exact evaluation order for smart rules - Remove the dense single-paragraph explanation that reviewers found confusing advanced_policy.test.ts: - Add explicit comment that config-free tests rely on beforeEach existsSpy=false - Add ruleName assertions to "reviews rm" tests (not just decision) - Add Layer 1 bypass invariant test: user allow-all rule must not override built-in block-force-push — the most security-critical coverage gap identified Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode ReviewREADME ChangesDocumentation-only changes. The restructuring is clearer and the new "Shields" concept is well-explained. The configuration precedence table accurately reflects the rule concatenation model described. No issues.
|
🤖 Claude Code ReviewReviewREADME ChangesDocumentation-only changes. The new structure (Layer 1 / Layer 2 / Shields model, config precedence table,
|
## [1.0.14](v1.0.13...v1.0.14) (2026-03-21) ### Bug Fixes * merge latest dev updates into main ([#24](#24)) ([8ad4bfa](8ad4bfa)), closes [hi#confidence](https://github.com/hi/issues/confidence)
|
🎉 This PR is included in version 1.0.14 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Auto-generated PR
Merge latest
devchanges intomainto trigger a release.