Skip to content

Commit dc79eac

Browse files
v1.2.3: visibility fixes, transport attestation, markdown lint enforcement
Visibility & Documentation: - Fix all 78 markdown lint violations across 20 files; re-enable all rules - Add 'How It Works — Verification Pipeline' section with ASCII architecture - Create examples/README.md with real receipt, verification, and CI examples - Improve README tagline to infra-grade positioning (receipt → policy → VSA) - Add CI badge to badge row; update test count to 1600+ - Add .github/SECURITY.md for GitHub community profile detection - Add .github/ISSUE_TEMPLATE/config.yml with security redirect Transport-level Agent Attestation: - MCP server stamps generator='aiir.mcp' + confidence='transport' on receipts - CI auto-detection reads GITHUB_ACTOR/GITLAB_USER_LOGIN for environment confidence - Review receipts now accept and propagate agent_attestation - 3-tier confidence model: declared → transport → environment Schema & Tests: - Add commit_receipt.v2.schema.json with tree_sha, parent_shas, agent_attestation - Add test_transport_attestation.py (411 lines) - Fix SHA validation test mocks for tree/parent SHA fields - Bump version to 1.2.3 across all public surfaces
1 parent bf01247 commit dc79eac

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1346
-111
lines changed

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
blank_issues_enabled: true
3+
contact_links:
4+
- name: Security Vulnerability
5+
url: https://github.com/invariant-systems-ai/aiir/security/policy
6+
about: Please report security vulnerabilities via our security policy — do NOT open a public issue.
7+
- name: Discussions
8+
url: https://github.com/invariant-systems-ai/aiir/discussions
9+
about: Ask questions, share ideas, or discuss AIIR usage.

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!-- markdownlint-disable MD041 -->
12
## Description
23

34
<!-- What does this PR do? Why? -->

.github/SECURITY.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Security Policy
2+
3+
## Supported Versions
4+
5+
| Version | Supported |
6+
|---------|-----------|
7+
| 1.2.x | ✅ Active (current) |
8+
| 1.1.x | ✅ Security fixes |
9+
| 1.0.x | ✅ Security fixes |
10+
| < 1.0.0 | ❌ Unsupported — upgrade to 1.2.x |
11+
12+
## Reporting a Vulnerability
13+
14+
**This is a security-critical tool.** We take every report seriously.
15+
16+
**Do NOT open a public GitHub issue for security vulnerabilities.**
17+
18+
Instead, report vulnerabilities via:
19+
20+
- **Email**: [noah@invariantsystems.io](mailto:noah@invariantsystems.io)
21+
- **Subject prefix**: `[VULN] aiir: <brief description>`
22+
23+
### What to include
24+
25+
1. Description of the vulnerability
26+
2. Steps to reproduce
27+
3. Affected versions
28+
4. Severity assessment (if known)
29+
5. Suggested fix (if any)
30+
31+
### Response timeline
32+
33+
| Stage | Target |
34+
|-------|--------|
35+
| Acknowledgment | 24 hours |
36+
| Initial triage | 48 hours |
37+
| Fix for Critical/High | 7 days |
38+
| Fix for Medium/Low | 30 days |
39+
| Public disclosure | After fix is released |
40+
41+
### Scope
42+
43+
The following are in scope:
44+
45+
- **aiir/cli.py** — public API shell and CLI entry point
46+
- **aiir/_core.py** — constants, encoding helpers, git operations, hashing
47+
- **aiir/_detect.py** — AI signal detection and commit metadata parsing
48+
- **aiir/_receipt.py** — receipt building, generation, formatting, writing
49+
- **aiir/_verify.py** — receipt content-addressed integrity verification
50+
- **aiir/_sign.py** — Sigstore signing and verification
51+
- **aiir/_ledger.py** — append-only JSONL ledger with auto-index
52+
- **aiir/_stats.py** — badge, stats dashboard, policy checks
53+
- **aiir/_github.py** — GitHub Actions integration
54+
- **aiir/mcp_server.py** — MCP server for AI assistants (path-restricted, error-sanitized)
55+
- **action.yml** — GitHub Actions composite action
56+
- **Receipt integrity** — content-addressed hashing chain
57+
- **Output injection** — GitHub Actions output/summary manipulation
58+
- **Supply chain** — dependency pinning and integrity
59+
60+
### Out of scope
61+
62+
- AI detection bypass (this is a known limitation of heuristic detection — see README)
63+
- Issues in upstream dependencies (`actions/setup-python`, `actions/upload-artifact`, etc.)
64+
- Denial of service via extremely large repositories (mitigated by `--max-count` limit)
65+
66+
## Security Design
67+
68+
### Threat model
69+
70+
This tool processes untrusted input from:
71+
1. **Git commit metadata** — author names, emails, subjects, message bodies
72+
2. **GitHub Actions inputs**`commit-range`, `ai-only`, `output-dir`
73+
3. **Diff content** — full diffs are hashed but not stored in receipts
74+
75+
### Security properties
76+
77+
- **Content-addressed receipts**: The `receipt_id` and `content_hash` are derived from SHA-256 of the canonical JSON receipt core. Modifying any field invalidates both.
78+
- **Sigstore keyless signing** (optional): Receipts can be cryptographically signed using Sigstore, providing non-repudiation and a public transparency log entry (Rekor). Uses OIDC keyless signing — no key management required. In GitHub Actions, ambient credentials are used automatically when `id-token: write` is set.
79+
- **NUL-byte delimited parsing**: Git metadata fields are parsed using `%x00` delimiters to prevent field injection via pipes or other characters in author names.
80+
- **Ref validation**: All user-provided git refs are validated to reject option-like strings (e.g., `--all`), preventing git argument injection.
81+
- **Heredoc output pattern**: GitHub Actions outputs use the heredoc delimiter pattern to prevent output injection via multiline values.
82+
- **Pinned dependencies**: All action dependencies are pinned to full commit SHAs, not mutable version tags.
83+
- **Markdown sanitization**: Commit subjects in step summaries are sanitized to prevent image beacon, phishing link, and HTML injection.
84+
- **Zero dependencies**: Only Python standard library. No supply chain attack surface from pip packages.
85+
- **PEP 740 digital attestations**: Every wheel and sdist published to PyPI includes in-toto-style digital attestations (SLSA provenance + PyPI Publish predicates), cryptographically signed via short-lived OIDC identities from Trusted Publishing. No static keys to compromise.
86+
- **SLSA provenance**: GitHub Actions `attest-build-provenance` generates SLSA provenance attestations for both wheel and sdist, binding each artifact to the specific build invocation.
87+
- **PyPI Integrity API verification**: Post-publish CI verifies that attestations are retrievable via PyPI's Integrity API (`GET /integrity/aiir/<version>/<file>/provenance`). A standalone verification script (`scripts/verify-pypi-provenance.py`) is provided for consumers.
88+
- **Trusted Publishing (OIDC)**: PyPI uploads use GitHub's OIDC identity provider — short-lived, scoped tokens instead of long-lived API tokens. No `PYPI_TOKEN` secret to rotate or leak.
89+
90+
### Verifying AIIR release provenance
91+
92+
Consumers can verify that any AIIR release was built by the official CI pipeline:
93+
94+
```bash
95+
# Verify the latest release attestations (zero dependencies)
96+
python scripts/verify-pypi-provenance.py
97+
98+
# Verify a specific version
99+
python scripts/verify-pypi-provenance.py 1.2.2
100+
101+
# Strict mode — fail if any artifact lacks attestations
102+
python scripts/verify-pypi-provenance.py --strict
103+
```
104+
105+
Alternatively, query the PyPI Integrity API directly:
106+
107+
```bash
108+
# Fetch attestations for a specific file
109+
curl -s https://pypi.org/integrity/aiir/1.2.2/aiir-1.2.2-py3-none-any.whl/provenance | python3 -m json.tool
110+
```
111+
112+
## Secret rotation
113+
114+
The AIIR project minimises long-lived secrets through OIDC Trusted Publishing
115+
(PyPI), Sigstore keyless signing, and the automatic `GITHUB_TOKEN`. Three
116+
repository-level secrets remain:
117+
118+
| Secret | Type | Scope | Rotation cadence |
119+
|--------|------|-------|------------------|
120+
| `GITLAB_TOKEN` | GitLab PAT | `write_repository` on the GitLab mirror | 90 days |
121+
| `NPM_TOKEN` | npm granular token | `publish` on `@invariantsystems/aiir` | 90 days |
122+
| `WEBSITE_DISPATCH_TOKEN` | GitHub fine-grained PAT | `contents:read` on `invariantsystems.io` | 90 days |
123+
124+
**Rotation procedure:**
125+
1. Generate a new token with the scopes listed above.
126+
2. Update the secret in **Settings → Secrets and variables → Actions**.
127+
3. Trigger a test workflow run to verify the new token works.
128+
4. Revoke the old token immediately after confirming the new one.
129+
130+
**Design intent:** If any secret expires or is revoked, the failure mode is
131+
graceful — GitLab sync, npm publish, and website dispatch all have fallback
132+
paths (CI mirror cron, manual publish, 6-hour self-heal schedule).
133+
134+
## Acknowledgments
135+
136+
We gratefully acknowledge security researchers who report vulnerabilities responsibly.

.markdownlint.yaml

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
# https://github.com/DavidAnson/markdownlint
33
#
44
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
# All rules enforced. File-level and line-level exceptions use
7+
# inline markdownlint-disable comments where structurally necessary
8+
# (frontmatter, PR templates, UI navigation paths, formula syntax).
59

610
# Allow long lines — markdown prose is not code.
711
MD013: false
@@ -23,45 +27,3 @@ MD003:
2327

2428
# Allow inline HTML for shields.io badges
2529
no-inline-html: false
26-
27-
# ── Relaxations for docs/contrib content ─────────────────────────────
28-
# These rules fire frequently on intentional patterns in our docs.
29-
30-
# Fenced code blocks without language (sometimes intentional for plain text)
31-
MD040: false
32-
33-
# Blanks around lists (frequently violated in contrib posts and docs)
34-
MD032: false
35-
36-
# Bare URLs (common in contrib launch posts, Show HN drafts, etc.)
37-
MD034: false
38-
39-
# Blanks around fences (contrib posts use compact formatting)
40-
MD031: false
41-
42-
# Emphasis as heading (docs use bold for UI navigation paths)
43-
MD036: false
44-
45-
# First line must be a heading (PR template starts with ## Description)
46-
MD041: false
47-
48-
# Blank lines inside blockquotes
49-
MD028: false
50-
51-
# Emphasis style consistency (contrib uses both * and _)
52-
MD049: false
53-
54-
# Heading spacing
55-
MD022: false
56-
57-
# No multiple blank lines (can happen in formatted docs)
58-
MD012: false
59-
60-
# Single top-level heading (contrib posts have frontmatter + heading)
61-
MD025: false
62-
63-
# Ordered list prefix
64-
MD029: false
65-
66-
# Link fragments
67-
MD056: false

ARCHITECTURE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
## Current Architecture (v1.0.x)
66

7-
```
7+
```text
88
┌─────────────────────────────────────────────────────────────┐
99
│ CLI / GitHub Action / MCP Server │
1010
│ (aiir/cli.py, action.yml, mcp_server.py) │

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,35 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
66

7+
## [1.2.3] — 2026-03-11
8+
9+
### Added
10+
11+
- **Markdown lint enforcement**: Fixed all 78 markdown lint violations across 20 files and re-enabled all suppressed rules in `.markdownlint.yaml`. CI now enforces the full rule set.
12+
- **Verification pipeline documentation**: New "How It Works — The Verification Pipeline" section in README with ASCII architecture diagram showing `git commit → receipt → Sigstore → policy → VSA → CI gate`.
13+
- **Example gallery** (`examples/README.md`): Real receipt JSON, verification output, CI check mockup, policy evaluation example, and badge usage guide.
14+
- **GitHub community profile**: Added `.github/SECURITY.md` for detection and `.github/ISSUE_TEMPLATE/config.yml` with security policy redirect and discussions link.
15+
- **CI badge**: Added CI status badge to README badge row.
16+
17+
### Changed
18+
19+
- **README messaging**: Tagline updated from consumer-style to infra-grade positioning. Test count updated to 1600+. Audience-specific bullets for developers, security teams, and auditors.
20+
721
## [1.2.2] — 2026-03-10
822

923
### Added
1024

25+
- **DAG binding hardening** (`commit_receipt.v2`): Receipt core now includes `tree_sha` (directory state) and `parent_shas` (graph position) in the hashed `commit` object. Closes the theoretical "receipt laundering" vector identified in red-team review — a receipt is now cryptographically bound to the commit's exact position in the DAG, not just its content hash. Backwards-compatible: v1 receipts still verify. JSON Schema: `schemas/commit_receipt.v2.schema.json`. New controls: R24-DAG-01, R24-DAG-02.
1126
- **P0: GitHub Check Run** (`create_check_run()`): AIIR verification now posts a visible `aiir/verify` check run on every PR when running as a GitHub Action. Enforceable via branch protection rules. Requires `checks: write` permission.
1227
- **P1: Review Receipts** (`build_review_receipt()`, `--review`): New receipt type (`aiir/review_receipt.v1`) for human review attestations. Content-addressed, schema-validated, appended to the same ledger. Supports `--review-outcome` (approved/rejected/commented) and `--review-comment`. JSON Schema: `schemas/review_receipt.v1.schema.json`.
1328
- **P2: Project Init** (`--init`): Scaffold a `.aiir/` directory with `receipts.jsonl`, `index.json`, `config.json`, `.gitignore`, and optional `policy.json`. Path-traversal guarded. Idempotent (safe to re-run).
1429
- **P3: PR Comment** (`post_pr_comment()`): Automatic receipt summary comment on every PR in GitHub Actions mode. Idempotent (updates existing comment via hidden HTML marker). Requires `pull-requests: write` permission.
1530
- **P4: Commit Trailers** (`format_commit_trailer()`, `--trailer`): Print `AIIR-Receipt`, `AIIR-Type`, `AIIR-AI`, `AIIR-Verified` trailer lines suitable for `git interpret-trailers`. Terminal-escape sanitized, capped at 10 trailers.
31+
- **Transport-level agent attestation** (`extensions.agent_attestation`): 3-tier confidence model for AI authorship evidence:
32+
- `"declared"` — self-reported via `--agent-tool`/`--agent-model`/`--agent-context` CLI flags (existing).
33+
- `"transport"`**NEW**: MCP server auto-populates `confidence: "transport"` because the MCP protocol itself guarantees an AI client invoked the tool. Generator stamped as `aiir.mcp`.
34+
- `"environment"`**NEW**: CI auto-detection reads `GITHUB_ACTOR` / `GITLAB_USER_LOGIN` and auto-populates attestation when the actor matches known AI/bot patterns (e.g., `copilot`, `dependabot[bot]`, `gitlab-duo`). Explicit `--agent-*` flags always take precedence.
35+
- Review receipts (`build_review_receipt`) now accept and propagate `agent_attestation`.
1636
- **PEP 740 digital attestations**: Publish workflow now explicitly enables `attestations: true` on `pypa/gh-action-pypi-publish`. Every wheel and sdist uploaded to PyPI includes in-toto-style digital attestations (SLSA provenance + PyPI Publish predicates), cryptographically signed via short-lived OIDC identities from Trusted Publishing.
1737
- **SLSA provenance for sdist**: `actions/attest-build-provenance` now covers both `.whl` and `.tar.gz` artifacts (was wheel-only).
1838
- **PyPI Integrity API verification**: Post-publish CI step queries `GET /integrity/aiir/<version>/<file>/provenance` for every release artifact and reports attestation coverage.
@@ -28,6 +48,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
2848

2949
### Fixed
3050

51+
- **MCP server generator identity**: MCP server called `generate_receipt()`/`generate_receipts_for_range()` without `generator` or `agent_attestation` — receipts from MCP were indistinguishable from CLI receipts. Now stamps `generator: "aiir.mcp"` and `agent_attestation.confidence: "transport"` on all 3 call sites (`_handle_aiir_receipt` single + range, `_handle_aiir_gitlab_summary`).
52+
- **Review receipt attestation gap**: `build_review_receipt()` did not accept `agent_attestation`. Now propagates `--agent-*` flags and CI auto-detection to review receipts.
3153
- **PR comment markdown injection**: `_format_pr_comment()` now uses `_sanitize_md()` instead of `_strip_terminal_escapes()` — neutralises markdown metacharacters (`|`, `` ` ``, `<`) in user-controlled fields rendered in GitHub PR comment tables.
3254
- **`--init` path traversal**: Replaced `startswith()` with `relative_to()` to prevent prefix-collision attacks (`/repo` vs `/repo_evil`).
3355
- **`--review` ref resolution**: CLI now resolves symbolic refs (e.g., `HEAD`) to full hex SHAs via `git rev-parse` before building review receipts. Previously, `--review HEAD` produced a receipt with `reviewed_commit.sha = "HEAD"`, which violated the JSON Schema.

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ python -m pytest tests/ -q # 1030+ tests, ~2 min
3838
All contributions must include a `Signed-off-by` line certifying the
3939
[Developer Certificate of Origin v1.1](https://developercertificate.org/):
4040

41-
```
41+
```text
4242
Signed-off-by: Your Name <your@email.com>
4343
```
4444

0 commit comments

Comments
 (0)