Files
Jarvis/scripts/test-verify-entitlements.sh
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

83 lines
3.0 KiB
Bash
Executable File

#!/bin/bash
# test-verify-entitlements.sh — fault-injection self-test harness for verify-entitlements.sh.
#
# Feeds three fixture plists (good / broken / forbidden) to the --verify-fixture mode
# and asserts the exit codes match expectations. This is the mechanical defense against
# a silently-broken verify-entitlements.sh: if someone inverts a grep sense or drops
# a required key from the MAIN_REQUIRED list, one of these cases catches it.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FIXTURES_DIR="$SCRIPT_DIR/test-fixtures"
VERIFIER="$SCRIPT_DIR/verify-entitlements.sh"
PASS=0
FAIL=0
run_case() {
local name="$1"
local fixture="$2"
local expect_code="$3"
local actual=0
"$VERIFIER" --verify-fixture "$fixture" >/dev/null 2>&1 || actual=$?
if [ "$actual" -eq "$expect_code" ]; then
echo " PASS: $name (exit=$actual as expected)"
PASS=$((PASS + 1))
else
echo " FAIL: $name (expected exit=$expect_code, got exit=$actual)" >&2
FAIL=$((FAIL + 1))
fi
}
# Plan 05-03: harness for the per-helper rule branches. Drives
# `--verify-helper-fixture` so the fault-injection corpus exercises every arm
# of `check_helper_entitlements_xml` without needing a real signed bundle.
run_helper_case() {
local name="$1"
local fixture="$2"
local helper_name="$3"
local expect_code="$4"
local actual=0
"$VERIFIER" --verify-helper-fixture "$fixture" "$helper_name" >/dev/null 2>&1 || actual=$?
if [ "$actual" -eq "$expect_code" ]; then
echo " PASS: $name (exit=$actual as expected)"
PASS=$((PASS + 1))
else
echo " FAIL: $name (expected exit=$expect_code, got exit=$actual)" >&2
FAIL=$((FAIL + 1))
fi
}
echo "test-verify-entitlements.sh — fault-injection self-test"
echo
# Main-app fixtures (P1-05).
# Case 1: good fixture → exit 0
run_case "good-entitlements → exit 0" "$FIXTURES_DIR/good-entitlements.plist" 0
# Case 2: broken fixture (missing allow-jit) → exit non-zero
run_case "broken-entitlements → exit !0" "$FIXTURES_DIR/broken-entitlements.plist" 1
# Case 3: forbidden fixture (carries automation.apple-events) → exit non-zero
run_case "forbidden-entitlements → exit !0" "$FIXTURES_DIR/forbidden-entitlements.plist" 1
# Plan 05-03 helper fixtures.
# Case 4: applescript helper carrying apple-events as required → exit 0
run_helper_case "helper-applescript-good (mcp-applescript) → exit 0" \
"$FIXTURES_DIR/helper-applescript-good.entitlements" \
"mcp-applescript" 0
# Case 5: applescript helper MISSING apple-events → exit !0 (T-05-03-02)
run_helper_case "helper-applescript-missing (mcp-applescript) → exit !0" \
"$FIXTURES_DIR/helper-applescript-missing.entitlements" \
"mcp-applescript" 1
# Case 6: cross-helper drift — mcp-time carrying apple-events → exit !0 (T-05-03-01)
run_helper_case "helper-time-with-apple-events (mcp-time) → exit !0" \
"$FIXTURES_DIR/helper-time-with-apple-events.entitlements" \
"mcp-time" 1
echo
echo "PASS=$PASS FAIL=$FAIL"
if [ "$FAIL" -gt 0 ]; then exit 1; fi