fix: merge latest dev updates into main#18
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>
🤖 Claude Code ReviewCode Review: Context Sniper FeatureCorrectness & Edge Cases
EDIT intent detection is fragile: Detecting EDIT by presence of
Truncated diff: Security IssuesUnvalidated
Race Conditions / AsyncNo new async paths introduced. The metadata is computed synchronously before the race engine forks — correct approach. TypeScript Type Safety
The Performance
Test Coverage Gaps
SummaryThe core design is sound — computing metadata once and flowing it downstream is the right call. The main issues are: the incomplete
Automated review by Claude Sonnet |
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>
🤖 Claude Code ReviewCode Review: Context Sniper ModuleCorrectness & Edge Cases
Default tier fallback: Security Issues
JSON.parse without try/catch in daemon: The body parse at line Async / Race ConditionsNo new async paths introduced. The TypeScript Type Safety
Performance
Test Coverage Gaps
SummaryThe untrusted
Automated review by Claude Sonnet |
…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>
🤖 Claude Code ReviewReviewDeletions (config files)Removing Zod dependencyAdding Zod at
Integration tests (
|
🤖 Claude Code ReviewReviewCleanup (config file deletions)Removing Zod dependency additionAdding Zod for config validation is a reasonable choice. One concern: the lockfile shows Integration tests — correctness issuesFlaky timing in audit/cloud test (critical): await new Promise((res) => setTimeout(res, 200));
expect(auditCalls.length).toBeGreaterThan(0);A 200ms fixed delay is unreliable in CI. If the CLI process hasn't finished its HTTP POST within that window, the test produces a false negative. Use a polling loop or expose a callback. This is especially fragile if the audit POST is fire-and-forget (not awaited in the CLI path).
Security gaps in test coverageNo test for path traversal in No test for No negative test for
Type safetyThe diff doesn't show the Zod schema definitions, so it's impossible to verify whether all config fields are properly typed. If Missing testThe diff is truncated — the malformed JSON test is cut off mid-string. Ensure it actually covers the fallback-to-defaults behavior and doesn't silently pass due to an incomplete assertion. OverallThe structure is solid and the intent is right, but the flaky timing assertion in the cloud audit test is the most actionable issue to fix before merging.
Automated review by Claude Sonnet |
…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>
🤖 Claude Code ReviewCode Review: Node9 Proxy Integration Tests + Config CleanupWhat's in this PR
Issues Found1. Zod added but never used in this diff (medium) 2. Flaky timing in audit cloud test (high) await new Promise((res) => setTimeout(res, 200));
expect(auditCalls.length).toBeGreaterThan(0);A 200ms arbitrary sleep is a classic flakiness source, especially in CI environments under load. If 3. 4. Truncated test file (low/blocker for review) 5. No cleanup of 6. Scratch files with double-underscore suffix deleted (good) What Looks Good
SummaryBlock on: Zod dependency without consuming code, truncated diff. Fix before merge: the timing-based assertion in the audit cloud test.
Automated review by Claude Sonnet |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Claude Code ReviewCode ReviewWhat's in this diff
Issues Found1. Timing race in audit+cloud test (High) await new Promise((res) => setTimeout(res, 200));
expect(auditCalls.length).toBeGreaterThan(0);A fixed 200ms sleep is unreliable — CI machines under load will flake. If 2. Zod added as dependency but no schema usage is shown (Medium) 3. Backup config files contained sensitive-looking defaults (Low/Hygiene) 4. Test isolation: runCheck({ ... }, { HOME: tmpHome }, tmpHome)Setting 5. Diff is truncated — Section 7 is incomplete 6. No test for the What Looks Good
Bottom line: Don't merge until the truncated Section 7 is reviewed, the timing race is resolved, and Zod's placement on the hot path is justified.
Automated review by Claude Sonnet |
…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>
🤖 Claude Code ReviewCode Review: Node9 Proxy — Integration Tests + Zod + Config CleanupOverall AssessmentMostly solid work. Cleaning up the double-underscored config files is good housekeeping, and the integration test approach (real subprocess, isolated HOME) is the right architecture for a security tool. Several issues need attention. Security Issues1. 2. Zod version pinning is loose. Test Correctness Issues3. Truncated diff — last test is cut off. 4. 5. Mock server error handling is incomplete. 6. Minor Issues7. 8. No test for malformed JSON payload to What Looks Good
Block on: truncated test, double-resolve in async runner, security note on
Automated review by Claude Sonnet |
…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>
🤖 Claude Code ReviewReviewWhat's in this PR
Issues FoundSecurity — The test comment explicitly documents that Test correctness — ambiguous assertion in "safe command" test if (r.stdout.trim()) {
const parsed = JSON.parse(r.stdout.trim());
expect(parsed.decision).not.toBe('block');
}This is a conditional assertion — if Test robustness —
Dependency —
Truncated diff The diff cuts off mid-test inside Missing test coverage (visible from what's present)
Minor Notes
Overall structure is solid. Address the conditional assertion and review the truncated server-teardown section before merging.
Automated review by Claude Sonnet |
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>
🤖 Claude Code ReviewCode Review: Node9 Proxy Integration Tests + Zod DependencyWhat's in this PR
Issues Found1. Zod added but never used in this diff (Medium) 2. 3. Double-resolve guard is unnecessary and misleading (Low) 4. 5. Security concern: 6. Config file cleanup (Good) 7. Test coverage gap: daemon interaction SummaryThe test suite is well-structured and the helper design is solid. The main concerns are: the incomplete diff hiding where/how
Automated review by Claude Sonnet |
## [1.0.9](v1.0.8...v1.0.9) (2026-03-16) ### Bug Fixes * merge latest dev updates into main ([#18](#18)) ([76b9747](76b9747))
|
🎉 This PR is included in version 1.0.9 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Auto-generated PR
Merge latest
devchanges intomainto trigger a release.