Commit Graph
5 Commits
Author SHA1 Message Date
james.maesandClaude Opus 4.7 102cb562f2 fix(05-review): WR-01 strip block comments and strings before grep
The pre-WR-01 lint stripped only LEADING `//` comment lines, so a
string literal like `let docs = "Use NSApp.run() to start"` or a
block comment `/* runModal is forbidden */` would false-positive
because the regex matched bare identifiers inside content the parser
wouldn't even reach.

Tightens both:
1. PATTERN now requires a call-site shape (`.method(` or `Type.method(`)
   so bare identifiers don't trigger.
2. Pre-strip pass via sed: strip line comments, block comments
   (whole-line delete), and string literals before grep.

Adds string-literal-modal-mentions.swift fixture covering:
- runModal inside a string literal
- runModal inside a block comment
- runModal in a trailing line comment
None of these should trigger; the lint now correctly accepts the
fixture (exit 0).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 16:23:59 -05:00
james.maes f0b8924b9d feat(05-05): modal-presentation lint + 2 fixtures + preBuildScript wiring
Closes the build-time half of MCP-04: scripts/check-no-modal-presentation.sh
fails the Jarvis build on any runModal / beginModalSession / NSApp.run /
NSApplication.shared.run call outside the two-file allowlist
(packages/Shell/Sources/Shell/TCCAlertService.swift +
packages/MCP/Sources/MCP/ConfirmationPresenter.swift). Wired as a
preBuildScript on the Jarvis target alongside the other build-breaking
lints (bus-protocol-version, no-evaluateJavaScript, single-writer-HUD).

Self-tests:
- whole-repo scan: exit 0 (only the two allowlisted call sites match)
- scripts/test-fixtures/allowed-non-modal-call.swift: exit 0
- scripts/test-fixtures/forbidden-modal-call.swift: exit 1 with the
  'forbidden modal-presentation call' error on stderr

Plan deviation: the plan's allowlist named 'App/TCC/TCCAlertService.swift'
but TCCAlertService actually lives at packages/Shell/Sources/Shell/. The
allowlist follows the file's real path.
2026-04-25 15:00:32 -05:00
james.maes 45328e3871 feat(05-03): bidirectional per-helper entitlement enforcement + 3 fixtures
- verify-entitlements.sh: refactor inline mcp-applescript helper grep into
  check_helper_entitlements_xml() driven off per-helper required+forbidden
  arrays. mcp-applescript MUST carry apple-events; mcp-time/mcp-clipboard
  MUST NOT (and forbidden list also rejects allow-jit / speech-recognition-
  assets / audio-input / allow-unsigned-executable-memory drift onto helpers)
- Unknown-helper case is a fatal error — 'no default-allow branch' so authoring
  drift cannot smuggle in an unconfigured helper
- Empty array guard via ${#array[@]} prevents bash 'unbound variable' under
  set -u when a helper has no required keys (Rule 1 fix discovered via Debug
  xcodebuild)
- New --verify-helper-fixture <plist> <helper-name> mode for fault-injection
  harness — feeds a fixture file in and runs check_helper_entitlements_xml
  without needing a real signed bundle
- 3 new fixtures cover the three threat-model arms:
    helper-applescript-good           T-pos:        mcp-applescript with apple-events
    helper-applescript-missing        T-05-03-02:   mcp-applescript MISSING apple-events
    helper-time-with-apple-events     T-05-03-01:   cross-helper drift (forbidden)
- test-verify-entitlements.sh: add run_helper_case() driver + 3 invocations,
  total fixture corpus now 6 cases (3 P1-05 + 3 P5-03)
- AppleScriptRunner.swift / MCPAppleScriptMain.swift: reword TCC-isolation
  comments so the literal subprocess CLI binary name does not appear in source
  (plan's anti-pattern grep '! grep -q osascript' enforces absence)
- Debug xcodebuild end-to-end: post-codesign walker reports
    'helper mcp-time / mcp-clipboard / mcp-applescript entitlements OK' x3
- Fault-injection harness: PASS=6 FAIL=0
2026-04-25 13:34:29 -05:00
james.maes a63d521d38 feat(02-04): add bus parity + HUD-04 lint + harness-parity pre-build scripts
Three build-breaking check scripts + two smoke test harnesses + two fixture dirs:

- scripts/check-bus-protocol-version.sh: greps Swift vs TS BUS_PROTOCOL_VERSION
  constants, diffs the two fixture directories (packages/Bus/Tests/BusTests/
  Fixtures/ vs webview/packages/bus/fixtures/). Fails build on any drift.
- scripts/check-no-evaluate-javascript.sh: scans App/ + packages/ for
  `.evaluateJavaScript(` outside test dirs; HUD-04 architectural lint.
- scripts/check-bus-harness-parity.sh: byte-identical check between
  canonical webview/bus-harness.html and bundle copy (Plan 02-03 provides
  the bundle copy; this script enforces no-drift going forward).
- scripts/test-check-bus-protocol-version.sh + bus-mismatch/ fixture:
  drives the protocol-version checker against a v2.0.0/v1.9.0 mismatch,
  asserts exit 1 + both versions in error text.
- scripts/test-check-no-evaluate-javascript.sh + bus-evaluate-js/ fixture:
  drives HUD-04 lint against bad.swift (evaluateJavaScript) + good.swift
  (callAsyncJavaScript). Asserts bad.swift flagged, good.swift not flagged.
  Fixture is copied to a temp dir to bypass the production */test-fixtures/*
  path-skip rule.

[Rule 3 - Blocking] Normalized Swift-side fixtures to drop trailing newlines.
The 14 JSON files under packages/Bus/Tests/BusTests/Fixtures/ had stray
trailing \n while the TS mirror at webview/packages/bus/fixtures/ did not.
Plan 02-02's SUMMARY documented the no-trailing-newline convention (matches
Swift JSONEncoder default) but Plan 02-01's fixtures were authored via a
text editor that added them. Stripping \n lets check-bus-protocol-version.sh
pass without relaxing the byte-equality invariant. Swift CodableRoundTripTests
remain green (24/24) because JSONSerialization ignores trailing whitespace.
2026-04-23 17:51:56 -05:00
james.maes 52f2bdee0d feat(01-05): add build-time codesign + entitlement verification scripts
- scripts/codesign.sh: deepest-first walker (find -depth) signing nested
  Contents/Helpers/**/*.app with per-helper .entitlements, then main app
  last. Never uses --deep (TN2206/Pitfall #3 guard).
- scripts/verify-entitlements.sh: two-mode script per RESEARCH Open Q #8.
  --pre-codesign validates source + writes JarvisEntitlementsVerified=YES
  to unsigned Info.plist so the flag becomes part of the signed content.
  --post-codesign greps signed entitlements (via codesign -d --entitlements -)
  for MAIN_REQUIRED/MAIN_FORBIDDEN + per-helper rules; never writes. Strips
  XML comments before grep (self-invalidating-grep defense).
- scripts/verify-codesign-settings.sh: pbxproj linter for CodeSignOnCopy = YES,
  --deep, and missing --options=runtime/--timestamp.
- scripts/test-verify-entitlements.sh: fault-injection self-test harness
  that feeds good/broken/forbidden fixtures to --verify-fixture mode and
  asserts exit codes.
- scripts/test-fixtures/{good,broken,forbidden}-entitlements.plist: fixtures
  exercising the happy path, missing allow-jit, and forbidden apple-events.

Self-test: PASS=3 FAIL=0. Linter passes against current pbxproj.

MCP-06, SEC-02, SEC-03, SEC-08 scaffolded.
2026-04-22 14:21:33 -05:00