Security
How we build and verify trustworthy software.
Security philosophy
We build verifiable computing infrastructure. Our security posture reflects that: minimize attack surface, maximize auditability, prove correctness where possible.
Zero dependencies
AIIR has zero runtime dependencies. No supply chain to compromise. The entire codebase is open and auditable — roughly 9,200 lines of core logic.
Content-addressed
Every receipt’s integrity is bound to 6 core fields via SHA-256. Change any core field —
type, commit, ai_attestation, provenance,
schema, version — and the hash breaks. Non-core fields (timestamp,
extensions) are annotation-only.
Adversarial testing
We run structured adversarial rounds against every release: fuzzing, homoglyph injection, path traversal, TOCTOU attacks, and more.
Transparency logs
Sigstore integration provides keyless signing, Fulcio certificates, and Rekor transparency log entries. Non-repudiation without key management.
Threat modeling
AIIR ships with a public THREAT_MODEL.md that covers the full STRIDE/DREAD analysis:
| STRIDE Category | Coverage |
|---|---|
| Spoofing | Content-addressed hashes + Sigstore identity binding |
| Tampering | SHA-256 integrity + Rekor transparency log |
| Repudiation | Keyless signing with Fulcio certificates |
| Information Disclosure | Receipts contain only commit metadata (no source code) |
| Denial of Service | Zero network calls in default mode |
| Elevation of Privilege | No filesystem writes outside specified output dir |
Each threat is scored using DREAD (Damage, Reproducibility, Exploitability, Affected Users, Discoverability) with concrete mitigations and residual risk documented.
Adversarial testing program
Every AIIR release goes through structured adversarial rounds. These are not unit tests — they're attacks. Each round attempts to break a specific security property:
- Receipt forgery — can an attacker produce a valid-looking receipt for a commit they didn't make?
- Homoglyph evasion — can Cyrillic/Greek lookalikes bypass declared-signal normalization?
- Path traversal — can
--outputescape the intended directory? - TOCTOU races — can git state change between read and receipt?
- JSON injection — can commit messages inject fields into receipt JSON?
- Large input handling — what happens with 10K-file commits or 1MB messages?
- Signature stripping — can an attacker remove Sigstore signatures undetected?
Results from each round feed back into the test suite as regression tests. The test suite includes these adversarial scenarios alongside functional and fuzz tests.
Technical details
Numbers you can verify. Every stat below maps to a public CI run or open-source file you can audit yourself.
Test infrastructure
| Layer | What | Count |
|---|---|---|
| Unit tests | Deterministic functional + edge-case coverage | 2214 tests |
| Fuzz tests | Hypothesis property-based testing — random & adversarial inputs | 52 fuzz functions · ~53,000 generated inputs per run |
| MCP protocol tests | JSON-RPC message parsing, method dispatch, rate limiting, oversized message rejection | 80 tests |
| Coverage gate | CI enforces minimum line coverage on every push | 100% threshold |
CI pipeline
The latest push to main fans out to 41 public check runs
across CI, Quality, Security,
CodeQL, ClusterFuzzLite, Sync, Scorecard, and receipt automation. Branch protection requires the
3 rollup gates ci-ok, quality-ok, and
security-ok before merge:
| Job | What it checks |
|---|---|
| test (×7) | Full matrix — Python 3.9–3.13 on Ubuntu + Windows + macOS |
| fuzz | Property-based fuzz tests — pure Python, Ubuntu only |
| lint | actionlint — validates all workflow YAML syntax, inputs, and expressions |
| coverage | Enforces 100% line coverage with --fail-under |
| package | Build wheel, install, smoke-test entry points, verify py.typed |
| CodeQL | GitHub code scanning — detects injection, XSS, path traversal, ReDoS |
| scorecard | OpenSSF Scorecard — supply chain risk auditing (currently 7.6/10) |
| mutation | Mutation testing via mutmut — kills mutants to verify test quality |
| ClusterFuzzLite | Continuous fuzzing — coverage-guided inputs via OSS-Fuzz infrastructure |
| CBOR round-trip | Python ↔ Rust parity — verifies CBOR sidecar determinism across languages |
| Rust SDK | Builds and tests aiir-cbor-verify crate with golden vector conformance |
| ci-ok | Summary gate — required by branch protection, passes only if all preceding jobs succeed |
Hardening controls
Defense-in-depth measures across the codebase. Each control maps to a specific threat in the threat model.
| Control | Threat | Implementation |
|---|---|---|
| Path traversal prevention | Directory escape via --output / --verify |
resolve() + relative_to(cwd) + TOCTOU re-check after mkdir |
| Symlink rejection | File read/write via symlink | Explicit is_symlink() checks on all input paths |
| Git ref sanitization | Command injection via branch names | Reject ../, shell metacharacters; -- arg separator |
| Credential stripping | PAT leakage in remote URLs | urlparse reconstruction removing credentials, query params, fragments |
| Homoglyph normalization | Declared-signal normalization bypass via Cyrillic/Greek lookalikes | NFKC + explicit confusable mapping table |
| OOM prevention | Large diff / message denial of service | Streaming SHA-256 (no full diff in memory), 10 MB message cap, 50 MB file cap |
| Rate limiting (MCP) | Subprocess flooding via rapid JSON-RPC calls | Sliding-window rate limiter (50 requests / 10s) |
| Error sanitization | Information disclosure via stack traces | All exceptions stripped to safe messages before JSON-RPC responses |
| Atomic file writes | Corruption from interrupted writes | O_CREAT | O_EXCL + os.replace() for config; O_EXCL for receipts
|
| Subprocess safety | Environment variable injection | GIT_SAFE_ENV with sanitized PATH, stripped GIT_* vars, 60s
timeout |
Supply chain
Zero runtime dependencies
AIIR's core has zero pip install dependencies. The entire attack surface is the Python
standard library.
Pinned CI actions
All GitHub Actions are pinned to full SHA (not tags). No @v4 — only @sha256:...
to prevent tag mutation attacks.
Trusted Publisher
PyPI releases use OIDC Trusted Publisher — no stored API tokens. The GitHub Actions workflow identity is the only credential.
Branch protection
Changes to main require the rollup gates ci-ok, quality-ok, and
security-ok to pass. Enforced by GitHub branch protection with admin enforcement enabled.
Responsible disclosure
| Channel | Details |
|---|---|
| noah@invariantsystems.io | |
| security.txt | /.well-known/security.txt (RFC 9116) |
| GitHub Security Advisories | Report via GitHub |
| Response time | Acknowledgment within 24 hours |
| Disclosure timeline | 90-day coordinated disclosure |
See SECURITY.md in the AIIR repository for the full policy.
What we don't do
- No telemetry. AIIR makes zero network calls in default mode. Plausible analytics on this website only (no cookies, GDPR compliant).
- No source code collection. Receipts contain commit metadata and diff hashes, never source code.
- No key management burden. Sigstore signing is keyless — uses OIDC identity from your CI provider.
- No vendor lock-in. Receipts are plain JSON. Export, migrate, or self-host anytime.
Remote environment linking
A future environment-linking surface would be a high-risk boundary. We will not open it from the website until the public threat model, authentication design, network-boundary evidence, rollout controls, audit logging, and shutdown path are documented and reviewed.
- No browser-to-local-machine connection is enabled today.
- No public control endpoint for a workstation or unpublished managed environment is enabled today.
- No public firewall or edge-exposure posture is being represented here as sufficient for a live machine-link path.
- The current public site only provides a request path for early access and planning.
Audit the code yourself
AIIR is Apache 2.0. The entire codebase is one pip install away.