🔶 我扫描了自己的 AI Agent 框架中破坏性/高后果操作,结果令人震惊
英文标题:🔶 I scanned my AI agent framework for destructive/consequential actions, and wow
Actenon Scan 是一款面向 Python、TypeScript 和 Go 的静态扫描工具,用于发现由模型或 Agent 控制的输入在缺少可强制执行的授权检查时,能够触达支付、代码仓库变更、数据删除、命令执行等高后果操作的路径。工具无需安装配置或云账号,首次扫描即可生成“爆炸半径”摘要,并支持解释执行路径、生成修复差异、输出 HTML、Markdown 和 SARIF 报告。它明确强调扫描结果并不自动等同于可被外部攻击利用的漏洞,但可帮助开发者识别需进一步审查的高风险 Agent 权限边界。该工具还提供 GitHub Action、pre-commit 集成、基线抑制、自定义守卫规则及自定义 sink 配置,适用于将 Agent 安全检查纳入 CI/CD 流程。
62.0 KB
中文
找出由 Agent 控制的意图在没有可强制执行的权限检查时触达高后果操作的位置。**解析 Python、TypeScript 和 Go。**暂不支持其他语言。
无需安装、无需配置、无需云账号。首次运行会生成一份爆炸半径摘要:
Your agent can reach 12 consequential actions without a dominating authorization check.
REPOSITORY 4 create_file, merge, delete_ref, publish_release
MONEY 3 process_refund, issue_credit, charge_card
DATA LOSS 2 delete_s3_prefix, purge_workspace
EXECUTION 2 deploy, run_migration
EGRESS 1 notify_customers
Most exposed: app/tools.py:47 process_refund()
Reachable by: @mcp.tool()
Consequence: MONEY
Guard evidence: none found on the analysed path
Model-controlled inputs: payment_intent, amount
12 findings in 340 files (0.42s)
Next:
actenon-scan explain app/tools.py:47
actenon-scan fix app/tools.py:47
然后可进一步深入分析:
actenon-scan explain app/tools.py:47 # 显示已分析的执行路径
actenon-scan fix app/tools.py:47 # 生成修复差异
actenon-scan brief app/tools.py:47 # 用于对外沟通的一页报告
actenon-scan scan . --format html # 独立 HTML 报告
actenon-scan scan . --format markdown # 粘贴至 GitHub issue
actenon-scan scan . --format sarif # 上传至 GitHub 代码扫描
Actenon Scan 检测什么
Actenon Scan 会找出模型控制或 Agent 控制的参数,在已分析路径中没有主导性权限检查的情况下,触达高后果操作的位置——例如支付、代码仓库变更、文件删除、Shell 命令、数据库写入或发送电子邮件。
可检测的后果类别:
| 类别 | 示例 |
|---|---|
| REPOSITORY | PyGithub create_file/delete_file、原始 GitHub REST、GitPython push/commit |
| MONEY | Stripe 退款、Braintree 扣费、通用支付 SDK 调用 |
| DATA LOSS | SQL DELETE、s3.delete_objects、文件删除 |
| EXECUTION | subprocess、os.system、eval/exec、容器创建 |
| DATABASE | 通过调用方控制的 SQL 使用 psycopg2/sqlite3 execute |
| EGRESS | 向外部 URL 发起 requests/httpx POST/PUT/DELETE |
| MESSAGING | SMTP 电子邮件、Slack、Twilio、SendGrid、Resend、SES |
| IDENTITY | IAM 变更、访问控制变更 |
| SECRETS | Secrets Manager 读取 |
| DEPLOYMENT | kubectl、terraform、helm |
| FILE | 文件写入、chmod、重命名 |
| BROWSER | Playwright page.click/fill、Selenium send_keys |
| PROVIDER | Provider SDK 调用(aws-sdk、gcp、azure) |
它不会证明什么
扫描器可能证明:
- 某个 Agent 或模型控制的参数能够触达高后果操作;
- 在已分析路径中未发现主导性权限检查;
- 存在一个可识别的 sink。
扫描器不会自动证明:
-
攻击者能够从外部触达该 Agent;
-
已支持架构或已分析文件之外不存在守卫机制;
-
攻击利用在实践中可行;
-
操作不可逆;
-
该发现就是一个漏洞。
actenon-scan explain path/to/file.py:42
显示已分析的执行路径:Agent 入口点 → 模型控制的输入 → 执行路径 → 守卫证据 → 后果。包含一个强制性的“这不会证明什么”章节。
actenon-scan fix path/to/file.py:42 # 自动选择模式,打印差异
actenon-scan fix path/to/file.py:42 --mode guard # 仓库原生守卫
actenon-scan fix path/to/file.py:42 --mode approval # 框架审批
actenon-scan fix path/to/file.py:42 --mode actenon # Actenon 证明验证
actenon-scan fix path/to/file.py:42 --apply # 写入变更
修复建议按中立顺序提供:首先是仓库原生守卫,其次是框架原生审批,最后是 Actenon 证明验证。不会强制在每一项建议中使用 Actenon。
GitHub Action(固定评论 + SARIF,零配置)
# .github/workflows/actenon-scan.yml
name: actenon-scan
on:
pull_request:
push:
branches: [main]
schedule:
- cron: '0 6 * * *'
jobs:
scan:
runs-on: ubuntu-latest
permissions:
pull-requests: write # 用于固定评论
security-events: write # 用于将 SARIF 上传至 Security 标签页
contents: read
steps:
- uses: actions/checkout@v4
- uses: Actenon/actenon-scan@v1
就是这样。该 Action:
- 在 PR 中仅扫描变更文件(在 push/schedule 时扫描完整代码树)
- 对含有发现的 PR 发布固定的爆炸半径评论(原地更新)
- 将 SARIF 上传至 GitHub Security 标签页
- 默认不会使构建失败
**准备好强制执行了吗?**在完成基线分类后,添加 fail-on: high:
- uses: Actenon/actenon-scan@v1
with:
fail-on: high
| 输入 | 默认值 | 描述 |
|---|---|---|
path |
. |
要扫描的路径 |
fail-on |
none |
在此严重级别失败(none/low/medium/high)。默认值:none。CLI 默认值为 medium(有意不同——见下文“退出代码”)。 |
config |
"" |
配置文件路径 |
baseline |
"" |
用于抑制已知发现的 baseline.json 路径 |
scan-scope |
auto |
changed(仅 PR)、full(整个仓库)或 auto |
comment-on-pr |
true |
在 PR 上发布固定爆炸半径评论 |
upload-sarif |
true |
将 SARIF 上传至 Security 标签页 |
version |
"" |
固定扫描器版本(默认:Action 自身的版本) |
| 输出 | 描述 | |
| --- | --- | |
findings-count |
发现总数 | |
high-count |
HIGH 严重级别发现数 | |
medium-count |
MEDIUM 严重级别发现数 | |
low-count |
LOW 严重级别发现数 | |
sarif-path |
SARIF 文件路径 |
# .pre-commit-config.yaml
repos:
- repo: https://github.com/Actenon/actenon-scan
rev: v1.1.4
hooks:
- id: actenon-scan
args: [--changed-only, HEAD]
# 可选:同时扫描 TypeScript/JavaScript 和 Go 文件。需要
# [typescript] / [go] extras(由 pre-commit 自动安装)。
- id: actenon-scan-typescript
- id: actenon-scan-go
除非你显式配置 fail-on: high,默认工作流不会仅因发现问题而阻止合并。
| 代码 | 含义 |
|---|---|
| 0 | 未发现达到或高于 --fail-on 阈值的问题 |
| 1 | 发现达到或高于阈值的问题(或传入 --fail-on-unsupported 且发现不受支持的文件) |
| 2 | 使用错误(参数错误、文件缺失) |
除非传入 --fail-on-unsupported,仅有不受支持的文件不会触发退出代码 1。如果一次扫描发现 0 个受支持文件、但有 N 个不受支持文件,则会以退出代码 0 结束,并在输出中显示明显警告。
CLI 与 Action:--fail-on 的默认值有意不同
| 界面 | 默认值 | 原因 |
|---|---|---|
CLI(actenon-scan scan .) |
medium |
CLI 的退出代码是其唯一的机器可读信号。如果扫描器发现 8 个无守卫的高后果操作却返回 0,CI 就会在无声中通过——这正是本工具旨在消除的“虚假安全感”失败模式。medium 是 SAST/代码检查工具的近乎通用约定(semgrep、bandit、eslint、shellcheck 都如此)。 |
GitHub Action(uses: Actenon/actenon-scan@v1) |
none |
Action 有自己的报告界面——固定 PR 评论和上传至 Security 标签页的 SARIF。即使检查为绿色,发现仍然可见。对于在未经分类的基线上采用该工具的团队而言,宽松默认值是合理的。完成分类后可设置 fail-on: high。 |
不要“修复”它们以让两者重新对齐——这两个界面具有不同的报告能力,其默认值反映了这种差异。
actenon-scan scan . --format html --output report.html # 独立 HTML
actenon-scan scan . --format markdown --output report.md # GitHub issue / PR
actenon-scan scan . --format sarif --output report.sarif # GitHub 代码扫描
actenon-scan scan . --format list # 旧式 linter 风格列表
actenon-scan scan . --format json --output report.json # 机器可读
HTML 报告是独立的(没有外部脚本、字体或资产),可安全地在本地打开,并包含可见的诚实声明:“本次扫描验证了什么 / 未验证什么”。
如何抑制或建立已知发现的基线
# 内联抑制单个发现——两种语法均可接受:
# actenon-scan: ignore[REPOSITORY-MUTATION]
# actenon-scan: suppress REPOSITORY-MUTATION
# 为已知发现建立基线:
actenon-scan scan . --baseline known-findings.json
# 根据当前扫描生成基线(锁定今天的发现,
# 使未来扫描中仅显示新增发现):
actenon-scan baseline . --output known-findings.json
扫描包含测试夹具的仓库
如果仓库包含安全测试夹具——即用于测试扫描器或自身安全控制、刻意不设守卫的代码——请在仓库根目录添加 .actenon-scan.json,将它们排除:
{
"_comment": "Exclude test fixtures that contain intentionally unguarded code.",
"exclude": [
"tests/benchmark/**",
"tests/corpus/**",
"tests/fixtures/**"
]
}
该文件会被 actenon-scan scan . 自动检测——无需 --config 标志。该仓库本身也提供了一个;参见 .actenon-scan.json。
如何配置自定义守卫
# 无需配置文件,按名称注册守卫:
actenon-scan scan . --guard my_authorize --guard require_permission
# 或使用配置文件:
actenon-scan scan . --config actenon-scan.json
actenon-scan init --format json # 写入默认配置
自定义 sink 和可达性信号(仅配置,无需修改代码)
无需 fork 仓库即可扩展检测能力。配置文件的 sinks 和 reachability 键会在加载时与默认规则合并:
{
"guard_patterns": ["my_authorize"],
"sinks": [
{
"id": "MY-PAYMENT-CHARGE",
"category": "payments",
"severity": "high",
"description": "Custom payment SDK charge call",
"match": {
"type": "qualified_call",
"qualified_names": ["my_payments.charge"]
},
"priority": 20
}
],
"reachability": {
"tool_decorators": ["my_agent_tool", "component"],
"tool_base_classes": ["MyAgentBase", "dspy.Module"]
}
}
这使你能够为未被识别的 Agent 框架添加支持(例如 Haystack @component、DSPy dspy.Module、ControlFlow @task),而无需等待上游发布。完整 schema 参见 default_rules.json,匹配类型参考见 CONTRIBUTING.md。
上述每一项声明均经过机器验证
claims: machine-verified 徽章链接至一个 CI 门禁(verify-claims.yml):该门禁会在每个 PR、每次推送至 main 以及每天一次执行;如果本 README 对 t 的任何事实性声明
English / 原文
Find where agent-controlled intent reaches consequential actions without an enforceable authority check. Parses Python, TypeScript, and Go. Other languages are not supported.
No install, no config, no cloud account. The first run produces a blast-radius summary:
Your agent can reach 12 consequential actions without a dominating authorization check.
REPOSITORY 4 create_file, merge, delete_ref, publish_release
MONEY 3 process_refund, issue_credit, charge_card
DATA LOSS 2 delete_s3_prefix, purge_workspace
EXECUTION 2 deploy, run_migration
EGRESS 1 notify_customers
Most exposed: app/tools.py:47 process_refund()
Reachable by: @mcp.tool()
Consequence: MONEY
Guard evidence: none found on the analysed path
Model-controlled inputs: payment_intent, amount
12 findings in 340 files (0.42s)
Next:
actenon-scan explain app/tools.py:47
actenon-scan fix app/tools.py:47
Then dig deeper:
actenon-scan explain app/tools.py:47 # show the analysed execution path
actenon-scan fix app/tools.py:47 # generate a remediation diff
actenon-scan brief app/tools.py:47 # one-page report for outreach
actenon-scan scan . --format html # self-contained HTML report
actenon-scan scan . --format markdown # paste into a GitHub issue
actenon-scan scan . --format sarif # upload to GitHub code scanning
What Actenon Scan detects
Actenon Scan finds places where a model-controlled or agent-controlled parameter reaches a consequential action — a payment, a repository mutation, a file deletion, a shell command, a database write, an email send — without a dominating authority check in the analysed path.
Consequence categories detected:
| Category | Examples |
|---|---|
| REPOSITORY | PyGithub create_file/delete_file, raw GitHub REST, GitPython push/commit |
| MONEY | Stripe refunds, Braintree charges, generic payment SDK calls |
| DATA LOSS | SQL DELETE, s3.delete_objects, file deletion |
| EXECUTION | subprocess, os.system, eval/exec, container creation |
| DATABASE | psycopg2/sqlite3 execute with caller-controlled SQL |
| EGRESS | requests/httpx POST/PUT/DELETE to external URLs |
| MESSAGING | SMTP email, Slack, Twilio, SendGrid, Resend, SES |
| IDENTITY | IAM mutations, access-control changes |
| SECRETS | Secrets-manager reads |
| DEPLOYMENT | kubectl, terraform, helm |
| FILE | File writes, chmod, rename |
| BROWSER | Playwright page.click/fill, Selenium send_keys |
| PROVIDER | Provider-SDK calls (aws-sdk, gcp, azure) |
What it does not establish
The scanner may establish that:
- an agent or model-controlled parameter reaches a consequential action;
- no dominating authority check was found in the analysed path;
- a recognised sink is present.
The scanner does not automatically establish that:
-
an attacker can externally reach the agent;
-
no guard exists outside the analysed file or supported architecture;
-
exploitation is practical;
-
the operation is irreversible;
-
the finding is a vulnerability.
actenon-scan explain path/to/file.py:42
Shows the analysed execution path: agent entry point → model-controlled inputs → execution path → guard evidence → consequence. Includes a mandatory "What this does NOT establish" section.
actenon-scan fix path/to/file.py:42 # auto-select mode, print diff
actenon-scan fix path/to/file.py:42 --mode guard # repository-native guard
actenon-scan fix path/to/file.py:42 --mode approval # framework approval
actenon-scan fix path/to/file.py:42 --mode actenon # Actenon proof verification
actenon-scan fix path/to/file.py:42 --apply # write the change
Remediation is offered in neutral order: repository-native guard first, framework-native approval second, Actenon proof verification third. Actenon is not forced into every recommendation.
GitHub Action (sticky comment + SARIF, zero config)
# .github/workflows/actenon-scan.yml
name: actenon-scan
on:
pull_request:
push:
branches: [main]
schedule:
- cron: '0 6 * * *'
jobs:
scan:
runs-on: ubuntu-latest
permissions:
pull-requests: write # for the sticky comment
security-events: write # for SARIF upload to Security tab
contents: read
steps:
- uses: actions/checkout@v4
- uses: Actenon/actenon-scan@v1
That's it. The action:
- Scans only changed files on PRs (full tree on push/schedule)
- Posts a sticky blast-radius comment on PRs with findings (updated in place)
- Uploads SARIF to the GitHub Security tab
- Does not fail the build by default
Ready to enforce? Add fail-on: high once you've triaged your baseline:
- uses: Actenon/actenon-scan@v1
with:
fail-on: high
| Input | Default | Description |
|---|---|---|
path |
. |
Path to scan |
fail-on |
none |
Fail at this severity (none/low/medium/high). Default: none. The CLI default is medium (intentionally different — see Exit codes below). |
config |
"" |
Path to config file |
baseline |
"" |
Path to baseline.json for known-findings suppression |
scan-scope |
auto |
changed (PR only), full (entire repo), or auto |
comment-on-pr |
true |
Post sticky blast-radius comment on PRs |
upload-sarif |
true |
Upload SARIF to Security tab |
version |
"" |
Pin scanner version (default: action's own version) |
| Output | Description | |
| --- | --- | |
findings-count |
Total findings | |
high-count |
HIGH-severity findings | |
medium-count |
MEDIUM-severity findings | |
low-count |
LOW-severity findings | |
sarif-path |
Path to the SARIF file |
# .pre-commit-config.yaml
repos:
- repo: https://github.com/Actenon/actenon-scan
rev: v1.1.4
hooks:
- id: actenon-scan
args: [--changed-only, HEAD]
# Optional: scan TypeScript/JavaScript and Go files too. Requires
# the [typescript] / [go] extras (auto-installed by pre-commit).
- id: actenon-scan-typescript
- id: actenon-scan-go
The default workflow does not block merging solely because findings exist unless you explicitly configure fail-on: high.
| Code | Meaning |
|---|---|
| 0 | No findings at or above the --fail-on threshold |
| 1 | Findings at or above the threshold (or --fail-on-unsupported and unsupported files found) |
| 2 | Usage error (bad arguments, missing file) |
Unsupported files alone do not trigger exit 1 unless --fail-on-unsupported is passed. A scan that finds 0 supported files but N unsupported files exits 0 with a visible warning in the output.
CLI vs Action: the --fail-on default intentionally differs
| Surface | Default | Why |
|---|---|---|
CLI (actenon-scan scan .) |
medium |
The CLI's exit code is its only machine-readable signal. A scanner that finds 8 unguarded consequential actions and returns 0 passes CI silently — the false-assurance failure this tool exists to close. medium is the near-universal convention for SAST/linter tools (semgrep, bandit, eslint, shellcheck all do it). |
GitHub Action (uses: Actenon/actenon-scan@v1) |
none |
The Action has its own reporting surface — a sticky PR comment + SARIF upload to the Security tab. Findings stay visible even when the check is green. A soft default is defensible for teams adopting the tool against an untriaged baseline. Set fail-on: high once you've triaged. |
Do not "fix" them back into alignment — the two surfaces have different reporting capabilities, and the defaults reflect that.
actenon-scan scan . --format html --output report.html # self-contained HTML
actenon-scan scan . --format markdown --output report.md # GitHub issue / PR
actenon-scan scan . --format sarif --output report.sarif # GitHub code scanning
actenon-scan scan . --format list # old linter-style list
actenon-scan scan . --format json --output report.json # machine-readable
HTML reports are self-contained (no external scripts, fonts, or assets), safe to open locally, and include the visible honesty statement: "What this scan verified / did not verify".
How to suppress or baseline known findings
# Suppress a single finding inline — BOTH syntaxes are accepted:
# actenon-scan: ignore[REPOSITORY-MUTATION]
# actenon-scan: suppress REPOSITORY-MUTATION
# Baseline known findings:
actenon-scan scan . --baseline known-findings.json
# Generate a baseline from the current scan (locks in today's findings
# so only NEW findings appear in future scans):
actenon-scan baseline . --output known-findings.json
Scanning a repo that contains test fixtures
If your repo contains security test fixtures — deliberately unguarded code used to test the scanner or your own security controls — add an .actenon-scan.json at the repo root to exclude them:
{
"_comment": "Exclude test fixtures that contain intentionally unguarded code.",
"exclude": [
"tests/benchmark/**",
"tests/corpus/**",
"tests/fixtures/**"
]
}
The file is auto-detected by actenon-scan scan . — no --config flag needed. This repo ships one itself; see .actenon-scan.json.
How to configure custom guards
# Register a guard by name without a config file:
actenon-scan scan . --guard my_authorize --guard require_permission
# Or use a config file:
actenon-scan scan . --config actenon-scan.json
actenon-scan init --format json # write a default config
Custom sinks and reachability signals (config-only, no code changes)
You can extend detection without forking the repo. The config file's sinks and reachability keys are merged with the defaults at load time:
{
"guard_patterns": ["my_authorize"],
"sinks": [
{
"id": "MY-PAYMENT-CHARGE",
"category": "payments",
"severity": "high",
"description": "Custom payment SDK charge call",
"match": {
"type": "qualified_call",
"qualified_names": ["my_payments.charge"]
},
"priority": 20
}
],
"reachability": {
"tool_decorators": ["my_agent_tool", "component"],
"tool_base_classes": ["MyAgentBase", "dspy.Module"]
}
}
This is how you add support for an unrecognised agent framework (e.g. Haystack @component, DSPy dspy.Module, ControlFlow @task) without waiting for an upstream release. See default_rules.json for the full schema and CONTRIBUTING.md for the match-type reference.
Every claim above is machine-verified
The claims: machine-verified badge links to a CI gate (verify-claims.yml) that fails on every PR, push to main, and once a day if any factual claim this README makes about the package stops being true:
- Zero runtime dependencies — scan's single most important credibility claim (it is what lets a security team deploy scan unilaterally into a codebase that has adopted nothing). Read from
pyproject.toml, not prose. - Install commands — every
pip installin this README is resolved against the live registry; the Python version badge is generated, not hand-edited. - The ecosystem table — rendered from the protocol's
ecosystem.yaml, never hand-edited.
If a claim drifts, the badge goes red before a human notices.
Scan is one of the independent repositories that together close the execution gap — the gap between upstream authorization and the execution edge that actually performs a consequential side effect.
| Repository | Role | Depends on | Packages |
|---|---|---|---|
actenon-protocol |
The neutral wire contract — what every artefact looks like on the wire | — | actenon-protocol (PyPI) · @actenon/protocol-types (npm) |
actenon-kernel |
The open verifier — defines what a valid proof is | actenon-protocol |
actenon-kernel (PyPI) |
actenon-permit |
The developer on-ramp and authority broker | actenon-kernel, actenon-protocol |
actenon-permit (PyPI) · @actenon/sdk (npm) |
actenon-scan ← you are here |
The independent static-analysis scanner | — | actenon-scan (PyPI) |
Optional: actenon-cloud — a managed control plane (source-available; see its LICENSE). Not required by any component above; every capability in this ecosystem works without it.
Also available: sdk-go (Go verifier SDK) and sdk-rust (Rust verifier SDK) for non-Python integrations.
Measured against a pinned real repository, not a synthetic tree: langchain-ai/langchain @fa7ce76, 1,954 Python files scanned. The pin, every figure below, and the raw crossover table live in tests/benchmark/perf-fixture.json.
Scan time depends on your core count , so the range is stated across the machines actually measured rather than as a single number you might not be able to reproduce:
| langchain, 1,954 files | serial | default (auto) |
|---|---|---|
| 10-core Apple Silicon | 1.9s | 0.96s |
| 2–4 core CI container | ~5.2s | ~5.2s (stays serial) |
Other repositories on the 10-core host: crewai (957 files) 2.4s → 1.1s, openai-agents (539) 2.1s → 0.67s, mcp-python-sdk (586) 0.6s → 0.29s.
Why the default is not always parallel
Parallelism is not free, and it does not pay off everywhere. Measured, it is ~2.5x faster on a 10-core machine and ~10% slower on a 2–4 core CI runner, where there are no spare cores to absorb the workers.
So --jobs auto-tunes: it parallelises only at 8+ cores and 250+ files , and stays serial otherwise. The 5–7 core range is unmeasured and therefore treated as serial — the default is the mode that is never slower, not the one that is sometimes faster.
--jobs N always overrides the auto-tune. --jobs 1 forces serial. Findings are identical either way; that equivalence is a test, not a claim.
Pre-commit path: --changed-only scans a 1–3 file diff in ~150ms.
What Scan recognises as a guard
Scan recognises three classes of guards. A sink is "guarded" if a recognised guard call appears lexically before the sink in the same function body, OR a recognised guard decorator wraps the function containing the sink. (This is a v1 lexical-precedence heuristic — documented limitation.)
30+ vendor-neutral guard patterns
Recognised out of the box, no Actenon required:
authorize,check_permission,require_permission,has_role,require_roleverify_proof,verify_token,verify_signaturejwt_required,require_jwt,validate_jwtopa_eval,casbin_enforce,cedar_authorizeverify_api_key,verify_mtls,verify_client_certrequire_auth,require_authz,auth_requiredcan,may,is_allowed,check_accessauthorize_request,authorize_action- …and the rest in
actenon_scan/detectors/guards.py
Recognised when you are using the Actenon ecosystem:
verify_pccb,PCCBVerifier,PCCBVerifier.verifyProtectedExecutor,ProtectedExecutor.executeActenon,Actenon.local,Actenon.cloudBroker,Broker.execute,Broker.execute_via_adapterGateway,Gateway.executeBoundaryMiddleware,BoundaryVerifier,BoundaryVerifier.verify_boundary
Register your own guard function names:
actenon-scan init
# Edit actenon-scan.json:
# {
# "guards": ["my_internal_check", "company_authorize", "acme_can"]
# }
actenon-scan scan ./my-agent-code
Custom guards are first-class — Scan does not privilege Actenon guards over yours.
Remediation routes — 7 per finding, 3 are Actenon-free
Each finding ships with seven remediation routes. Scan does not pretend Actenon is the only answer.
- Add an existing internal guard. If your codebase already has a guard function Scan didn't recognise, just add it via
actenon-scan initand the finding disappears. - Register the guard with Scan. Same as above — Scan treats your guards as first-class.
- Use Actenon Kernel (proof verification). For when you want proof-bound execution at the resource boundary. The Kernel is the trust anchor; it works without Permit or Cloud.
- Use brokered Actenon protection (Permit + adapter). For when you control the agent framework and want credentials never to reach the agent. The recommended developer on-ramp.
- Use resource-owned verification. For when the resource itself is the protected endpoint (FastAPI route, Express endpoint, Go handler). The Boundary Kit automates this.
- Use Cloud-managed Actenon. For when you want a hosted control plane with 9-layer evidence bundles. Optional.
- Redesign the boundary. Sometimes the right answer is to remove the consequential action from the agent's reachable tool surface entirely.
Cloud is optional. Local brokered protection (route 4) works without any Cloud login.
Scan depends on nothing at runtime. No Permit, no Kernel, no Cloud, no Protocol. actenon-protocol is a dev-only dependency, used solely by the drift-gate test (tests/test_protocol_drift.py) to verify that Scan's guard vocabulary and refusal-code references stay in sync with the Protocol's catalogue — it is not installed when you pip install actenon-scan. See pyproject.toml.
Scan is a standalone security tool. You can adopt it without adopting anything else from Actenon, and you can stop using it without affecting any other Actenon component.
Submit a case where the scanner gets it wrong. Every accepted case becomes a permanent test fixture, credited by GitHub handle.
In scope: a supported-language sink the scanner misses, a guard in the recognised vocabulary that it fails to honour, a finding on code with no model-controlled input on the path, or any case where it reports clean on something it should not.
Out of scope: unsupported languages, guard names outside the default vocabulary where --guard resolves it, anything documented as NOT COVERED in docs/COVERAGE.md.
The scoreboard lists every case — including the ones that are accepted but not yet fixed. The open misses are the point: a challenge that only shows fixed cases is a trophy cabinet.
Apache-2.0 — see LICENSE.