Defensive static-analysis scanner for the AI-agent execution gap.
Project description
actenon-scan
Stop AI agents from taking consequential actions they were never authorised to take.
actenon-scan is a defensive static-analysis scanner that finds the execution gap: places where a consequential, irreversible action (payment, deletion, deployment, access grant) is reachable from an AI-agent tool boundary without a preceding authority or proof check on that code path.
Think of it as Bandit or Semgrep — but scoped to one problem: the Replit incident, where an agent took a destructive action it should not have.
Works on any Python repo. Does not require the repo to have adopted Actenon.
Quick start
pip install actenon-scan
actenon-scan scan .
Sample output:
actenon-scan: 3 finding(s) in 3 file(s) (scanned 47 file(s))
refund_tool.py
8:4 [HIGH] PAY-STRIPE-REFUND (payments)
stripe.Refund.create(payment_intent=payment_id, amount=amount)
confidence: high
Guard this payment call with actenon proof verification. See: https://github.com/Actenon/actenon-permit
delete_tool.py
14:4 [HIGH] DATA-DELETE-SQL (data_destruction)
'DELETE FROM {table_name}'
confidence: high
Guard this destructive call with actenon proof verification. See: https://github.com/Actenon/actenon-permit
GitHub Action (SARIF + inline PR annotations)
name: actenon-scan
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: Actenon/actenon-scan@main
with:
path: .
fail-on: medium
# SARIF is automatically uploaded to GitHub code scanning
# — inline PR annotations appear automatically
How it decides
A finding is raised when a SINK is (a) AGENT-REACHABLE and (b) UNGUARDED.
| Signal | What it means | Detection |
|---|---|---|
| SINK | A call to a consequential/irreversible operation (payment refund, DELETE FROM, kubectl apply, IAM grant) | AST pattern matching on call names + SQL string literals |
| AGENT-REACHABLE | The sink lives inside a registered agent tool (@tool, @mcp.tool, @server.tool, tool wrapper, or a class subclassing BaseTool) |
AST decorator/wrapper/class detection |
| UNGUARDED | No preceding authority/proof check on the path to the sink within the same function | Lexical precedence check for guard calls (verify_proof, authorize, check_permission) or guard decorators |
If the sink is not agent-reachable (e.g., it's in a plain script with no agent framework imports), no finding is raised. This keeps false positives low.
If the sink is guarded (an authorize() or verify_proof() call appears before it in the same function, or the function has a guard decorator), no finding is raised.
Fix it
When actenon-scan flags a finding, the fix is to add a proof-bound authority check before the consequential call:
# BEFORE (flagged)
@tool
def process_refund(payment_id: str, amount: int):
stripe.Refund.create(payment_intent=payment_id, amount=amount)
# AFTER (clean)
@tool
def process_refund(payment_id: str, amount: int):
from actenon import verify_proof
verify_proof(action="refund", target=payment_id, amount=amount)
stripe.Refund.create(payment_intent=payment_id, amount=amount)
The scanner recognises both Actenon guards (verify_proof, require_proof, actenon.*) and generic guards (authorize, check_permission, require_approval). So even if you don't adopt Actenon, adding a guard silences the finding — the tool is honestly useful to non-adopters.
- actenon-kernel — the open proof gate
- actenon-permit — the developer-first issuer + PDP + broker
Suppression
If a finding is a false positive, suppress it inline:
# actenon-scan: ignore[PAY-STRIPE-REFUND]
stripe.Refund.create(...)
Or use a baseline to suppress already-known findings:
actenon-scan scan . --format json > baseline.json
# Future runs suppress these:
actenon-scan scan . --baseline baseline.json
CLI
actenon-scan scan <path> [--format pretty|json|sarif] [--fail-on none|low|medium|high]
[--config config.json] [--baseline baseline.json]
[--include GLOB]... [--exclude GLOB]...
actenon-scan rules # list active rules
actenon-scan init # write a default config
Exit codes
0— no findings at or above the--fail-onthreshold (default:medium)1— findings present at or above the threshold
This makes the tool CI-gating.
License
Apache-2.0. See LICENSE.
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 actenon_scan-0.1.0.tar.gz.
File metadata
- Download URL: actenon_scan-0.1.0.tar.gz
- Upload date:
- Size: 25.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
94e778a9e8f156be4dc7ff090f17721aa09a109454f8339fccd895d759876828
|
|
| MD5 |
5992f036341fd20d15ea2ff147e3f337
|
|
| BLAKE2b-256 |
32d761b2b0fd50a641d984f5544c89cd21e624ce989fb9bcbfe2ec7a1702f1aa
|
File details
Details for the file actenon_scan-0.1.0-py3-none-any.whl.
File metadata
- Download URL: actenon_scan-0.1.0-py3-none-any.whl
- Upload date:
- Size: 32.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bdd8ef68f6289f852b172d1c5ee5352c932f106d3d5f7ead88bbb3567e908aad
|
|
| MD5 |
9d2be17205de64ee3f33a472b292425b
|
|
| BLAKE2b-256 |
28fdc5bf20a625c39a0e6200df60978556b5f6ee45de7797cb1c9d71ed5bf88b
|