# Defense-in-Depth Score: PentestGPT

**Repo:** https://github.com/GreyDGL/PentestGPT · **Commit:** `e8b1bb77d1ac00329675cec3b060aba971ec1ac8` · **Reviewed:** 2026-10-03
**What it is:** Automated penetration testing agentic framework powered by LLMs (USENIX Security 2024)
**Category:** Cybersecurity
**Scored configuration:** The maintained autonomous framework (pentestgpt_agent, run with its default CLI options via make run on the operator's own machine), where both LLM roles get the provider CLI's full-access permission mode; the legacy interactive client and the root unified_agent copy are footnoted.
**Agent surface (default):** code execution yes · filesystem write yes · network egress yes · external credentials yes · persistent memory yes · untrusted input yes · third party extensions opt-in · sub agents yes · external communication no

## Score: 1.8 / 10.0 (Minimal)

| # | Criterion | S | C | D | B | Raw | Cap | Score | Confidence |
|---|---|---|---|---|---|---|---|---|---|
| C1 | Identity & least privilege | L0 | L0 | L0 | L0 | 0.00 | — | **0.00** | High |
| C2 | Approval gates | L0 | L0 | L0 | L0 | 0.00 | C2-POWERBYPASS | **0.00** | High |
| C3 | Tool & action scoping | L1 | L0 | L0 | L0 | 0.07 | — | **0.07** | High |
| C4 | Code-execution isolation | L0 | L0 | L0 | L0 | 0.00 | — | **0.00** | High |
| C5 | Untrusted input blast radius | L0 | L0 | L0 | L0 | 0.00 | C5-WORSTCASE | **0.00** | High |
| C6 | Memory, context & configuration integrity | L1 | L1 | L2 | L2 | 0.35 | — | **0.35** | Medium |
| C7 | Third-party extensions | L1 | L1 | L1 | L1 | 0.25 | — | **0.25** | High |
| C8 | Secrets & sensitive-data protection | L1 | L1 | L2 | L0 | 0.25 | — | **0.25** | High |
| C9 | Audit & traceability | L2 | L2 | L2 | L3 | 0.55 | — | **0.55** | High |
| C10 | Limits & kill switch | L1 | L1 | L2 | L1 | 0.30 | — | **0.30** | High |


PentestGPT's autonomous framework hands two LLM roles the provider CLI's full-access mode (no approval prompts, no in-process sandbox) and states that the surrounding deployment is the only security boundary. Deterministic code does constrain planning (typed decisions, target-field scope check, turn and decision caps, durable traces), but none of it binds what the shell tool can do or reach. Treat it as running with the operator's full authority and use it only inside a disposable, network-isolated environment holding the authorized targets.

## Critical gaps
- Both LLM roles run in the provider's full-access mode with no approval step, so the shell tool is ungated. (ASI02, ASI09, T2; C2) — [pentestgpt_agent/src/pentestgpt_agent/trial.py:73](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L73)
- A hijacked run can both leak data and take irreversible actions unattended, since nothing structural limits it after untrusted target content is read. (ASI01, LLM01, T6; C5) — [pentestgpt_agent/src/pentestgpt_agent/trial.py:73](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L73); [pentestgpt_agent/src/pentestgpt_agent/agents.py:47](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/agents.py#L47)
- No in-framework isolation: commands run as the operator, and the maintained docs say deployment isolation is the only boundary. (ASI05, T11; C4) — [pentestgpt_agent/src/pentestgpt_agent/trial.py:73](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L73)
- The agent holds the operator's ambient authority and provider login with no scoping, so a hijack inherits everything the account can reach. (ASI03, T3; C1) — [pentestgpt_agent/src/pentestgpt_agent/trial.py:73](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L73)

## Criterion details

### C1 Identity & least privilege — 0.00 (high)

The framework launches both LLM roles through the provider CLI in full-access mode, so the agent acts with the operator's whole OS identity and the provider login the CLI already holds. No credential is scoped, narrowed or withheld from the shell tool, and nothing checks authority per request. The only credential handling in the repo's own code is passing a few provider environment flags. A hijacked run can use anything the operator's account can reach.

- **S L0:** Ambient authority: both roles run with the provider's full-access permission mode and the operator's identity. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:73](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L73); [pentestgpt_agent/src/pentestgpt_agent/trial.py:96](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L96) (verified)
  - *To reach the next level:* A dedicated, scoped identity or an authorization gate in code before credentials are attached.
- **C L0:** No authorization layer exists; the only limits on authority are prompt text. — [pentestgpt_agent/src/pentestgpt_agent/agents.py:18](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/agents.py#L18); searched `rg -n -S -e 'approv|confirm|ask_user|permission_mode|human_in_the_loop'` in `pentestgpt_agent/src` → 0 hits (no authorization or approval code in the maintained framework) (verified)
  - *To reach the next level:* Every tool path goes through an in-code authorization layer.
- **D L0:** Default run is full access; narrowing requires editing source. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:314](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L314) (verified)
  - *To reach the next level:* Least-privilege default with explicit elevation.
- **B L0:** If the run is hijacked, the operator's whole account on the host (files, keys, other services reachable from it) is exposed; only deployment isolation, which the framework neither creates nor checks, limits it. — [pentestgpt_agent/README.md:24](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/README.md#L24); [Makefile:151](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/Makefile#L151) (verified)
  - *To reach the next level:* Credentials scoped to one project or tenant, mostly read-only.
- **Cap:** none

### C2 Approval gates — 0.00 (high)

There is no approval step anywhere in the maintained framework: every command and file write the model chooses runs immediately. The provider permission mode in effect is the one that skips all permission prompts. Deterministic code only decides which planned task runs next, not whether a given action may execute, and no undo or checkpoint exists for the actions taken.

- **S L0:** No human approval; the loop commits plans and tasks with no human step. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:73](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L73); searched `rg -n -S -e 'approv|confirm|ask_user|permission_mode|human_in_the_loop'` in `pentestgpt_agent/src` → 0 hits (zero matches; no approval concept in the framework) (verified)
  - *To reach the next level:* Per-call human approval showing the exact call.
- **C L0:** The shell tool, the most powerful path, is entirely ungated, as is every other provider tool. — [pentestgpt_agent/src/pentestgpt_agent/agents.py:16](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/agents.py#L16) (verified)
  - *To reach the next level:* Every tool path crosses an approval gate.
- **D L0:** Approval does not exist, so it cannot be on by default; the full-access mode is hard-coded. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:313](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L313) (verified)
  - *To reach the next level:* Approval on by default with a loudly named flag to disable.
- **B L0:** Destructive and irreversible actions (deleting or changing data on reachable hosts or the local machine) run with no checkpoint, undo or human involvement. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:73](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L73) (verified)
  - *To reach the next level:* Checkpoints or rollback for filesystem state and previews for external actions.
- **Cap:** C2-POWERBYPASS — The shell tool, the most powerful action path, runs with no gate in the default configuration.

### C3 Tool & action scoping — 0.07 (high)

The one real scope control is a deterministic check that each planned task's target field matches an operator-supplied allowed target, plus a phrase denylist for test-type tasks. It checks the plan's metadata, not the commands or network destinations the shell tool then uses, so the model can still act against any host or path. All provider tools are enabled by default.

- **S L1:** A denylist of phrases for test-type tasks and a canonical-URL allowlist on the task's target field; neither constrains tool arguments. — [pentestgpt_agent/src/pentestgpt_agent/plan.py:79](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/plan.py#L79); [pentestgpt_agent/src/pentestgpt_agent/plan.py:148](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/plan.py#L148) (verified)
  - *To reach the next level:* Allowlist validation of tool arguments (resolved paths, hosts, parameterized inputs) in code.
- **C L0:** No tool validates its inputs; plan validation applies only to the Supervisor's decision object. — [pentestgpt_agent/src/pentestgpt_agent/plan.py:193](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/plan.py#L193); searched `rg -n -S -e 'subprocess|os\.system|shell=True|eval\(|exec\('` in `pentestgpt_agent/src` → 0 hits (the framework has no tool implementations of its own; tools are the provider CLI's) (verified)
  - *To reach the next level:* All tools validated through a shared layer.
- **D L0:** Every provider tool, including write, exec and network, is enabled by default. — [pentestgpt_agent/src/pentestgpt_agent/agents.py:16](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/agents.py#L16) (verified)
  - *To reach the next level:* A read-only default tool set with explicit enabling of write and exec.
- **B L0:** A misused shell can run any command against any host or path the operator can reach. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:73](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L73) (verified)
  - *To reach the next level:* Scoped, quantity-bounded, reversible operations only.
- **Cap:** none

### C4 Code-execution isolation — 0.00 (high)

Model-chosen commands run in the provider CLI's shell as the operator's user, with the permission system set to skip checks. The framework adds no container, OS sandbox or filter and says plainly that isolation is the operator's job. The repository's Docker image is a separate tool image that does not contain the framework, gives its user passwordless sudo and adds a network-admin capability, so it is not a boundary for the scored mode.

- **S L0:** No isolation primitive: same-user execution with the provider's full-access mode. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:71](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L71); searched `rg -n -S -e 'docker|sandbox|seccomp|landlock|bwrap|chroot'` in `pentestgpt_agent/src` → 7 hits (hits are the SandboxPolicy.FULL_ACCESS selection and its trace/summary echoes, plus a comment; no isolation is created) (verified)
  - *To reach the next level:* At least a hardened container or OS sandbox profile enforced by the framework.
- **C L0:** The main exec path is unsandboxed, and no sandboxed path exists. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:73](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L73) (verified)
  - *To reach the next level:* The main exec tool sandboxed.
- **D L0:** Isolation is absent by default and is not offered as an option by the framework (G1 does not add a lower cap). — [pentestgpt_agent/README.md:24](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/README.md#L24) (verified)
  - *To reach the next level:* On by default with an explicit operator flag to disable.
- **B L0:** Host-equivalent: commands run as the operator with the provider login and the full environment reachable; in the tool image the user also has passwordless sudo. — [Dockerfile:70](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/Dockerfile#L70); [docker-compose.yml:20](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/docker-compose.yml#L20) (verified)
  - *To reach the next level:* Workspace-only mount, no secrets in the environment, egress limited.
- **Cap:** none
- **Notes:** The Docker image (Dockerfile, docker-compose.yml) is a tool environment that does not include pentestgpt_agent (Dockerfile comment and Makefile docker-run stub), so it was not scored as an alternative isolation mechanism.

### C5 Untrusted input blast radius — 0.00 (high)

Target responses and files are the agent's main input, and they flow into the model and into stored observations that later steer new tasks. The only defence is prompt text telling the roles to treat them as data. A hijacked run still has an unrestricted shell, network access and the operator's credentials with no human step, so it can both leak data and take irreversible actions.

- **S L0:** Prompt-only: the roles are told not to follow instructions in target data; nothing in code limits a hijacked agent. — [pentestgpt_agent/src/pentestgpt_agent/agents.py:47](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/agents.py#L47) (verified)
  - *To reach the next level:* Rule of Two enforced in code once untrusted content is read.
- **C L0:** Target output enters context with the same standing as other task text; no source is distinguished in code. — [pentestgpt_agent/src/pentestgpt_agent/agents.py:334](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/agents.py#L334); [pentestgpt_agent/src/pentestgpt_agent/agents.py:335](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/agents.py#L335) (verified)
  - *To reach the next level:* Every untrusted source handled by a structural limit.
- **D L0:** No control exists to be on by default. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:73](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L73) (verified)
  - *To reach the next level:* A control that is on by default and explicit to disable.
- **B L0:** Leak plus irreversible action are both available unattended: shell, network and credential files with no approval. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:73](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L73); [scripts/entrypoint.sh:87](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/scripts/entrypoint.sh#L87) (verified)
  - *To reach the next level:* Both exfiltration and irreversible actions need human approval.
- **Cap:** C5-WORSTCASE — A hijack can exfiltrate data and take irreversible actions with no human involved.

### C6 Memory, context & configuration integrity — 0.35 (medium)

Run state lives in a per-run SQLite file; each episode starts fresh and the maintained code turns off the Claude provider's own auto-memory. Stored observations must be exact slices of captured tool output, which gives provenance but also means target-controlled text is persisted and re-shown to later model calls. Instruction and settings files in the agent workspace are loaded by the provider CLI in the wrapper copy in the repo, which was not audited for the pinned external wrapper, so that path is treated as uncontrolled.

- **S L1:** Observations are persisted as exact receipt slices with provenance and bounded size, but target-controlled text is re-injected and no write is gated or expired; workspace instruction files are not controlled. — [pentestgpt_agent/src/pentestgpt_agent/execution.py:66](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/execution.py#L66); [pentestgpt_agent/src/pentestgpt_agent/memory.py:206](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/memory.py#L206) (verified)
  - *To reach the next level:* Entries presented as data with provenance, and project config unable to change security settings.
- **C L1:** Only the SQLite observation store is controlled; the workspace files the provider CLI auto-loads are not. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:62](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L62); [unified_agent/backends/claude_code.py:93](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/unified_agent/backends/claude_code.py#L93) (inferred)
  - *To reach the next level:* All memory stores and auto-loaded files controlled.
- **D L2:** Each run has its own directory and database, episodes are fresh (no resume of provider history), and the run directory is created owner-only. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:154](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L154); [pentestgpt_agent/src/pentestgpt_agent/trace.py:238](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trace.py#L238) (verified)
  - *To reach the next level:* The model cannot write to other namespaces and retention limits are on by default.
- **B L2:** Poisoned observations persist for the run and can steer later tasks that run ungated tools, but they are scoped to one run and held in an inspectable database. — [pentestgpt_agent/src/pentestgpt_agent/agents.py:335](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/agents.py#L335) (verified)
  - *To reach the next level:* Session-scoped and easily purged, with no ungated tool use triggered.
- **Cap:** none
- **Notes:** Legacy client (pentestgpt_legacy/llm/config.py:33) loads .env from the current directory and honours per-provider base-URL overrides, which would trigger C6-REPOCONFIG if that mode were scored; it is footnoted, not scored.

### C7 Third-party extensions — 0.25 (high)

The framework ships no plugin, MCP or marketplace loader of its own; the third-party code that runs is the provider CLI and the pinned wrapper package. The wrapper is pinned to one commit, but the provider CLIs are installed by the operator with no version or integrity check, and the tool image installs them unpinned. The model can also install anything through its unrestricted shell, which no control in the repo limits.

- **S L1:** The wrapper package is pinned to a commit, but the provider CLIs that actually execute are user-installed and unpinned (the image installs them without version pins). — [pentestgpt_agent/pyproject.toml:32](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/pyproject.toml#L32); [Dockerfile:65](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/Dockerfile#L65) (verified)
  - *To reach the next level:* Provider CLIs pinned with integrity checks.
- **C L1:** One extension type (the wrapper package) is pinned; provider CLIs and anything installed at runtime are not. — [Dockerfile:82](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/Dockerfile#L82) (verified)
  - *To reach the next level:* All extension types verified.
- **D L1:** Provider CLIs are explicit operator installs; workspace-file extension loading by the provider was not verified and is treated as possible. — searched `rg -n -S -e 'importlib|entry_points|pickle|trust_remote_code|mcpServers|plugin'` in `pentestgpt_agent/src` → 0 hits (no extension loader in the maintained framework) (verified)
  - *To reach the next level:* Nothing third-party enabled by default and exact commands shown when adding one.
- **B L1:** Provider CLIs run as separate processes under the same user with the operator's environment. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:83](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L83) (verified)
  - *To reach the next level:* Per-extension scrubbed environment or sandbox.
- **Cap:** none

### C8 Secrets & sensitive-data protection — 0.25 (high)

The repo's code never masks, redacts or scrubs anything, and the full-access shell can read the provider login tokens that the Docker helper scripts store and export. Run traces record full prompts, commands and outputs locally, but the files are created owner-only. The README describes default-on analytics, but no telemetry code exists in the maintained packages, so that was not credited or penalized beyond a documentation mismatch.

- **S L1:** Credentials come from the provider CLI login or environment; there is no masking or redaction code in the framework, only owner-only file permissions on traces. — [pentestgpt_agent/src/pentestgpt_agent/trace.py:174](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trace.py#L174); searched `rg -n -S -e 'redact|scrub|mask|SecretStr|sanitiz'` in `pentestgpt_agent/src` → 0 hits (no redaction anywhere in the framework) (verified)
  - *To reach the next level:* Type-level masking and redaction before logs and model-bound messages.
- **C L1:** One path is protected (trace file permissions); logs, model-bound messages and subprocess environments are not. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:282](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L282) (verified)
  - *To reach the next level:* Logs and transcripts redacted as well.
- **D L2:** No telemetry code in the framework; the Claude backend sets non-essential-traffic off; traces are local and owner-only; redaction does not exist to disable. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:63](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L63); searched `rg -n -S -e 'langfuse|telemetry|sentry|posthog'` in `pentestgpt_agent/src unified_agent pentestgpt_legacy` → 0 hits (README telemetry text (README.md) has no code counterpart in these packages) (verified)
  - *To reach the next level:* Content-free opt-in telemetry with always-on redaction.
- **B L0:** Long-lived provider login tokens are stored in a plaintext volume file, exported into the container environment, and reachable by the model's shell. — [scripts/entrypoint.sh:87](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/scripts/entrypoint.sh#L87); [scripts/docker-login.sh:64](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/scripts/docker-login.sh#L64) (verified)
  - *To reach the next level:* Scoped, short-lived, rotatable credentials.
- **Cap:** none

### C9 Audit & traceability — 0.55 (high)

Every provider event is written to an append-only per-episode journal with a flush to disk per event, tagged with run, role, task, attempt and revision identifiers, and a separate SQLite transition log records state changes. A bundled audit tool replays a finished run from these files. The journal sits in the operator's working directory, so a full-access agent could alter or delete it, and there is no approver record because there are no approvals.

- **S L2:** Structured record of tool calls, commands and file changes with arguments, outputs and timestamps, plus ids for role, task and attempt. — [pentestgpt_agent/src/pentestgpt_agent/trace.py:383](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trace.py#L383); [pentestgpt_agent/src/pentestgpt_agent/trace.py:393](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trace.py#L393) (verified)
  - *To reach the next level:* Attribution of principal and approver and tamper-evident storage.
- **C L2:** All provider event types are recorded; raw provider events and reasoning content are omitted, and no approvals or config changes exist to record. — [pentestgpt_agent/src/pentestgpt_agent/trace.py:378](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trace.py#L378) (verified)
  - *To reach the next level:* Approvals, denials and sub-agent events recorded.
- **D L2:** On by default and written by the harness outside the agent workspace, but a full-access agent can modify the files. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:328](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L328); [pentestgpt_agent/src/pentestgpt_agent/trial.py:329](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L329) (verified)
  - *To reach the next level:* Written by a component the model cannot control.
- **B L3:** Each event is appended and fsynced before the next; the journal plus SQLite allows full replay and an audit command exists. — [pentestgpt_agent/src/pentestgpt_agent/trace.py:157](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trace.py#L157); [pentestgpt_agent/src/pentestgpt_agent/audit.py:417](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/audit.py#L417) (verified)
  - *To reach the next level:* Risky actions blocked unless their record is written first.
- **Cap:** none

### C10 Limits & kill switch — 0.30 (high)

The loop is bounded by a count of supervisor decisions (default 20, hard maximum 1000), per-episode turn limits (executor 12, supervisor 4, with per-task-kind budgets) and two attempts per task, all validated in code and unalterable by the model. There is no wall-clock limit, cost cap, rate limit or tool timeout in the framework's own code; command timeouts are only requested in the prompt. Nothing in the repo handles interruption or kills in-flight commands.

- **S L1:** Iteration caps on decisions and turns enforced in code; time and cost limits are absent or advisory (prompt text). — [pentestgpt_agent/src/pentestgpt_agent/trial.py:131](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L131); searched `rg -n -S -e 'timeout|wall_clock|deadline|budget_usd|max_cost|cost_limit|SIGINT|KeyboardInterrupt|killpg'` in `pentestgpt_agent/src` → 3 hits (all three hits are prompt text asking the model to wrap commands with a timeout; none is code) (verified)
  - *To reach the next level:* Add enforced wall-clock or token/cost cap and per-execution timeout.
- **C L1:** The top-level loop and per-episode turn caps are bounded; no tool timeouts or process limits exist. — [pentestgpt_agent/src/pentestgpt_agent/loop.py:53](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/loop.py#L53) (verified)
  - *To reach the next level:* Tool timeouts and spawned-process accounting.
- **D L2:** Sensible defaults set by the operator; the model cannot change them, and values are range-checked with hard maxima. — [pentestgpt_agent/src/pentestgpt_agent/trial.py:52](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L52); [pentestgpt_agent/src/pentestgpt_agent/trial.py:135](https://github.com/GreyDGL/PentestGPT/blob/e8b1bb77d1ac00329675cec3b060aba971ec1ac8/pentestgpt_agent/src/pentestgpt_agent/trial.py#L135) (verified)
  - *To reach the next level:* The model cannot raise its limits and configuration cannot exceed a hard ceiling (this holds for turns and decisions, but no time or cost ceiling exists).
- **B L1:** No wall-clock or spend ceiling, and no code that cancels in-flight commands when a run is stopped. — searched `rg -n -S -e 'cancel|abort|signal'` in `pentestgpt_agent/src` → 0 hits (no cancellation or signal handling in the framework) (verified)
  - *To reach the next level:* Tight per-run time and cost ceilings with cancellation of pending calls.
- **Cap:** none

## Rule-of-Two check
[A] untrusted input: Target responses and files enter model context and are persisted as observations (pentestgpt_agent/src/pentestgpt_agent/agents.py:47) · [B] sensitive data/systems: Provider login tokens and the operator's files are reachable by a full-access shell (pentestgpt_agent/src/pentestgpt_agent/trial.py:73) · [C] state change / egress: Unrestricted shell and network with no approval gate (pentestgpt_agent/src/pentestgpt_agent/trial.py:73) · Same default session? Yes

## Highest-impact improvements
1. Run both roles inside a hardened container or microVM created by the framework (non-root, dropped capabilities, workspace-only mount, egress limited to authorized targets and the model provider). — C4 S L0→L3, +0.225 before caps (Playbook 3)
2. Add a per-call approval gate (or a read-only auto-approve list) for shell and write tools, with the exact command shown. — C2 S L0→L3, +0.225 before caps (Playbook 5)
3. Add enforced wall-clock and cost caps plus a per-command timeout that kills the process group. — C10 S L1→L3, +0.150 before caps (Playbook 3)
4. Give the provider process a scrubbed environment and a dedicated, short-lived credential instead of the operator's long-lived login. — C1 S L0→L2, +0.150 before caps (Playbook 4)
5. Enforce the target scope on commands and network destinations (egress allowlist), not only on the plan's target field. — C3 S L1→L3, +0.150 before caps (Playbook 3)

## Re-audit log
- No changes.

## Limitations
- Static source review at the pinned commit only; nothing was installed, built or run.
- The scored mode is the maintained pentestgpt_agent framework. Its provider layer is an external package (unified-agent, pinned by git revision in pentestgpt_agent/pyproject.toml) that is not in this repo and was not audited; the full-access permission mapping and workspace settings loading were read from the obsolete in-repo copy (unified_agent/) and are marked inferred where relied on.
- The legacy interactive client (pentestgpt_legacy) was only skimmed: it has no command execution path in the files searched, loads .env from the current directory, and honours base-URL overrides; it is not scored.
- README describes a Langfuse telemetry default and a pentestgpt CLI, but no corresponding code was found in the maintained packages; this is a documentation mismatch, not credited.
- No reviewer-directed instructions were found in the repo text examined.
