Continuous, audit-ready security design review for cloud and agentic systems
Project description
Attestral
Continuous, audit-ready security design review for cloud and agentic systems.
Attestral reads your Terraform and MCP/agent configs, builds one system model, reviews it against a deterministic rule pack (with optional LLM reasoning and an LLM-as-judge), and emits a design review with a tamper-evident evidence chain you can hand to reviewers, auditors, and customers. It then compiles the reviewed design into a runtime policy and diffs live telemetry back against it.
pip install attestral
attestral scan ./my-project
The loop in one picture
flowchart LR
A["attestral scan<br/><b>attest</b>"] --> B["attestral verify<br/><b>prove</b>"]
A --> C["attestral compile<br/><b>enforce</b>"]
C --> D["attestral drift<br/><b>detect</b>"]
D -->|"design changed?<br/>re-attest"| A
style A fill:#96222E,color:#fff
style B fill:#1F6A4A,color:#fff
Attest the design, prove the record has not been altered, compile it into a default-deny runtime policy, and detect when what runs diverges from what was reviewed. The whole loop runs offline, on a laptop, free.
How a scan works (the pipeline)
flowchart TB
subgraph ING["1 · Ingest"]
TF["Terraform (.tf)"] --> M
MCP["MCP configs<br/>(mcp.json)"] --> M
M["SystemModel<br/>components · edges · trust boundaries"]
end
M --> L1
subgraph REV["2 · Review (layered, each finding tagged by origin)"]
L1["<b>L1 Deterministic rules</b><br/>26 typed matchers · fail-closed<br/>origin: deterministic"]
L2["<b>L2 ML classifiers</b> (planned v0.6)<br/>DeBERTa prompt-injection on agentic surfaces<br/>origin: ml"]
L3["<b>L3 LLM</b> (optional)<br/>elicitation + LLM-as-judge verifier<br/>origin: llm"]
L1 --> L2 --> L3
end
REV --> W["Waivers<br/>documented, expiring exceptions"]
W --> EV["3 · Evidence<br/>SHA-256 hash chain · verify offline"]
EV --> OUT["Output: Markdown · JSON · <b>SARIF</b> (Code Scanning)"]
style L1 fill:#0a7d3611,stroke:#0a7d36
style L3 fill:#96222E11,stroke:#96222E
| Layer | What it does | Reproducible? | Cost |
|---|---|---|---|
| L1 Deterministic | 26 typed matchers over the model, fail-closed (unknown matcher never matches) | Yes, fully | Free, offline |
| L2 ML (planned v0.6) | Local transformer (DeBERTa) for prompt-injection / capability classification on MCP surfaces | Pinned model + revision | Free, offline |
| L3 LLM (optional) | Elicits novel design threats, and a judge cross-examines findings to cut false positives | Verdicts recorded in the chain | Your API key |
Every finding carries its origin, so the deterministic core is never silently mixed with model reasoning. That separation is what makes the review audit-grade.
Install and run the whole loop (60 seconds)
pip install attestral
attestral scan examples/demo-project -o review # attest -> review.md + review.json
attestral verify review.json # prove -> chain VALID
attestral compile examples/demo-project -o policy.yaml # enforce -> default-deny policy
attestral drift policy.yaml examples/demo-project/runtime-events.jsonl --fail-on-drift # detect
The four commands
flowchart LR
subgraph scan["attestral scan"]
s1["Terraform + MCP"] --> s2["findings + evidence chain<br/>md / json / sarif"]
end
subgraph verify["attestral verify"]
v1["report.json"] --> v2["VALID / INVALID<br/>(offline)"]
end
subgraph compile["attestral compile"]
c1["attested model"] --> c2["default-deny policy<br/>bound to chain head"]
end
subgraph drift["attestral drift"]
d1["policy + telemetry"] --> d2["drift findings"]
end
# SCAN: review a project (Terraform + MCP configs discovered automatically)
attestral scan ./my-project --format both # md + json
attestral scan . --fail-on high # CI gate: exit 1 on high/critical
attestral scan . --format sarif -o attestral # SARIF -> GitHub Security tab + PR annotations
# VERIFY: prove a report has not been altered (no network, no server)
attestral verify review.json
# COMPILE: turn the attested design into a default-deny mcp-guard policy
attestral compile ./my-project -o policy.yaml
# DRIFT: diff runtime telemetry against the attested design
attestral drift policy.yaml events.jsonl --fail-on-drift
The sophistication layers (optional)
# LLM threat elicitation on top of the deterministic layer
export ANTHROPIC_API_KEY=...
attestral scan ./my-project --llm
# LLM-as-judge: cross-examine findings to cut false positives.
# Verdicts (confirmed / false_positive / needs_review) are recorded in the chain.
export ATTESTRAL_JUDGE_API_KEY=... # or reuse ANTHROPIC_API_KEY
attestral scan . --judge --judge-panel 3 # 3 judges vote per finding
attestral scan . --judge --judge-suppress # auto-waive confident false positives, on the record
The judge never deletes a finding. A confident false_positive becomes a machine-generated waiver carrying the judge's reasoning: suppressed from the gate, but kept on the record.
Baseline and waivers
Real repos start with findings. A waiver accepts a known risk and keeps the gate green without hiding anything: the waived finding stays in the evidence chain with its justification, and becomes a SARIF suppression (GitHub shows it dismissed, not open).
# attestral-waivers.yaml (auto-discovered at the scan root)
waivers:
- rule: ATL-005
component: aws_db_instance.app # or "*" for every component
reason: Encryption enforced at the storage layer; tracked in SEC-1234.
expires: 2026-12-31 # optional
Fail-safe: a waiver with no reason is ignored, and an expired waiver stops suppressing. A finding can only be silenced by a current, justified exception.
What it catches (26-rule pack)
| Area | Examples |
|---|---|
| Cloud misconfig (AWS, CIS-grounded) | public S3/RDS/Redshift, 0.0.0.0/0 security groups, wildcard IAM, unencrypted RDS/EBS/Neptune, disabled backups, KMS rotation off, public EC2 IPs |
| Agentic / MCP (OWASP LLM Top 10, MCP research) | shell-capable servers, broad filesystem roots, non-TLS transport, secrets in env, auto-installed packages (supply chain), mutable @latest tags (rug-pull), outbound-fetch/browser tools |
| Cross-cutting | agent runtime and cloud sharing no declared boundary controls |
Every finding maps to NIST 800-53, ASVS, SOC 2, OWASP LLM/Agentic, and MITRE ATLAS references.
Real-world benchmark
Run on TerraGoat (Bridgecrew's deliberately-vulnerable Terraform), same repo, two rule packs:
| Findings on TerraGoat AWS | |
|---|---|
| v0.4.0 (10 rules) | 3 |
| v0.5.0 (26 rules) | 6 |
The pipeline (ingest, evidence chain, tamper detection, gate, SARIF) is verified on real code; the rule pack keeps growing to raise coverage.
Use it in CI
# .github/workflows/attestral.yml
name: attestral
on: [pull_request]
permissions:
contents: read
security-events: write # to upload to the Security tab
jobs:
design-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with: { python-version: "3.12" }
- run: pip install "attestral[terraform]"
- run: attestral scan . --format sarif -o attestral
- uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: attestral.sarif }
- run: attestral scan . --fail-on high # hard gate (auto-uses attestral-waivers.yaml)
Ready-made workflows live in examples/github-actions/.
Writing custom rules
Rules are YAML with structured matchers. No eval anywhere, and an unknown matcher fails closed (never matches).
rules:
- id: ORG-001
title: Internal load balancer missing auth attribute
severity: high
target: aws_lb # component type prefix, or "model"
match: { attr_missing: auth }
description: ...
recommendation: ...
frameworks: ["NIST AC-3", "SOC2 CC6.1"]
python -c "from attestral.rules import RuleEngine; RuleEngine(['org_rules.yaml'])"
Development
pip install -e ".[dev,terraform,llm]"
pytest -q # offline suite; the live judge test skips without a key
ruff check attestral tests
To run the live judge test, set ATTESTRAL_JUDGE_API_KEY (or ANTHROPIC_API_KEY) and re-run pytest -q.
License
Apache 2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file attestral-0.5.0.tar.gz.
File metadata
- Download URL: attestral-0.5.0.tar.gz
- Upload date:
- Size: 44.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
df8499876257de21dbb0463fedca7abc1cd71c8779e9243b66b9aeca6e81cbec
|
|
| MD5 |
09292a83b325dd8aaa1f75ddbb1f666b
|
|
| BLAKE2b-256 |
53229a4faef52a9fbf5f1ba4e5b0612d9bab3ddc65d2af390c1e632b8d9dfe4c
|
Provenance
The following attestation bundles were made for attestral-0.5.0.tar.gz:
Publisher:
publish.yml on attestral-labs/attestral
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
attestral-0.5.0.tar.gz -
Subject digest:
df8499876257de21dbb0463fedca7abc1cd71c8779e9243b66b9aeca6e81cbec - Sigstore transparency entry: 2147892830
- Sigstore integration time:
-
Permalink:
attestral-labs/attestral@1dd6e583e4231c9680122a5b898f5d0c11afe915 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/attestral-labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1dd6e583e4231c9680122a5b898f5d0c11afe915 -
Trigger Event:
release
-
Statement type:
File details
Details for the file attestral-0.5.0-py3-none-any.whl.
File metadata
- Download URL: attestral-0.5.0-py3-none-any.whl
- Upload date:
- Size: 30.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b49d7f997a513397d65f0e03a09501f3fb496d73ebe82ee73f258569d033a9aa
|
|
| MD5 |
3106e11baa6cf33366b878afd6016e37
|
|
| BLAKE2b-256 |
f92bebfee528f075492a8a7f7c77130452e4e42e2457b5a669c234a50bf15fe0
|
Provenance
The following attestation bundles were made for attestral-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on attestral-labs/attestral
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
attestral-0.5.0-py3-none-any.whl -
Subject digest:
b49d7f997a513397d65f0e03a09501f3fb496d73ebe82ee73f258569d033a9aa - Sigstore transparency entry: 2147892917
- Sigstore integration time:
-
Permalink:
attestral-labs/attestral@1dd6e583e4231c9680122a5b898f5d0c11afe915 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/attestral-labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1dd6e583e4231c9680122a5b898f5d0c11afe915 -
Trigger Event:
release
-
Statement type: