If you discover a security vulnerability, please report it responsibly:
- Do NOT open a public issue
- Use GitHub Security Advisories to report privately
- Or email: heznpc (via GitHub profile)
- Source map disabled —
sourcemap: falseexplicitly set in esbuild to prevent accidental source exposure via npm - Package verification —
npm pack --dry-runpre-publish gate rejects.map,.ts,.env, and.tokenfiles - npm audit — Checks for known vulnerabilities in dependencies
- npm signature verification —
npm audit signaturesvalidates package provenance on every CI run - gitleaks — Scans for accidentally committed secrets on every push
- License compliance — Blocks copyleft licenses (GPL/AGPL)
- OIDC publishing — No npm tokens stored as secrets
AirMCP treats advisories reported by npm audit according to the table below. The policy is defined in RFC 0003 — npm audit upgrade plan (docs/rfc/0003-npm-audit-policy.md); the rollout is staged so CI behaviour changes only after a holding period at each severity level.
| Severity | CI behaviour today | Triage SLA | Fix SLA |
|---|---|---|---|
| critical | hard block (CI fails) | 1 business day | 3 business days |
| high | hard block (CI fails) | 3 business days | 7 business days |
| moderate | advisory only (summarised in CI logs) | 5 business days | next minor release |
| low / info | no CI action | best effort | best effort |
The moderate+ advisory is emitted by scripts/summarize-audit.mjs, which runs as a non-fatal step in .github/workflows/ci.yml immediately after the hard npm audit --audit-level=high gate. Once we've held moderate findings at zero for one release, RFC 0003 Phase 2 swaps the hard gate down to moderate and retires the advisory step.
- Zod validation — All 268 string input parameters have
.max()length limits to prevent oversized-input DoS - JXA injection prevention —
esc(),escAS(),escShell(),escJxaShell()sanitize all user input before script interpolation - Swift bridge prototype pollution guard — JSON responses from the Swift helper are parsed with a reviver that rejects
__proto__/constructor/prototypekeys at any depth (seesrc/shared/swift.ts) - PII scrubbing — Email addresses and file paths redacted from error messages
- Audit logging — Sensitive keys auto-redacted, log files restricted to owner-read-write (0o600)
- stdio transport — No network exposure, local-only communication
- HTTP security — Bearer token auth (timing-safe, SHA-256 hashed), rate limiting (120 req/min), origin validation, session timeout
- Shared note guard — Destructive operations blocked on shared notes by default
- HITL gating — Configurable human-in-the-loop approval for destructive operations
AirMCP's inbound attack surface (HTTP transport, stdio JSON-RPC) is defended by the items above. For traffic going the other direction — AppleScript do shell script with curl, JXA using ObjC.import('Foundation') for URL requests, or a Swift bridge command hitting the network — the following boundary applies:
- No centralised outbound policy. Each module is responsible for the network calls it makes. AirMCP does not interpose a proxy, TLS pin, or per-host rate limit on outbound traffic.
- Current usage is minimal. The only built-in outbound paths are the
googlemodule (OAuth 2.0 + Calendar/Drive/Gmail APIs over TLS to Google-owned hosts) and theweathermodule (Apple's WeatherKit / first-party system APIs). Neither forwards user-controlled URLs. - If you add a new outbound call: validate the destination against an allowlist or require an explicit opt-in config flag. Do not fetch URLs pulled from note bodies, calendar descriptions, reminder notes, or any other user-editable field without sanitisation.
- Third-party modules / user skills that make network calls are outside this threat model; users should review
~/.airmcp/skills/*before enabling them.
Report any outbound-path concerns via the reporting channel at the top of this document.