cain-agent: a Claude Agent SDK pentest pipeline with hard scope enforcement
Real-world AI penetration testing engineer for authorized assessments — built-in cloud module covering AWS/Azure/GCP + Aliyun/Tencent/Huawei clouds. Built on Claude Agent SDK
At a glance
- What is it?
- Cain wraps the Claude Agent SDK in a deterministic recon, test and report pipeline for authorized engagements, with a cloud module for AWS, Azure, GCP, Aliyun, Tencent Cloud and Huawei Cloud. The interesting part is not the model choice, it is the way scope and validation are enforced outside the model.
- Who is it for?
- Adopt cain-agent if you run authorized assessments against web targets and want per-stage model routing, a machine-readable aggregate, and scope enforcement that lives in a hook rather than in a prompt. Do not adopt it if you need a documented rollback path, a stable CLI contract, or any use outside engagements you are authorized to perform.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap cain-agent targets: lab agents versus authorized engagements
Most LLM security agents are built around capture-the-flag targets. The README draws that contrast explicitly: CTF and lab agents work against static environments with preset flags, while cain-agent is positioned for real enterprise assets, bug bounty programmes and authorized engagements. The stated vulnerability focus is business-logic flaws, authentication chains and cloud misconfigurations rather than known syntax patterns. The deliverable is described as an auditable evidence chain, a reproducible PoC and remediation advice, not a flag.
That framing sets the audience. This is a tool for a penetration tester who already has written authorization, a defined target list and an obligation to produce a report someone else can audit. It is not a scanner you point at arbitrary hosts, and it is not a teaching environment. The README repeats the word authorized, and the safety section claims read-only behaviour by default, scope enforcement and credential redaction. Those are the claims worth checking in code before you trust the tool with a client engagement.
Deterministic orchestration wrapped around agent freedom
The design principle stated in the README is that deterministic engineering constrains agent freedom. Stage transitions, scope enforcement and dangerous-operation circuit breakers are hard constraints; path selection and evidence analysis are left to the agent. The architecture diagram shows the flow: the CLI invokes a run, a scope bootstrap step converts the target into scope.yaml, and an orchestrator drives the stages.
Three pipeline stages are named: recon, test and report. A detail that matters more than the diagram is the finder and validator split. The README says every execution channel mounts the same ScopeGuardHook and that the finder is not the validator, described as dual-session semantics. In practice that means the session that proposes a finding is not the session that confirms it, which is the standard way to reduce self-confirmation bias in an LLM pipeline. The hook is the enforcement point, and the README frames scope as an engineering constraint rather than something the model is asked to respect. That is the correct place for it. A model that can be argued out of a boundary has no boundary.
Install and first run: scope.yaml is the contract
Installation is editable-mode Python. The README gives:
git clone https://github.com/cdxiaodong/cain-agent cd cain-agent pip install -e .
The cloud extras are separate: pip install -e ".[cloud]" adds AWS S3, Huawei OBS and Kubernetes checks. A version check confirms the CLI is present: cain-agent --version.
A run needs a target and a wall-clock budget:
cain-agent run --target https://app.example.com --total-budget 1800
The flags listed are --target (required), --workspace (state directory, default ./workspace), --total-budget in seconds and --idle-timeout per step. The README states the target is written into scope.yaml and enforced on every tool call, so scope.yaml is the file to read first in the repository. If you are evaluating this for a real engagement, verify how that file is parsed and what happens when a discovered host falls outside it. The README does not document that failure path.
Two execution engines and per-stage model routing
The default backend is claude. The alternative is pi, which requires Node.js 20 or newer and a one-time bridge install:
npm ci --prefix toolchain/pi export ANTHROPIC_API_KEY="your-api-key" cain-agent run --target https://app.example.com --backend pi
The pi backend accepts other providers through --pi-provider and --pi-model, with the provider's standard key variable set, for example OPENAI_API_KEY, GEMINI_API_KEY, DEEPSEEK_API_KEY or OPENROUTER_API_KEY. For an Anthropic Messages-compatible gateway, PI_BASE_URL plus ANTHROPIC_AUTH_TOKEN is the documented pair.
The routing design is the most interesting engineering decision here. Recon is repetitive enumeration; the test stage needs a strong model for vulnerability judgement. So the two discovery stages can run different engines:
cain-agent run --target https://app.example.com --recon-backend pi --recon-provider anthropic --recon-model your-gateway-model-id --test-backend claude
The report stage keeps its own channel through --pi-validation-provider and --pi-validation-model. Fallback rules are explicit: per-stage flags fall back to --backend, and provider or model flags fall back to --pi-provider and --pi-model. With no per-stage flags at all, the two discovery stages share one session, which the README calls zero default change. The claim that no mix downgrades scope depends on ScopeGuardHook being mounted on every channel, which is a testable assertion rather than a documented guarantee.
What a finished run leaves behind
The workspace report directory holds three artifacts. report.md is the human-readable output: an executive summary with target, authorized scope, per-stage timings and finding counts, a findings table with severity markers, confidence and evidence-chain digests, per-finding details, an evidence-hash index, per-issue-type remediation advice and a legal disclaimer. aggregated-report.json is the machine-readable aggregate at schema_version 1, carrying the same source data for downstream systems. validation-summary.json holds four-state counts and failure details from the validation pipeline.
The evidence handling is the notable constraint: the README states evidence plaintext is never persisted, only hashes. That is a defensible choice for client work, and it also means the report alone will not let a reviewer re-read the raw request that produced a finding. If your engagement requires raw request and response retention, this design works against you. The markdown is rendered by a pure-Python template in src/cain_agent/report_markdown.py with no added dependencies, and the README claims the same aggregate in produces the same report out. The repository also mentions a self-built vulnerable-terraform benchmark with four-metric scoring, but the README does not publish the metric definitions or results, so treat that as an internal harness rather than a public comparison.
Where cain-agent is the wrong tool
The README does not document rollback. If a run writes state into the workspace and a stage fails, there is no described way to revert to a prior state or resume from a checkpoint, and no documented behaviour for a partially completed validation pipeline beyond the failure details in validation-summary.json. For an engagement that must be reproducible step by step, that is a gap you would need to close yourself.
Budget handling is wall-clock, not action-count. --total-budget and --idle-timeout cap time, so a target that responds slowly consumes budget without producing findings, and a fast target can generate a large number of tool calls inside the same window. There is no documented per-host or per-endpoint rate limit, which matters against production systems with WAF and risk control, the exact environment the README says Cain targets.
Coverage is also narrower than the topic list suggests. The cloud module is described as covering six providers, but the optional extras named are AWS S3, Huawei OBS and Kubernetes checks. The README does not enumerate equivalent checks for Azure, GCP, Aliyun or Tencent Cloud, so the depth per provider is unverified. The OWASP list (SQLi, XSS, SSRF, CSRF, file upload, XXE, command injection, path traversal) is web-focused; there is nothing about Active Directory, internal network pivoting or binary exploitation. Against an internal network engagement, this is the wrong tool.
Compared with a general-purpose agent framework
The obvious alternative is to build the same loop yourself on the Claude Agent SDK, or on a general agent framework, and write your own tools. The difference is where the constraints live. A generic framework gives you tool definitions and a loop; scope checking, the finder and validator split, the stage machine and the report schema are yours to write. Cain ships those as opinionated components, including ScopeGuardHook and the three-stage pipeline, which is the actual product.
The cost of that opinion is flexibility. A custom harness can target an internal network, retain raw evidence, or implement action-count budgets. Cain's documented surface is web and cloud targets with hashed evidence and time budgets. If your methodology already exists and is auditable, adopting Cain means adopting its stage model and its aggregate schema, not just its tools. The per-stage model routing is the feature hardest to replicate cheaply, since it requires the separate pi bridge and the fallback rules documented in toolchain/pi/README.md.
Maintenance, licence and upgrade cost
The repository is not archived, and the last push was on 2026-09-14. The most recent release is v0.2.0, dated 2026-08-25, described as orchestration validation loop and dual execution engines. That release lines up with the pi backend and per-stage routing documented in the README, so the documented features and the release notes are consistent. The README itself carries an actively developed marker, but the concrete signal is the push and release dates.
The licence is Apache-2.0, which permits commercial and closed-source use with the usual conditions: keep the licence and notice files, and state significant changes. Apache-2.0 also includes an explicit patent grant and a patent termination clause, unlike MIT. Nothing in the repository indicates a separate terms-of-service restriction, but the licence covers the code, not how you use it. Running an autonomous agent against systems you are not authorized to test is a legal question the licence does not address, and the README's legal disclaimer in report.md does not change that.
Upgrade cost is the practical concern. The CLI surface is already large: backend selection, per-stage backend, provider and model flags, gateway environment variables and the pi bridge toolchain. The README documents fallback rules for those flags, which suggests the surface is still moving, and v0.2.0 is an early version. Pin the version in your engagement environment and read the release notes before moving, because a change to the aggregate schema or the flag fallback rules would alter both your automation and your reports.
Editorial conclusion
Adopt cain-agent if you run authorized assessments against web targets and want per-stage model routing, a machine-readable aggregate, and scope enforcement that lives in a hook rather than in a prompt. Do not adopt it if you need a documented rollback path, a stable CLI contract, or any use outside engagements you are authorized to perform. Before installing, read scope.yaml parsing in the repository and confirm the ScopeGuardHook actually rejects a target outside the declared scope, because that hook is the whole safety argument.
Community notes