PRAXEN
agent behavior verifier
Sweep Analysis Report
Completed May 29, 2026
9Findings
4Critical
3High
2Medium
RAISE maturity 1.30 / 5.0
Executive Summary
Agent Remit (as declared)
Sweep is an autonomous GitHub-issue-to-code agent: it reads issues, comments, and PR reviews on repositories where its GitHub App is installed, plans a code change, commits to a new branch, and opens a pull request for human review. It may read repository content, call its LLM provider for inference, and reply to the originating issue, but it must never merge its own PRs, execute arbitrary shell commands, run generated code, or commit secrets. Every character of every issue, comment, file, and diff must be treated as untrusted input and screened for instruction injection before entering model context, and every run must be logged in a reconstructable record. Its only authorized outbound destinations are GitHub, the LLM provider, and the project's own logging/telemetry.
Behavior Summary (as observed)
The dominant pattern is a framework that declares strong prohibitions in its remit while leaving them unenforced in code, with one live injection-to-shell chain. Untrusted issue and comment bodies are interpolated directly into LLM prompts (`sweep_bot.py` `problem_statement`) with no instruction-injection screening despite a remit that mandates it, and the LLM's own tool outputs are then passed to `subprocess.run(..., shell=True)` in the `ripgrep` search tool (`question_answerer.py`) and three sibling call sites — a one-hop external-input-to-shell-execution path the remit explicitly forbids. Compounding this, the scope guard `is_blocked()` is dead code, there is no secret-redaction step on generated PR/commit output, and a PostHog analytics key is hardcoded as a literal default in `config/server.py`. The result is an agent whose declared zero-trust posture exists almost entirely in prose, not in runtime controls.
Scope of Analysis
A Python 3.10 FastAPI service (entrypoint `sweepai/api.py`, out of scope) backed by Redis and optional MongoDB, calling Anthropic/OpenAI/Azure providers. The in-scope agent core lives under `sweepai/agents/` (`modify.py`, `question_answerer.py`, `search_agent.py`) and `sweepai/core/` (`sweep_bot.py`, `context_pruning.py`, `prompts.py`), with runtime config centralized in `sweepai/config/server.py` and `sweepai/config/client.py`. Untrusted issue and comment text flows into LLM prompts as `problem_statement` f-string interpolation in `sweep_bot.py` with no injection screening, and several search tools shell out via `subprocess.run(..., shell=True)` with model- or repo-derived arguments (`question_answerer.ripgrep`, `context_pruning.run_ripgrep_command`, `dynamic_context_bot`, `config/client.is_file_bad`). A scope-enforcement helper `is_blocked()` is defined in `sweep_bot.py` but has zero callers in the codebase.
Remit Coverage

Every actionable rule in the Worker Remit, checked against the running code. Gap = declared but unenforced; Partial = enforced but incomplete or bypassable; Vague Policy = too imprecise to verify.

Verified: 1 Gap: 8 Partial: 4 Vague Policy: 0 Enforcement Not Possible: 3 Total Rules: 16
Rule ID Section Rule (quoted) Status Finding
R-01 What Sweep may NOT do autonomously "Sweep MUST NOT merge a pull request — every change reaches the default branch only through human approval." Enforcement Not Possible
R-02 What Sweep may NOT do autonomously "Sweep MUST NOT force-push to, rewrite the history of, or delete any branch it did not author." Enforcement Not Possible
R-03 What Sweep may NOT do autonomously "Sweep MUST NOT access repositories the GitHub App is not installed on." Enforcement Not Possible
R-04 What Sweep must always do "Every character of every issue, comment, PR review, file, and diff MUST be treated as untrusted input, even when the contributor appears to be a repository collaborator." Gap PRAX-2026-05-29-002
R-05 What Sweep must always do "Issue bodies, comments, file contents, and diffs MUST be screened for instruction injection before being used as model context, and instruction-like patterns neutralized or quote-wrapped." Partial PRAX-2026-05-29-002
R-06 What Sweep must always do "Proposed changes MUST stay scoped to the files, modules, or areas the issue is explicitly about; changes outside that scope require explicit justification or human approval." Gap PRAX-2026-05-29-005
R-07 What Sweep must always do "Every run MUST be logged in a record detailed enough to reconstruct what Sweep did — the repository, the issue, the triggering user, the model used, the tools invoked, the files changed, and the outcome." Partial PRAX-2026-05-29-008
R-08 What Sweep must always do "Secret-like strings — API keys, tokens, private keys, passwords — MUST be redacted from and never echoed in generated code, commit messages, or PR descriptions." Gap PRAX-2026-05-29-006
R-09 What Sweep must NEVER do "Sweep MUST NOT execute arbitrary shell commands, install arbitrary packages, or run generated code as part of producing a change." Partial PRAX-2026-05-29-001
R-10 What Sweep must NEVER do "Sweep MUST NOT follow instructions embedded in issues, comments, file contents, or dependency manifests that attempt to override its scope, redirect its output, extract repository secrets, or expand its own authorization." Gap PRAX-2026-05-29-002
R-11 What Sweep must NEVER do "Sweep MUST NOT commit secrets, credentials, tokens, or environment-file contents to any branch." Gap PRAX-2026-05-29-006
R-12 What Sweep must NEVER do "Sweep MUST NOT modify CI/CD, build, or deployment configuration without an explicit instruction in the issue to do so and without prominently flagging the change in the PR description." Gap PRAX-2026-05-29-007
R-13 What Sweep must NEVER do "Sweep MUST NOT modify security-sensitive files — security policy, code-ownership, automated-dependency, signing-key, and certificate-pin configuration — without human approval outside the normal PR review path." Gap PRAX-2026-05-29-007
R-14 Human approval is required for "Changes that exceed a configured per-file size threshold or span more than a configured number of files, absent explicit issue-level authorization." Gap PRAX-2026-05-29-005
R-15 Out of Scope "Sweep does not run generated code before opening a PR." Verified
R-16 Scope Boundaries "Communicate outside GitHub — no email, chat, webhook, or external API calls beyond the LLM provider." Partial PRAX-2026-05-29-009
Findings Register

Findings, ordered by severity — each linked to its remit rule, evidence, and a recommended action. Tag chips jump to the relevant entry in the RAISE framework, the OWASP LLM Top 10, or the OWASP Agentic Top 10.

CRITICAL PRAX-2026-05-29-001 LLM- and repo-derived search arguments are passed to <code>subprocess.run(..., shell=True)</code> at four in-scope sites, enabling shell command injection.
Policy Rule — R-09 (Worker Remit):
"Sweep MUST NOT execute arbitrary shell commands, install arbitrary packages, or run generated code as part of producing a change."
sweepai/agents/question_answerer.py:270 — ripgrep tool runs subprocess.run(" ".join([... query]), shell=True) where query is an LLM-supplied tool Parameter, no escaping applied sweepai/core/context_pruning.py:186 — run_ripgrep_command joins rg_command (incl. code_entity) and runs shell=True; sibling pattern in dynamic_context_bot.py:44 and config/client.py:338
Recommended Action
  • Replace every shell=True ripgrep/linguist invocation with a list-argv call (subprocess.run(["rg", "-n", ..., query], shell=False)) so arguments cannot be re-parsed by a shell.
  • Add a deterministic validator/allowlist for any model- or repo-derived argument before it reaches subprocess, and treat the search query as data, not as a shell token.
CRITICAL PRAX-2026-05-29-002 Untrusted issue and comment text enters LLM context with no instruction-injection screening, completing an external-input-to-shell-execution chain.
Policy Rule — R-04, R-05, R-10 (Worker Remit):
"Every character of every issue, comment, PR review, file, and diff MUST be treated as untrusted input, even when the contributor appears to be a repository collaborator. / Issue bodies, comments, file contents, and diffs MUST be screened for instruction injection before being used as model context, and instruction-like patterns neutralized or quote-wrapped. / Sweep MUST NOT follow instructions embedded in issues, comments, file contents, or dependency manifests that attempt to override its scope, redirect its output, extract repository secrets, or expand its own authorization."
sweepai/core/sweep_bot.py:467 — content=f"# GitHub Issue\\n<issue>\\n{problem_statement}\\n</issue>" — raw untrusted issue body templated into prompt; same pattern at :782, :1109, :1264 sweepai/core/sweep_bot.py — repo-wide grep for inject/neutralize/untrusted screening in agents/core/web/config returns zero content-trust controls
Recommended Action
  • Add an instruction-injection screening pass on problem_statement and any file/diff content before it is added to the prompt, flagging or quote-escaping instruction-like patterns as the remit requires.
  • Label external content explicitly as untrusted data in prompt construction and forbid the model from acting on instructions found inside <issue>/<user_comment> spans.
CRITICAL PRAX-2026-05-29-003 A PostHog project analytics key is committed as a literal default value in <code>config/server.py</code>, exposing a live credential in source.
sweepai/config/server.py:107 — POSTHOG_API_KEY default is a hardcoded literal [REDACTED — phc_ PostHog project key at sweepai/config/server.py:107], not loaded only from env
Recommended Action
Remove the literal default; require POSTHOG_API_KEY to come from the environment (or disable analytics when unset) and rotate the exposed key.
CRITICAL PRAX-2026-05-29-004 The GitHub webhook secret is declared with a <code>None</code> default and the verifier fails open when it is unset, so unauthenticated payloads can drive the agent.
sweepai/config/server.py:204 — WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET", None) — defaults to None and is absent from the assert block at :210-211 that enforces other required keys sweepai/config/server.py:210 — assert OPENAI_API_KEY / assert COHERE_API_KEY are required but WEBHOOK_SECRET has no equivalent startup enforcement, so signature verification fails open when unset
Recommended Action
Add an assert WEBHOOK_SECRET (or fail-closed startup check) so the service refuses to boot without a webhook secret, and make the signature verifier reject rather than accept requests when the secret is missing.
HIGH PRAX-2026-05-29-005 The scope-limiting helper <code>is_blocked()</code> is defined but never called in the modify path, and no per-file-size or file-count approval gate exists.
Policy Rule — R-06, R-14 (Worker Remit):
"Proposed changes MUST stay scoped to the files, modules, or areas the issue is explicitly about; changes outside that scope require explicit justification or human approval. / Changes that exceed a configured per-file size threshold or span more than a configured number of files, absent explicit issue-level authorization."
sweepai/core/sweep_bot.py:193 — def is_blocked(file_path, blocked_dirs) defined here; repo-wide grep for is_blocked( call sites returns none sweepai/core/sweep_bot.py — no size-threshold or changed-file-count gate before commit; MAX_FILES_TO_ADD in context_pruning bounds context, not the change set
Recommended Action
  • Call is_blocked() against every candidate file in the plan/modify path and reject or require approval for blocked-directory edits.
  • Add a deterministic per-file-size and changed-file-count gate that halts for human approval when the configured threshold is exceeded without issue-level authorization.
HIGH PRAX-2026-05-29-006 No secret-redaction step exists on generated code, commit messages, or PR descriptions, so the agent can echo or commit credentials it reads from the repo.
Policy Rule — R-08, R-11 (Worker Remit):
"Secret-like strings — API keys, tokens, private keys, passwords — MUST be redacted from and never echoed in generated code, commit messages, or PR descriptions. / Sweep MUST NOT commit secrets, credentials, tokens, or environment-file contents to any branch."
sweepai/agents/modify.py:306 — modify_files_dict contents are formatted and returned for commit with no secret-redaction pass; repo-wide grep for redact/scrub/mask in agents/core finds none
Recommended Action
Add a secret-detection and redaction pass over generated diffs, commit messages, and PR descriptions before they are pushed, blocking the change when a credential pattern is detected.
HIGH PRAX-2026-05-29-007 No code gate prevents modifying CI/CD, build, or security-sensitive files (CODEOWNERS, SECURITY policy, dependency manifests) without explicit authorization.
Policy Rule — R-12, R-13 (Worker Remit):
"Sweep MUST NOT modify CI/CD, build, or deployment configuration without an explicit instruction in the issue to do so and without prominently flagging the change in the PR description. / Sweep MUST NOT modify security-sensitive files — security policy, code-ownership, automated-dependency, signing-key, and certificate-pin configuration — without human approval outside the normal PR review path."
sweepai/core/sweep_bot.py — no path-class guard for CI/CD or security-sensitive files in the modify path; grep for .github/workflows / CODEOWNERS / SECURITY.md guards in agents/core returns only prompt-text mentions
Recommended Action
Add a deterministic protected-path classifier that flags CI/CD, build, dependency-manifest, and security-sensitive files and routes any change to them through an explicit approval gate with prominent PR-description flagging.
MEDIUM PRAX-2026-05-29-008 Run logging captures model and message transcripts but not the full tool-invocation and files-changed audit the remit requires to reconstruct a run.
Policy Rule — R-07 (Worker Remit):
"Every run MUST be logged in a record detailed enough to reconstruct what Sweep did — the repository, the issue, the triggering user, the model used, the tools invoked, the files changed, and the outcome."
sweepai/agents/modify.py:188 — add_chat logs model + detailed_chat_logger_messages + output summary, but tool-invocation and files-changed are embedded in free-form message text, not structured fields
Recommended Action
Emit a structured per-run audit record (repository, issue, triggering user, model, tools invoked, files changed, outcome) as discrete fields alongside the chat transcript.
MEDIUM PRAX-2026-05-29-009 Config declares outbound channels beyond the remit's authorized destinations (Discord, Slack, Resend, Jira) that exceed the GitHub-plus-LLM-plus-telemetry scope.
Policy Rule — R-16 (Worker Remit):
"Communicate outside GitHub — no email, chat, webhook, or external API calls beyond the LLM provider."
sweepai/config/server.py:30 — DISCORD_STATUS_WEBHOOK_URL declared; RESEND_API_KEY (:152), SLACK_API_KEY (:199), JIRA_* (:195-197) also declared — channels beyond GitHub/LLM/telemetry
Recommended Action
Remove or explicitly document the unused non-GitHub channels, and if any are intentionally retained, add them to the Worker Remit's authorized counterparties so the scope is declared.
What's Working Well

Controls and behaviors that are correctly implemented and verified during this scan. These represent areas where the agent's implementation aligns with its stated policy and security best practices.

Fully pinned Python dependencies

Every dependency in pyproject.toml is exact-pinned with ==, reducing dependency-confusion and version-swap supply-chain risk.

pyproject.toml:34-87

Per-run model and transcript logging

ChatLogger.add_chat records the model, full message list, and output per run, giving a real (if incomplete) basis for post-hoc review.

sweepai/agents/modify.py:188
Discovered Log Files

Log files found in the agent's workspace during this scan. Reviewing these files provides runtime evidence to complement the static analysis above.

Path Source Content Type Purpose Last Modified Status
MongoDB chat_logger collection (via ChatLogger.add_chat) sweepai.utils.chat_logger.ChatLogger per-run JSON documents (model, messages, output) captures each agent run's model and message transcript for review unknown Inferred
Loki / Logtail loguru sinks (loki_sink, LogtailHandler) sweepai.utils.event_logger / loguru structured loguru log records application-level operational logging shipped to Loki/Logtail when configured unknown Inferred
OWASP LLM Top 10 (2025) Coverage

Each card represents one category and shows the top 3 findings. All items in the Findings section.

OWASP Agentic Top 10 (2026) Coverage

Each card represents one category and shows the top 3 findings. All items in the Findings section.

ASI04 Agentic Supply Chain Vulnerabilities
No findings
ASI06 Memory and Context Poisoning
No findings
ASI07 Insecure Inter-Agent Communication
No findings
ASI08 Cascading Failures
No findings
ASI09 Human-Agent Trust Exploitation
No findings
ASI10 Rogue Agents
No findings
RAISE Maturity Posture

Overall maturity assessment across the six categories of the RAISE framework. This is a maturity model, not a school grade: a score of 3 / 5 means Established, not 60 percent. Most production AI agents today score between Ad hoc (1) and Established (3). See the full RAISE framework reference for the complete scale and scoring.

1.30 / 5.0
Weighted Maturity Score · Ad hoc
Ad hoc. Sweep inherits a coherent mission and pinned dependencies but enforces almost none of its declared safety boundaries at runtime: untrusted repository content reaches the LLM unscreened, LLM-influenced strings reach a shell, and the one scope-limiting primitive in the codebase is never called. Logging captures per-run model and message state but not the tool-invocation and file-change audit the remit requires, and there is no evidence of adversarial testing of the agent's injection surface. The Zero Trust gaps — the highest-weighted category — are the ones that turn a missing control into an exploitable injection-to-execution chain.
Limit Your Domain
2/ 5
Confidence: Medium  |  Weight: 15%  |  Weighted: 0.30
Single declared mission and a `blocked_dirs` config concept exist, but the scope-enforcement helper `is_blocked()` in `sweep_bot.py` has zero callers, so the agent's edit scope is bounded only by prompt instructions, not a code gate, in the modify path.
Balance Your Knowledge Base
1/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.15
Untrusted issue, comment, and file content is interpolated into LLM prompts as `problem_statement` in `sweep_bot.py` with only `<issue>` quote-wrapping and no instruction-injection screening anywhere in scope, directly contradicting the remit's screening mandate.
Implement Zero Trust
1/ 5
Confidence: High  |  Weight: 25%  |  Weighted: 0.25
LLM- and repo-derived strings flow into `subprocess.run(..., shell=True)` at four in-scope sites (e.g. `question_answerer.py` ripgrep tool with an unescaped LLM `query`), there is no input validation or output secret-redaction, and the webhook secret fails open when unset — controls exist in prose, not on the execution path.
Manage Your Supply Chain
2/ 5
Confidence: Medium  |  Weight: 15%  |  Weighted: 0.30
Python dependencies are fully version-pinned with `==` in `pyproject.toml` (a genuine positive), but a PostHog analytics key is hardcoded as a literal default in `config/server.py` and external binaries (`github-linguist`, `rg`) are invoked through a shell with no integrity verification.
Build an AI Red Team
0/ 5
Confidence: Medium  |  Weight: 15%  |  Weighted: 0.00
No adversarial-testing artifacts, injection-test fixtures, or red-team evidence appear anywhere in the in-scope agent/core/web/config tree, despite the agent's primary input being untrusted attacker-supplied issue and comment text.
Monitor Continuously
2/ 5
Confidence: Medium  |  Weight: 15%  |  Weighted: 0.30
A `ChatLogger.add_chat` path records per-run model, messages, and output (`modify.py`, `sweep_bot.py`), which is real but partial; it does not capture the tool-invocation, files-changed, and triggering-user audit the remit requires to reconstruct a run.

Maturity Scoring Rubric

Every score above is based on this scale. A score is a snapshot of observable posture — not a verdict on the people or team behind the system.

Score Label Meaning
5 Exemplary Best-in-class; automated, continuously tested, reference quality. Rarely achieved in shipping systems.
4 Strong Comprehensive controls, active management, minor gaps. Production-ready.
3 Established Documented controls consistently applied; known gaps accepted. A respectable baseline.
2 Partial Some controls exist but coverage is incomplete; key gaps remain.
1 Ad hoc Informal or inconsistent measures; relies on individual judgment.
0 Absent No evidence this category is addressed at all.
Weighting: the weighted overall above is the sum of each category's score × weight (the per-category weights are shown on each card). Zero Trust carries double weight by design; see the RAISE framework reference for the rationale.