How to use

Each prompt below opens with a search instruction so the assistant scans the whole repository rather than a list you have to compile by hand — see Tip 1 for why this beats pinning files on anything but a toy project. Run prompts one at a time, in a fresh chat session per prompt — mixing them in one long thread is the single most common reason the assistant’s answers degrade. Capture each verdict; Tip 4 walks through the fix-list format and the SELF-REVIEW.md file you record findings in.

In a hurry? Jump to the all-in-one combined sweep — all twenty-five checks in one paste, with a generated report.

The common response shape

Every prompt below ends with the same instruction block. It puts the evidence first and the judgement second, so the verdict and the severity follow mechanically from what was found — not from the model’s memory. Reading the response is much faster when it is always laid out the same way:

First, show your work in this order:
A. SEARCH — list the exact search terms and globs you used and the
   files you opened. Ignore generated, vendored, and dependency folders.
B. EVIDENCE — enumerate every match as file path + line number +
   a one-line quote. The verdict below must follow from this list.

Then respond with exactly these sections:
1. VERDICT: one of [present / not present / unclear / not applicable]
   present        = at least one cited artefact matches the failure mode
   not present    = the surface was searched and the safe pattern (or its
                    absence) is shown
   not applicable = the precondition does not exist (name it)
   unclear        = the surface exists but the evidence is insufficient
                    (name the one fact you are missing)
2. SEVERITY: one of [Critical / High / Medium / Low / N/A], computed as
   Likelihood x Impact using the rubric in "How verdicts and severity
   are decided" below. State the Impact baseline, the Likelihood level,
   and the matrix cell you landed on. Use N/A for not present / not
   applicable.
3. WHY IT MATTERS: two sentences, plain English.
4. FIX: a concrete change, with a short before/after code snippet
   if applicable. If "unclear", list the one piece of context you
   need to decide.
5. AUDIT BLOCK: a fenced json object with this exact schema, so two
   runs on the same commit are diff-able:
   {"id":"<check-slug>","verdict":"...","impact":"...",
    "likelihood":"...","severity":"...",
    "evidence":[{"file":"...","line":0,"quote":"..."}],
    "search_terms":["..."]}

Each prompt below assumes you append that block. To keep the page readable it is shown once, then referenced as [response shape]. The AUDIT BLOCK is the per-finding record you paste into SELF-REVIEW.md — the append-only log Tip 4 defines, where each finding later gets its re-run verdict.

How verdicts and severity are decided

The value of this pack for an audit is that the verdict and the severity are not the model’s opinion — they are derived, by a fixed rule, from evidence the model has to cite. Two reviewers (or two runs) looking at the same code and the same evidence should reach the same verdict and the same severity. This section is that rule, so anyone can re-derive a result without trusting the model.

How the verdict is decided

The verdict is gated on cited evidence: a claim with no file:line quote behind it is not a finding.

VerdictDecision ruleAudit trail it leaves
presentAt least one artefact in the code or config matches the failure-mode definition.The cited file path + line + one-line quote.
not presentThe relevant surface was searched and the safe pattern (or a clear absence) is shown.The search terms used — so the absence is verifiable, not assumed.
not applicableThe precondition for the failure mode does not exist in this codebase.The named missing precondition (e.g. “no CI files”, “no end-user auth”).
unclearThe surface exists but the evidence is insufficient to decide.The single named fact a reviewer needs to resolve it.

How severity is calculated

Severity is Likelihood × Impact. Neither axis is a free-form judgement:

Impact baselines. High = can lead to code execution, credential or data compromise, or supply-chain control. Medium = data exposure, or a governance / integrity gap. (Low severity is not a baseline; it only emerges from the matrix when Likelihood is Low.)

Impact baselineAnti-patterns
HighWildcard tool exposure; unauthenticated tool channel; conflated context; comment-to-commit promotion; live-fetch dependency; standing credential; mutable reference trust; unsupervised perimeter; shared identity runtime; poisoned knowledge base; persistent memory poisoning; unsanitised output sink; implicit agent-to-agent trust; system prompt as secret store; boundaryless data egress
MediumPhishable flow; plaintext journal; documented defence that doesn’t exist; internal-to-product gap; porous vector store; hallucinated authority; unbounded consumption; unattributable action; rubber-stamp approval; ungated model swap

Likelihood (scored per run).

LikelihoodWhen
HighAn unsafe path is reachable by untrusted input (internet-facing, or third-party / user-supplied content) and no mitigating control is present.
MediumExploitation needs an authenticated or internal actor, or a partial / incomplete control exists.
LowExploitation needs privileged local access or several unlikely preconditions, or a strong compensating control is already in place.

The matrix. Severity is the cell where the two axes meet:

Likelihood ↓ \ Impact →LowMediumHigh
HighMediumHighCritical
MediumLowMediumHigh
LowLowLowMedium

Why a run is auditable

Three properties let a reviewer who was not in the room reconstruct any result:

The honest limit — and how to pin a run down

AI assistant output is probabilistic: byte-identical text is not guaranteed across runs, even at temperature zero, because hosted models change under you and tool-call order varies. So “consistent” here has a precise meaning — same model build + same commit + same prompt → the same rule-derived verdict and severity, with any difference surfaced by diffing the audit blocks rather than hidden in prose. To get there:

  1. Pin the model name and version, set temperature to its lowest setting, and run one prompt per fresh session (see Tip 1) — mixing checks in one thread is the main reason a combined run and an individual run disagree.
  2. Record the run (model, version, temperature, date, commit SHA) next to each finding — Tip 4 has the format.
  3. For the mechanically-detectable checks, back the AI pass with a deterministic static check, so those verdicts are 100% reproducible:
CheckDeterministic rule you can also run
Live-fetch dependency; mutable reference trustgrep / semgrep for :latest, @main, unpinned actions/*@v*, and curl | sh; a CI lint that requires SHA-pinned actions.
Wildcard tool exposuresemgrep for shell=True, unrestricted exec, and HTTP clients with no host allow-list.
Standing credential; system prompt as secret storea secret scanner (gitleaks / trufflehog) over source and prompt templates.
Plaintext journala lint that flags logging of raw prompt / tool-argument variables with no redaction wrapper.

The AI pass owns the judgement-heavy checks; the static rules own the mechanical ones. Where both cover a check, the static rule is the tie-breaker of record — it cannot hallucinate.

The twenty-five prompts

Reminder: AI assistant output is probabilistic. Verify every claim against your source before acting on it. False positives and false negatives are expected.

Wildcard tool exposure

Prompt 1 — Is the tool catalogue a wildcard?

Looks for the agent being granted an open-ended set of capabilities (shell, arbitrary HTTP, broad file access) when a named verb list would do.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the AI agent in this
codebase is granted tool access that is wildcard or near-wildcard
— for example, an unrestricted shell, an HTTP client with no
domain allow-list, file system access with no path restriction, or
an MCP / plugin client that loads every tool advertised by the
server.

Tell me whether this codebase exhibits that pattern.

[response shape]
Unauthenticated tool channel

Prompt 2 — Are remote tools authenticated and trusted?

Catches connectors / plugins / MCP servers loaded over plain HTTP, without auth, or from sources that are not on a controlled list.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the AI agent connects
to remote tool servers (HTTP endpoints, MCP servers, plugins,
function-call backends) without authentication, without TLS, or from
URLs that are user-controlled / configuration-controlled with no
allow-list of trusted hosts.

[response shape]
Conflated context (prompt injection)

Prompt 3 — Is untrusted content mixed with instructions?

The structural prompt-injection problem: content fetched from the web, a database, or a tool result is concatenated into the same context as the system prompt with no separator and no sanitisation.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: untrusted text
(web page contents, tool results, user-supplied documents, RAG
retrievals) is placed into the model’s context window without
clear separation from trusted instructions, without sanitisation,
and without any downstream check that prevents the model from
following directives embedded in that text.

[response shape]
Comment-to-commit promotion

Prompt 4 — Can a comment trigger a write action?

Relevant if your tool integrates with code review, issue trackers, or chat. Looks for the pattern where any commenter implicitly becomes a committer because the agent acts on their words.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: a comment, issue,
chat message, or other text written by anyone with read access can
cause the agent to perform a write action (commit code, merge a
pull request, deploy, modify a ticket, send a message) without an
additional authorisation step that checks the actor’s
permission to perform that specific write.

If the codebase does not integrate with any such surface, say
"not applicable" and stop.

[response shape]
Live-fetch dependency

Prompt 5 — Are dependencies pinned or fetched at runtime?

Floating tags (@latest, :main), curl | sh, runtime pip install / npm install — anything that resolves an external artefact at run-time instead of at build-time.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the agent (or its
install / startup script) fetches code or container images using
floating references — for example ":latest", "@main",
unpinned package installs at runtime, or "curl ... | sh"
patterns. A pinned version, a lock file, or a content hash counts
as not-present.

[response shape]
Standing credential

Prompt 6 — Does the agent hold long-lived broad credentials?

Looks for static API keys, long-lived OAuth tokens, or service-account secrets baked into the agent’s environment instead of minted just-in-time from a workload identity.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the agent process
holds a credential that is long-lived (no automatic rotation), broad
in scope, and continuously available in memory or on disk —
for example a static API key, a personal access token, or a
service-account secret read from environment variables at startup
and never refreshed.

A short-lived token minted just-in-time per call from a workload
identity counts as not-present.

[response shape]
Phishable flow

Prompt 7 — Is the login flow appropriate for the device?

The specific case of OAuth device-code flow running on a device that has a perfectly good browser — turning a fallback flow into the attacker’s preferred channel.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the agent uses an
authentication flow that is meaningfully more phishable than the
device can support — most commonly OAuth device-code flow
running on a machine that has a browser available, where the
authorisation-code-with-PKCE flow would have worked.

If the codebase does not perform end-user authentication at all,
say "not applicable".

[response shape]
Plaintext journal

Prompt 8 — Does telemetry leak sensitive content?

Logs / traces that record prompts, tool arguments, retrieved documents, or model outputs verbatim — turning observability into a pre-staged data leak.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the agent writes
telemetry (logs, traces, metrics, spans) that contains raw model
prompts, raw tool arguments, retrieved document contents, or raw
model output, without redaction of personal data, credentials, or
business-sensitive content, and without an access boundary that
matches the sensitivity of the original data.

[response shape]
Mutable reference trust

Prompt 9 — Are CI / pipeline references pinned by hash?

CI actions / workflows / orchestration steps referenced by tag (@v4) instead of by commit hash. The reference can be repointed after you reviewed it.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: any CI workflow,
pipeline definition, build script, or orchestration manifest in
this codebase references third-party actions, images, or modules
by a mutable name (a tag, a branch, "latest") instead of by a
content-addressable hash (commit SHA, image digest).

If the codebase has no CI / pipeline files, say "not applicable".

[response shape]
Unsupervised perimeter

Prompt 10 — Are all shipped agents governed, or only the headline ones?

Catches the gap where one curated agent has policies, evals, and rate limits, but the same product ships ten other invocable agents or sub-agents with none of that.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the codebase
defines or registers multiple agents, sub-agents, or tool-bearing
flows, but the security and governance controls (rate limits, tool
allow-lists, evaluation hooks, logging, content filters) are
applied to only a subset of them.

Enumerate every distinct agent or sub-agent you can find across the
repository and state for each whether the controls applied to
the main one also apply to it.

[response shape]
Shared identity runtime

Prompt 11 — Does the agent run with the operator’s privileges?

The agent executes inside the same process / shell / identity as the human operator, so “anything the operator can do, the agent can do” — sandbox missing.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the agent executes
in the same security context as the operator who launched it (same
OS user, same cloud identity, same file system permissions, same
process), rather than in a sandbox / separate identity with a
narrower set of permissions.

[response shape]
Documented defence that doesn’t exist

Prompt 12 — Do the docs match the code?

README / docs claim a control (“all tool calls are sandboxed”, “prompts are redacted before logging”) that the code does not actually implement.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Read every source file
the failure mode could touch, and also read README.md and any docs/
file that mentions security, sandboxing, isolation, logging,
redaction, authentication, or permissions. List the search terms you
used so I can confirm nothing was missed.

You are looking for one specific failure mode: a security-relevant
claim in the documentation is not backed by an implementation in
the source.

For each claim, quote the doc sentence verbatim, then either point
to the file/lines that implement it or state "no implementation
found".

[response shape]
Internal-to-product gap

Prompt 13 — Is this tool ready to be shipped to others?

A tool built for internal use takes on new obligations the moment it is shipped — multi-tenant isolation, abuse handling, supportable error messages, a security contact. Catches the gap before launch day.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Read the source plus
README.md, LICENSE, SECURITY.md, and any docs/ file. List the search
terms you used so I can confirm nothing was missed.

Assume this codebase is about to be published for use by people
other than its authors. Identify obligations that apply to shipped
software but not to internal tools, and state whether each is met:
- multi-tenant isolation (no cross-tenant data leakage)
- abuse-handling path (rate limits, reporting channel)
- user-facing error messages (no internal stack traces / secrets)
- a security contact and disclosure policy
- a clear statement of what data the tool sends where

[response shape]

Candidate checks (14–25). The twelve prompts below target the candidate anti-patterns under review — gaps mapped against the OWASP LLM Top 10, the OWASP Agentic taxonomy, NIST AI RMF, MITRE ATLAS, and the compliance regimes. Run them the same way: one at a time, fresh chat per prompt, append the [response shape].

Poisoned knowledge base

Prompt 14 — Is the RAG / training corpus trusted blindly?

Documents the agent retrieves or is fine-tuned on are an injection surface; a poisoned corpus steers behaviour long after ingestion with no runtime prompt to inspect.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: documents ingested
into a retrieval-augmented-generation index or a fine-tuning /
training corpus are treated as trusted — there is no provenance
check, no integrity validation, and no sanitisation of content that
could carry embedded instructions, before that content can influence
the model’s behaviour.

If the codebase has no RAG corpus or training data, say
"not applicable".

[response shape]
Persistent memory poisoning

Prompt 15 — Is cross-session memory written without validation?

An agent that persists “learnings” across sessions can be taught a malicious instruction once and replay it indefinitely. Memory is state, and unvalidated state is an attack surface.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the agent persists
memory, notes, or “learnings” across sessions, and content can be
written into that store from untrusted input (user messages, tool
results, retrieved documents) without validation, attribution, or a
scope boundary — so a malicious instruction written once is
replayed in later sessions.

If the agent keeps no cross-session memory, say "not applicable".

[response shape]
Porous vector store

Prompt 16 — Does retrieval enforce per-tenant isolation?

A shared vector index that ignores per-user access control leaks one tenant’s documents into another’s retrieval results; embeddings can also be inverted back toward source text.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: a vector store or
retrieval index is queried without enforcing the caller’s access
rights — there is no per-tenant or per-user filter on the
similarity search, so one user’s query can return another’s
documents, and the embeddings are not protected against inversion.

If the codebase has no vector store or retrieval index, say
"not applicable".

[response shape]
Unsanitised output sink

Prompt 17 — Is model output trusted as code, SQL, or markup?

Passing raw model output into a shell, a query, or a browser DOM turns the model into an injection vector for the classic web vulnerabilities. Output is untrusted input to the next system.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: raw model output is
passed into a downstream interpreter or sink — a shell command,
a SQL query, an HTML / DOM context, an eval, a file path, or a
generated API call — without encoding, parameterisation, or
validation appropriate to that sink, so the model can emit an
injection payload.

[response shape]
Hallucinated authority

Prompt 18 — Are confident agent assertions acted on unverified?

Acting on a confident but fabricated answer — a non-existent API, a wrong policy, a made-up citation — because the interface presents it with no uncertainty signal or verification step.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the system takes a
consequential action, or presents a fact to a user as authoritative,
based purely on the model’s assertion — with no verification
against a source of truth, no uncertainty signal, and no
ground-truth check — so a confident hallucination (a
non-existent API, a wrong policy, a fabricated citation) is acted on.

[response shape]
Implicit agent-to-agent trust

Prompt 19 — Do sub-agents trust each other without checks?

In a multi-agent system, one agent accepting another’s output or tool calls without authentication or scope checks lets a single compromised agent pivot through the whole orchestration.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: in a multi-agent or
sub-agent system, one agent accepts another agent’s output,
instructions, or tool calls as trusted — without authenticating
the source, validating the message, or constraining it to that
agent’s permitted scope — so a single compromised agent can
pivot through the orchestration.

If the codebase has only a single agent, say "not applicable".

[response shape]
System prompt as secret store

Prompt 20 — Are secrets or policy hidden in the prompt?

Embedding API keys, connection strings, or access rules in the system prompt assumes the prompt is confidential. It is extractable, so anything in it is effectively published.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the system prompt,
developer prompt, or any instruction text sent to the model contains
secrets (API keys, connection strings, passwords, tokens) or
security-relevant access rules that are relied on for enforcement
— treating the prompt as confidential when it is extractable.

[response shape]
Unbounded consumption

Prompt 21 — Is there a token, cost, or loop ceiling?

An agent with no token budget, recursion limit, or request cap can be driven into runaway loops or adversarial cost amplification — a denial-of-service that bills you instead of crashing.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the agent has no
ceiling on consumption — no maximum token budget per request,
no recursion / tool-call-loop limit, no per-user rate or spend cap
— so it can be driven into a runaway loop or adversarial cost
amplification (denial of wallet).

[response shape]
Unattributable action

Prompt 22 — Can every action be traced to who and why?

When an agent acts under a shared identity with no per-decision log, you cannot answer “who did this and why” after an incident — failing the non-repudiation controls auditors expect.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: actions the agent
takes (tool calls, writes, approvals, external requests) are not
recorded in a tamper-resistant audit trail that links each action to
the initiating user / session, the agent decision, and a timestamp
— so after an incident you cannot answer “who did this, and
why”.

[response shape]
Rubber-stamp approval

Prompt 23 — Does the human-approval gate cause fatigue?

A human-approval gate that fires on every routine action trains the reviewer to click “approve” without reading. The control exists on paper but provides no real oversight.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: a human-in-the-loop
approval gate fires on every action regardless of risk, presents too
little context to make a real decision, or has no consequence for
bulk-approving — so the reviewer is trained to rubber-stamp and
the control provides no real oversight.

If there is no human-approval gate at all, say "not applicable".

[response shape]
Ungated model swap

Prompt 24 — Can the model or prompt change with no eval gate?

Switching the underlying model or editing the system prompt without a regression-eval gate ships silent behaviour and safety changes straight to production, outside change management.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the model version,
system prompt, or generation parameters can be changed and reach
production without passing an automated evaluation gate — no
representative regression suite, no comparison of new versus previous
behaviour, and no gradual rollout or quick revert.

If the system has no configurable model / prompt, say
"not applicable".

[response shape]
Boundaryless data egress

Prompt 25 — Is data sent across boundaries without control?

Routing user data to a model or third party in another jurisdiction or tenant, with no classification, residency rule, or egress allow-list, breaches privacy and sovereignty obligations.

Search the whole repository and Understand repo in depth to find where this applies — do not
wait for me to list files. Ignore generated, vendored, and dependency
folders (build output, node_modules, vendor). Identify every location
the failure mode below could occur, read those files in full before
you judge, and list the search terms you used so I can confirm nothing
was missed.

You are looking for one specific failure mode: the agent has broad
read access and broad outbound reach (model endpoints, external
tools, write destinations) with no control that decides, per piece
of data, whether it may cross a jurisdictional, contractual,
purpose, or tenant boundary — no data classification / residency
tagging, no egress policy at the tool boundary, and unlogged egress.

If the agent never handles regulated / multi-tenant data or has no
external egress, say "not applicable".

[response shape]

How long this takes

End-to-end, expect 30–45 minutes for a small tool, longer for a larger one — most of which is reading the answers, not running the prompts. Several of the twenty-five will return “not applicable” for any given tool; that is fine and is the point of having a checklist.

Failure modes & triage

SymptomLikely causeFix
Every prompt returns “not present”Scope is wrong (assistant is reading docs, not code) or the response shape is being ignored.Re-do Tip 1. Confirm with the sanity-check prompt at the bottom of Tip 1.
Verdict is “unclear” on most promptsFiles listed are not the ones that contain the relevant logic.Ask the assistant which file would contain <the topic>, then re-run with that file added.
Findings are confidently wrongHallucination; the assistant did not actually read the file.Switch from chat to inline-edit / file-attach mode that forces a read. Always check that quoted lines actually appear in the file.
Same prompt gives different answers on re-runModel non-determinism; longer answers are more variable.Lower temperature if your assistant exposes it; otherwise run a prompt three times and take the intersection of findings.

Run all twenty-five as one prompt

The twenty-five prompts above are best run one at a time in a fresh chat per prompt — that keeps each answer sharp. But if you want a single paste that walks the assistant through every check in one pass (useful for a quick first sweep, or for assistants that hold context well), the combined prompt below chains all twenty-five, expands the [response shape] once at the top, and finishes by writing a professional Markdown and HTML report — with scalable SVG diagrams (no overlapping or overflowing text, a click-to-expand full-page view, and subtle motion where it aids understanding), accessibility-compliant and Fluent 2 themed — into your chosen output folder (set the [report folder] placeholder; it defaults to a Self-Testing-Report/ folder at the repository root). Paste it whole.

Treat the combined run as fast triage, not the audit of record. Spreading one paste across twenty-five checks gives each check less of the model’s attention, so it is more prone to missed evidence and merged answers than the individual prompts — it will not match a per-prompt run check-for-check, and it is not meant to. Use it to surface candidates quickly; before you rely on any present verdict or its severity, re-run that single check on its own in a fresh session (Tip 1) and keep the individual run, with its audit block and run record, as the result of record. The generated report is the snapshot; promote each confirmed finding into SELF-REVIEW.md (Tip 4) so it carries a re-run verdict over time. The report’s AUDIT BLOCKS appendix lets you diff one combined run against the next, but the per-prompt run remains the audit of record for any verdict you rely on.

All-in-one

The combined sweep — twenty-five checks, one paste

Runs every failure-mode check in sequence in a single session, asks for a check-ordered summary, then generates a Microsoft-standard Markdown and HTML report into a folder you choose (the [report folder] placeholder, defaulting to Self-Testing-Report/ at the repository root). The report uses scalable SVG diagrams with no text/diagram overlap and no overflow, a click-to-open full-page view for each diagram, gentle animation where it helps (honouring reduced-motion), light/dark Fluent 2 theming, and WCAG 2.1 AA accessibility. Slower and more variable than running prompts individually, but zero copy-paste juggling and you finish with a shareable artefact.

# Security self-assessment — deep, evidence-first, interactive report (v5)

You are performing a security self-assessment of this entire codebase. Work
through all 25 checks below in order, in one session, and do not stop until
every check has a verdict, a severity, and both report files are written.

> **What changed from v4.** The verdict `mitigated` is gone. Verdicts are now
> exactly four: **present / not present / unclear / not applicable**. Residual
> risk from a partial, probabilistic, opt-in, or silently-degrading control is
> no longer a separate verdict — it is expressed through **Likelihood**, which
> lowers the **Severity** while the verdict stays **present**. Severity is a
> rule-derived lookup: **Severity = Likelihood × Impact**, where Impact is a
> fixed per-check baseline (High or Medium only) and Likelihood is scored per
> run on a three-level scale. The severity badge set is now Critical / High /
> Medium / Low / **N/A** (Info is removed). Every response is **evidence-first**:
> show your SEARCH and EVIDENCE before the verdict, so the verdict and severity
> follow mechanically from what was found, not from the model's memory.

---

## Codebase version pinning

At the very start of your investigation, run `git rev-parse HEAD` and record
the commit hash. Include it in the report metadata table as "Commit". All
evidence must come from files at THAT commit. If you observe code that
contradicts a prior report, note the discrepancy explicitly — do not assume
the prior report was wrong; the code may have changed.

---

## Verdict classification rubric — BINDING

The verdict is **gated on cited evidence**: a claim with no `file:line` quote
behind it is not a finding. Use EXACTLY these four verdicts. No other
interpretation is valid:

- **present** — At least one cited artefact in the code or config matches the
  failure-mode definition. The unsafe path exists, even if a partial control
  sits in front of it. *Audit trail it leaves:* the cited file path + line +
  one-line quote.
- **not present** — The relevant surface was searched and the safe pattern (or
  a clear, verified absence) is shown. A deterministic, default-on,
  non-bypassable control in the running path earns this. *Audit trail:* the
  search terms used, so the absence is verifiable, not assumed.
- **not applicable** — The precondition for the failure mode does not exist in
  this codebase (no RAG, no vector store, no CI files, no multi-agent, no
  end-user auth). *Audit trail:* the named missing precondition.
- **unclear** — The surface exists but the evidence is insufficient to decide.
  *Audit trail:* the single named fact a reviewer needs to resolve it. Never
  use "unclear" as a hedge — you must name the one missing fact.

**A partial control does not clear a finding — it lowers Likelihood.** This is
the single most important change from v4. When a defence exists but residual
risk remains (it is probabilistic/advisory, opt-in/off-by-default, an empty
allow-list that accepts everything, or it silently degrades to a no-op when an
external resource is missing), the verdict is **present** and you score
**Likelihood = Medium** (a partial / incomplete control is in place). You never
record such a control as "not present", and there is no longer a "mitigated"
verdict to fall back on.

**Decision rules (apply in order):**

| Situation | Verdict | Likelihood signal |
|-----------|---------|-------------------|
| Code path removed entirely (import deleted, raises error) | not present | — |
| Deterministic control, default-on, non-bypassable, in the running path | not present | — |
| Unsafe path reachable by untrusted input, no control | present | High |
| Control exists but is probabilistic / advisory (flags, does not block) | present | Medium |
| Control exists but is opt-in (off by default) | present | Medium |
| Control degrades silently without an external resource | present | Medium |
| Allow-list exists but defaults to empty (accepts everything) | present | Medium |
| Exploitation needs an authenticated / internal actor | present | Medium |
| Strong compensating control already in place, or privileged local access needed | present | Low |
| Surface exists but one fact is missing to decide | unclear | — |
| Technology / surface does not exist in codebase | not applicable | — |

When in doubt between two verdicts, state the ambiguity in "Why this verdict,
here" and, if you cannot name the single missing fact, choose the verdict that
keeps the finding visible (prefer **present** over **not present**). When in
doubt between two Likelihood levels, choose the HIGHER one and explain why. A
run that disagrees with its own rubric definitions is a failed run.

---

## Severity rubric — BINDING

**Severity = Likelihood × Impact.** Neither axis is a free-form judgement.

**Impact is a fixed baseline per check** — not a per-run guess. It reflects the
consequence the anti-pattern's framework mapping (OWASP LLM Top 10, OWASP
Agentic, NIST AI RMF, MITRE ATLAS) attaches to it. Baselines are **High or
Medium only** (Low severity is never a baseline — it only emerges from the
matrix when Likelihood is Low):

- **High** = can lead to code execution, credential or data compromise, or
  supply-chain control.
- **Medium** = data exposure, or a governance / integrity gap.

| Impact baseline | Checks |
|-----------------|--------|
| **High** | 1 Wildcard tool exposure · 2 Unauthenticated tool channel · 3 Conflated context · 4 Comment-to-commit promotion · 5 Live-fetch dependency · 6 Standing credential · 9 Mutable reference trust · 10 Unsupervised perimeter · 11 Shared identity runtime · 14 Poisoned knowledge base · 15 Persistent memory poisoning · 17 Unsanitised output sink · 19 Implicit agent-to-agent trust · 20 System prompt as secret store · 25 Boundaryless data egress |
| **Medium** | 7 Phishable flow · 8 Plaintext journal · 12 Documented defence that doesn't exist · 13 Internal-to-product gap · 16 Porous vector store · 18 Hallucinated authority · 21 Unbounded consumption · 22 Unattributable action · 23 Rubber-stamp approval · 24 Ungated model swap |

That is 15 High + 10 Medium. Do not re-rate the Impact baseline per run; it is
fixed. Only Likelihood moves.

**Likelihood is scored per run** from observable signals, on a fixed
three-level scale:

| Likelihood | When |
|------------|------|
| **High** | An unsafe path is reachable by untrusted input (internet-facing, or third-party / user-supplied content) and **no mitigating control is present**. |
| **Medium** | Exploitation needs an authenticated or internal actor, **or a partial / incomplete control exists** (probabilistic, opt-in, empty-default allow-list, silently-degrading). |
| **Low** | Exploitation needs privileged local access or several unlikely preconditions, **or a strong compensating control is already in place**. |

**The matrix.** Severity is the cell where the two axes meet:

| Likelihood ↓ \ Impact → | Low | Medium | High |
|-------------------------|-----|--------|------|
| **High** | Medium | High | **Critical** |
| **Medium** | Low | Medium | High |
| **Low** | Low | Low | Medium |

(Impact "Low" never appears as a baseline above; it is shown only to complete
the published matrix.)

**N/A severity.** A verdict of **not present** or **not applicable** carries no
exploitable risk, so its severity is **N/A** — do not compute a matrix cell for
it. Only **present** findings get a Critical / High / Medium / Low severity.
For **unclear**, report severity as **N/A** and state that severity cannot be
fixed until the one missing fact is resolved.

**Severity is a lookup, never a vibe.** The answer to "why is this High?" must
always be the two inputs and the matrix cell — e.g. "Impact High (baseline) ×
Likelihood Medium (partial control) → High". State all three every time.

---

## Threat model scope — BINDING

For EVERY check, evaluate TWO attack scenarios:

1. **Prompt injection / model manipulation** — the model is tricked into
   misusing its available capabilities within normal operation.
2. **Container / process compromise** — the process is compromised (RCE via a
   dependency vulnerability, container escape, or supply-chain attack). What
   identity, credentials, and permissions does the attacker inherit from the
   hosting process?

Both scenarios contribute to the verdict and the Likelihood. If the narrow
operation path is secure but the hosting identity is over-privileged, that is a
finding (the process-compromise scenario yields risk even if normal operation
is safe). State which scenario drives the verdict and the Likelihood score.

---

## Evidence verification — environment variable defaults

When a check depends on an environment variable's default value:

1. Find the call that reads the variable (e.g. `os.getenv()`, `process.env.`,
   `Environment.GetEnvironmentVariable()`).
2. Quote the EXACT default / fallback value. Do not paraphrase.
3. Trace whether anything flips that default before first use (a boot-time
   override, a config-file load, a deploy-script injection).
4. If the default means the control is OFF unless the operator acts, the
   verdict is **present** with **Likelihood = Medium** (opt-in / partial
   control) — never "not present".
5. If the control requires an external resource to be provisioned (a storage
   account, a database, a Redis instance) and silently degrades to a no-op
   without it, that is residual risk → verdict **present**, **Likelihood =
   Medium**.
6. Only a default that is ON, deterministic, non-bypassable, and verified in
   the running path supports **not present**.

Example of what to cite (use the actual variable names from the codebase):
```
os.getenv("FEATURE_AUDIT_LOG", "on")   → default "on"    → default-on (candidate not present)
os.getenv("FEATURE_AUDIT_LOG", "")     → default ""      → default-off → present, Likelihood Medium
process.env.ENABLE_FILTER ?? "false"   → default "false" → default-off → present, Likelihood Medium
```

---

## Classification guardrails for commonly-misjudged checks

These checks are historically misclassified across runs. Apply these binding
rules — note that in v5 every "residual risk" outcome is **present** with a
reduced **Likelihood**, never a separate verdict:

- **CHECK 3 (Conflated context / prompt injection):** Any prompt-injection
  defence that relies on content classifiers, output filters, delimiter
  framing, or groundedness detection is **probabilistic by nature** and cannot
  clear the finding. If untrusted text enters the model's context window, the
  failure mode is **present**; a probabilistic defence sets **Likelihood =
  Medium** (partial control), it does not earn "not present". Only a system
  with NO untrusted data in the model's context window earns "not present" for
  this check.

- **CHECK 5 (Live-fetch dependency):** Evaluate BOTH the production build path
  (Dockerfile, build script) AND all CI/CD workflow install steps. A version
  pin without a hash / checksum is **present** with **Likelihood = Low** (a
  strong-but-incomplete compensating control). An unpinned install
  (`pip install pytest`, `npm install mocha`) or a mutable base-image tag
  (`:latest`, `:3.12`, `:node-20`) without digest pinning is **present** with
  **Likelihood = High** if reachable. Cite both production AND CI paths.

- **CHECK 8 (Plaintext journal):** Check BOTH the redaction / filtering
  mechanism (what it covers) AND any log calls that dump full exception stack
  traces (`exc_info=True`, `traceback.format_exc()`, `console.error(err.stack)`,
  `logger.error("...", e)`). A redaction filter that covers tokens / keys but
  not resource identifiers, file paths, or endpoint URLs leaked in stack traces
  is **present** with **Likelihood = Medium** (partial redaction).

- **CHECK 11 (Shared identity runtime):** Evaluate the hosting identity's FULL
  permission set (all role assignments, all API scopes, all file-system
  permissions), not just whether individual operations use scoped credentials.
  If the hosting identity holds roles broader than the minimum needed
  (Contributor where Reader suffices, admin scope where read suffices), that is
  **present** under the process-compromise scenario. List every role / scope
  assigned to the hosting identity; broad standing privilege keeps Likelihood
  at Medium or High.

- **CHECK 18 (Hallucinated authority):** If groundedness or factuality
  detection exists but is advisory-only (it flags but does not block), the
  finding is **present** with **Likelihood = Medium** (advisory control), never
  "not present".

- **CHECK 21 (Unbounded consumption):** If per-request caps exist but no
  per-user daily / monthly cap exists, that is **present** with **Likelihood =
  Low** (partial ceiling) — not "not present".

- **CHECK 22 (Unattributable action):** Quote the EXACT default value of the
  audit / ledger feature flag from source code. If the audit trail requires an
  external resource to be provisioned (a database, a storage account, a log
  sink) and silently degrades to a no-op without it, that is **present** with
  **Likelihood = Medium** (infrastructure-dependent control) — even if the
  feature flag defaults to "on".

- **CHECK 23 (Rubber-stamp approval):** A human-in-the-loop gate that fires on
  every action regardless of risk, shows too little context, or has no
  consequence for bulk-approval provides no real oversight → **present** with
  **Likelihood = Medium** (the control exists but is ineffective).

---

## How to investigate (this determines report quality)

For each check:

- Search the whole repo yourself. Understand the repo in depth. Do not wait for
  files. **Ignore generated, vendored, and dependency folders** (build output,
  `node_modules`, `vendor`, `dist`, `.venv`, generated folders, and any
  unpacked third-party sub-project archive — these are reference data, not the
  assessed code).
- Trace the runtime call path, not just declarations. A type, flag, or helper
  only matters if something running invokes it. For every candidate, confirm
  whether it is reached from a CLI entrypoint, shell, API handler, batch
  dispatch, server, or install path — or only from tests. Treat "defence exists
  in code but is never wired into the running path" as **present** with a
  Likelihood that reflects the unwired control (it does not lower risk).
- Find the sharp edges, not the config surface. Look for: permission handlers
  that always approve; CLI flags like `--allow-all`, `--yolo`, `--autopilot`,
  `--no-ask-user`; whole-`process.env` passthrough; refresh tokens or PATs on
  disk / in memory; clones by mutable branch ref; CI actions by tag not SHA;
  internal errors leaking to HTTP clients; full stack traces in log output;
  `exc_info=True` or `console.error(err.stack)` in telemetry. Open the main
  entrypoint, API handler, auth module, webhook handler, and upstream fetch
  code.
- **Evidence must be exact.** File path + line number + a verbatim one-line
  quote per claim. Aim for 4-5 cited lines on every finding (present, not
  present, or unclear). Read files in full before judging. List the search
  terms you used per check.
- **Earn the verdict and the Likelihood.** Prefer "not present" ONLY where a
  deterministic, non-bypassable, default-on control exists and you quote it.
  Any partial / probabilistic / opt-in / silently-degrading control is
  **present** at a reduced Likelihood. Weight Likelihood for the deployment
  model described in the repo (single-user, multi-tenant, cloud, local) — a
  surface reachable by untrusted internet input is High; one needing an
  internal actor is Medium.

---

## Depth uniformity — no taper allowed

Checks 20-25 MUST have the same evidence density as Checks 1-5. If you notice
your evidence bullets dropping from 5 to 2, you are tapering — stop, re-read the
relevant files, and bring evidence back to 4-5 bullets.

Specifically: after completing Check 15, pause and verify you still have
sufficient context budget. If needed, internally summarise earlier checks'
evidence to free context for remaining checks. The last 10 checks MUST NOT be
shallower than the first 10. A taper pattern (rich evidence early, sparse
evidence late) is a failed run.

---

## Per-check response format — evidence first

For EVERY check, **show your work first**, then give the verdict and severity so
they follow mechanically from the evidence:

```
A. SEARCH — list the exact search terms and globs you used and the files you
   opened. Ignore generated, vendored, and dependency folders.
B. EVIDENCE — enumerate every match as file path + line number + a one-line
   quote. The verdict below must follow from this list.

Then respond with exactly these sections:
1. VERDICT: one of [present / not present / unclear / not applicable]
2. SEVERITY: one of [Critical / High / Medium / Low / N/A], computed as
   Likelihood × Impact. State the Impact baseline, the Likelihood level, and
   the matrix cell you landed on. Use N/A for not present / not applicable /
   unclear.
3. WHY IT MATTERS: two sentences, plain English.
4. FIX: a concrete change, with a short before/after code snippet if
   applicable. If "unclear", list the one piece of context you need to decide.
5. AUDIT BLOCK: a fenced json object with the exact schema below, so two runs
   on the same commit are diff-able.
```

After CHECK 25, write a one-paragraph SUMMARY of every **present** and
**unclear** check, ordered most-serious first (by severity, then check number),
then an AUDIT BLOCKS appendix: one fenced JSON array with one object per check
(see schema below) — so two runs on the same commit are diff-able.

### Depth bar — every check must read like the worked exemplar

Vague, one-line fields are a failed run. Each check, regardless of verdict, must
carry the SAME seven labelled fields, each as full prose, never a stub:

1. **What this check is.** 1-2 sentences defining the risk in plain English for
   a non-expert, spelling out any acronym (AI, MCP, etc.) on first use.
2. **Why we check it.** Name the exact standard mapping (e.g. OWASP LLM Top 10
   LLM06 — Excessive Agency, MITRE ATLAS, NIST AI RMF) and say in one sentence
   what that mapping means here.
3. **What goes wrong if it is not fixed.** One concrete worst-case sentence —
   the real-world consequence, not a generic platitude.
4. **Verdict + severity.** The verdict, then the severity with its derivation:
   `present — High (Impact High × Likelihood Medium)`. For not present / not
   applicable / unclear, the severity is **N/A**.
5. **Evidence.** 4-5 bullets, each path/file line N + verbatim code. For "not
   present" quote the deterministic control that earns it; for "not applicable"
   say what was searched and why the surface is absent; for "present" quote the
   unsafe path AND any partial control (the partial control explains the
   Likelihood); for "unclear" quote the surface and name the one missing fact.
6. **Why this verdict, here.** 2-3 sentences tying the cited lines to the
   verdict — name the running path (API handler, CLI, install) that proves it
   is reachable, not just declared — AND justify the Likelihood level (no
   control → High; partial / internal-actor → Medium; strong compensating
   control → Low). State which threat-model scenario drives it.
7. **Recommended fix and why it helps.** A before/after code block AND a closing
   sentence on the blast-radius reduction (often: which Likelihood level the fix
   moves you to). Skip the code block only for n/a checks — but still explain
   what to do if the surface is added later.

#### Worked exemplar — the bar for all 25 checks

This is a FICTIONAL example showing the required format, depth, and style. Your
evidence must cite REAL paths, lines, and code from the ACTUAL codebase being
assessed — never copy this example's file paths or code.

> **What this check is.** This check asks whether the artificial-intelligence
> (AI) agent is granted tool access that is effectively unlimited — an open
> shell, an un-allow-listed network client, unrestricted file-system access, or
> auto-approval of all tool calls regardless of risk.
>
> **Why we check it.** This maps to OWASP LLM Top 10 LLM06 (Excessive Agency —
> the model can do more than it should) and MITRE ATLAS unauthorised-action
> patterns (AML.T0054). An agent with no tool boundary turns any model mistake
> or injected instruction into a real-world action.
>
> **What goes wrong if it is not fixed.** A single bad model decision or planted
> instruction can delete files, exfiltrate data, or run arbitrary commands on
> the hosting machine.
>
> **Verdict: present — Critical (Impact High × Likelihood High)**
>
> **Evidence.**
> - src/agent/permissions.py line 42 — `def check_tool(name): return True  # always approve`
> - src/agent/executor.py line 88 — `result = await tool.run(args)` (no pre-check before execution)
> - src/cli/batch.py line 31 — `parser.add_argument("--no-confirm", default=True)` (confirmation disabled by default)
> - src/agent/tool_registry.py line 15 — `TOOLS = {t.name: t for t in discover_all()}` (loads every discovered tool)
> - config/defaults.yaml line 8 — `tool_allowlist: "*"` (wildcard, no restriction)
>
> **Why this verdict, here.** The permission check at line 42 returns True
> unconditionally, the batch CLI disables confirmation by default (line 31), and
> the tool registry loads every discovered tool with no allow-list filter (line
> 15). Because a prompt-injection payload in a tool result reaches `tool.run()`
> at line 88 with no gate at all, the unsafe path is reachable by untrusted
> input with no mitigating control → **Likelihood High**; crossed with the fixed
> **Impact High** baseline, the matrix yields **Critical**.
>
> **Recommended fix and why it helps.** Replace the blanket approval with an
> explicit allow-list and require confirmation for unlisted tools:
> ```python
> # Before:
> def check_tool(name): return True  # always approve
>
> # After:
> SAFE_TOOLS = {"read_file", "list_dir", "search"}
> def check_tool(name):
>     if name in SAFE_TOOLS:
>         return True
>     return prompt_user_confirmation(name)
> ```
> This reduces blast radius from "any tool executes silently" to "only
> pre-approved read-only tools run without confirmation", forcing a human gate
> on high-impact actions — moving Likelihood from High to Low and the severity
> from Critical to Medium.

---

## The 25 checks

Each check below lists its **fixed Impact baseline** in brackets — that value
is the Impact axis for the severity lookup and never changes per run.

CHECK 1 — Wildcard tool exposure **[Impact: High]**: the AI agent is granted
tool access that is wildcard or near-wildcard — for example, an unrestricted
shell, an HTTP client with no domain allow-list, file-system access with no
path restriction, or an MCP / plugin client that loads every tool advertised by
the server.

CHECK 2 — Unauthenticated tool channel **[Impact: High]**: the agent connects
to remote tool servers (HTTP endpoints, MCP servers, plugins, function-call
backends) without authentication, without TLS, or from URLs that are
user-controlled / configuration-controlled with no allow-list of trusted hosts.

CHECK 3 — Conflated context (prompt injection) **[Impact: High]**: untrusted
text (web-page contents, tool results, user-supplied documents, RAG
retrievals) is placed into the model's context window without clear separation
from trusted instructions, without sanitisation, and without any downstream
check that prevents the model from following directives embedded in that text.
NOTE: any defence that relies on content classifiers, delimiter framing, or
output filters is probabilistic — verdict is **present** with **Likelihood
Medium**, never "not present".

CHECK 4 — Comment-to-commit promotion **[Impact: High]**: a comment, issue,
chat message, or other text written by anyone with read access can cause the
agent to perform a write action (commit code, merge a pull request, deploy,
modify a ticket, send a message) without an additional authorisation step that
checks the actor's permission to perform that specific write. If the codebase
does not integrate with any such surface, answer "not applicable".

CHECK 5 — Live-fetch dependency **[Impact: High]**: the agent (or its install /
startup script) fetches code or container images using floating references —
`:latest`, `@main`, unpinned package installs at runtime, or `curl ... | sh`
patterns. NOTE: evaluate BOTH the production build path AND all CI/CD workflow
install steps. A version pin without a hash is **present** with **Likelihood
Low**; an unpinned install or a mutable base-image tag is **present** with
**Likelihood High** if reachable. A pinned version with a content hash or lock
file in every path is "not present".

CHECK 6 — Standing credential **[Impact: High]**: the agent process holds a
credential that is long-lived (no automatic rotation), broad in scope, and
continuously available in memory or on disk — a static API key, a personal
access token, or a service-account secret read from environment variables at
startup and never refreshed. A short-lived token minted just-in-time per call
from a workload identity is "not present". NOTE: if the code path that reads
static secrets has been REMOVED (import deleted, raises error), that is "not
present"; if it EXISTS but is gated behind a condition, quote the condition and
determine whether it is reachable.

CHECK 7 — Phishable flow **[Impact: Medium]**: the agent uses an authentication
flow that is meaningfully more phishable than the device can support — most
commonly OAuth device-code flow running on a machine that has a browser
available, where authorisation-code-with-PKCE would have worked. If the
codebase does not perform end-user authentication at all, answer "not
applicable".

CHECK 8 — Plaintext journal **[Impact: Medium]**: the agent writes telemetry
(logs, traces, metrics, spans) that contains raw model prompts, raw tool
arguments, retrieved document contents, or raw model output, without redaction
of personal data, credentials, or business-sensitive content, and without an
access boundary that matches the sensitivity of the original data. NOTE: check
BOTH the redaction / filter mechanism AND any calls that dump full exception
stack traces containing resource identifiers, file paths, or endpoint URLs.
Partial redaction is **present** with **Likelihood Medium**.

CHECK 9 — Mutable reference trust **[Impact: High]**: any CI workflow, pipeline
definition, build script, or orchestration manifest references third-party
actions, images, or modules by a mutable name (a tag, a branch, "latest")
instead of by a content-addressable hash (commit SHA, image digest). If the
codebase has no CI / pipeline files, answer "not applicable".

CHECK 10 — Unsupervised perimeter **[Impact: High]**: the codebase defines or
registers multiple agents, sub-agents, or tool-bearing flows, but the security
and governance controls (rate limits, tool allow-lists, evaluation hooks,
logging, content filters) are applied to only a subset of them. Enumerate every
distinct agent or sub-agent and state for each whether the controls applied to
the main one also apply to it.

CHECK 11 — Shared identity runtime **[Impact: High]**: the agent executes in
the same security context as the operator who launched it (same OS user, same
cloud identity, same file-system permissions, same process), rather than in a
sandbox / separate identity with a narrower set of permissions. NOTE: evaluate
the hosting identity's FULL permission set (all role assignments, API scopes,
file-system access), not just whether individual operations use scoped
credentials. List every role or scope assigned to the hosting identity.

CHECK 12 — Documented defence that doesn't exist **[Impact: Medium]**: a
security-relevant claim in the documentation is not backed by an implementation
in the source. Read README.md and any docs/ file that mentions security,
sandboxing, isolation, logging, redaction, authentication, or permissions. For
each claim, quote the doc sentence verbatim, then either point to the
file/lines that implement it or state "no implementation found".

CHECK 13 — Internal-to-product gap **[Impact: Medium]**: assume this codebase
is about to be published for use by people other than its authors. Read the
source plus README.md, LICENSE, SECURITY.md, and any docs/ file. Identify
obligations that apply to shipped software but not to internal tools, and state
whether each is met:
- multi-tenant isolation (no cross-tenant data leakage)
- abuse-handling path (rate limits, reporting channel)
- user-facing error messages (no internal stack traces / secrets)
- a security contact and disclosure policy
- a clear statement of what data the tool sends where

CHECK 14 — Poisoned knowledge base **[Impact: High]**: documents ingested into
a retrieval-augmented-generation (RAG) index or a fine-tuning / training corpus
are treated as trusted — no provenance check, no integrity validation, and no
sanitisation of content that could carry embedded instructions, before that
content can influence the model. If there is no RAG corpus or training data,
answer "not applicable".

CHECK 15 — Persistent memory poisoning **[Impact: High]**: the agent persists
memory or "learnings" across sessions, and content can be written into that
store from untrusted input (user messages, tool results, retrieved documents)
without validation, attribution, or a scope boundary — so a malicious
instruction written once replays in later sessions. If the agent keeps no
cross-session memory, answer "not applicable".

CHECK 16 — Porous vector store **[Impact: Medium]**: a vector store or
retrieval index is queried without enforcing the caller's access rights — no
per-tenant or per-user filter on the similarity search, so one user's query can
return another's documents, and the embeddings are not protected against
inversion. If there is no vector store or retrieval index, answer "not
applicable".

CHECK 17 — Unsanitised output sink **[Impact: High]**: raw model output is
passed into a downstream interpreter or sink — a shell command, a SQL query, an
HTML / DOM context, an eval, a file path, or a generated API call — without
encoding, parameterisation, or validation appropriate to that sink, so the
model can emit an injection payload.

CHECK 18 — Hallucinated authority **[Impact: Medium]**: the system takes a
consequential action, or presents a fact to a user as authoritative, based
purely on the model's assertion — no verification against a source of truth, no
uncertainty signal, no ground-truth check — so a confident hallucination (a
non-existent API, a wrong policy, a fabricated citation) is acted on. NOTE: if
groundedness or factuality detection exists but is advisory-only (flags but
does not block), the verdict is **present** with **Likelihood Medium**, never
"not present".

CHECK 19 — Implicit agent-to-agent trust **[Impact: High]**: in a multi-agent
or sub-agent system, one agent accepts another's output, instructions, or tool
calls as trusted — without authenticating the source, validating the message,
or constraining it to that agent's permitted scope — so a single compromised
agent can pivot through the orchestration. If there is only a single agent,
answer "not applicable".

CHECK 20 — System prompt as secret store **[Impact: High]**: the system prompt,
developer prompt, or any instruction text sent to the model contains secrets
(API keys, connection strings, passwords, tokens) or security-relevant access
rules relied on for enforcement — treating the prompt as confidential when it
is extractable.

CHECK 21 — Unbounded consumption **[Impact: Medium]**: the agent has no ceiling
on consumption — no maximum token budget per request, no recursion /
tool-call-loop limit, no per-user rate or spend cap — so it can be driven into
a runaway loop or adversarial cost amplification (denial of wallet). NOTE: if
per-request caps exist but no per-user daily / monthly cap exists, that is
**present** with **Likelihood Low**.

CHECK 22 — Unattributable action **[Impact: Medium]**: actions the agent takes
(tool calls, writes, approvals, external requests) are not recorded in a
tamper-resistant audit trail that links each action to the initiating user /
session, the agent decision, and a timestamp — so after an incident you cannot
answer "who did this, and why". NOTE: quote the exact default value of the
audit / ledger feature flag from source code. If the audit trail silently
degrades when its backing store is not provisioned, that is **present** with
**Likelihood Medium**.

CHECK 23 — Rubber-stamp approval **[Impact: Medium]**: a human-in-the-loop
approval gate fires on every action regardless of risk, presents too little
context to make a real decision, or has no consequence for bulk-approving — so
the reviewer is trained to rubber-stamp and the control provides no real
oversight. If there is no human-approval gate at all, answer "not applicable".

CHECK 24 — Ungated model swap **[Impact: Medium]**: the model version, system
prompt, or generation parameters can be changed and reach production without
passing an automated evaluation gate — no representative regression suite, no
comparison of new versus previous behaviour, and no gradual rollout or quick
revert. If the system has no configurable model / prompt, answer "not
applicable".

CHECK 25 — Boundaryless data egress **[Impact: High]**: the agent has broad
read access and broad outbound reach (model endpoints, external tools, write
destinations) with no control that decides, per piece of data, whether it may
cross a jurisdictional, contractual, purpose, or tenant boundary — no data
classification / residency tagging, no egress policy at the tool boundary, and
unlogged egress. If the agent never handles regulated / multi-tenant data or
has no external egress, answer "not applicable".

---

## Reproducibility contract

Two runs on the same model build and the same commit MUST produce:
- Identical verdicts (same classification per check)
- Identical Impact baselines (these are fixed per check), identical Likelihood
  scores, and therefore identical severity (same matrix cell per check)
- Evidence pointing to the same files and approximate lines (±5 lines)
- An AUDIT BLOCKS JSON array that is diff-able with `jq`

AI assistant output is probabilistic, so "consistent" has a precise meaning:
*same model build + same commit + same prompt → the same rule-derived verdict
and severity*, with any difference surfaced by diffing the audit blocks rather
than hidden in prose. If you are uncertain between two Likelihood levels, choose
the HIGHER one and explain the ambiguity in "Why this verdict, here". Never
round severity down silently. A run that disagrees with its own rubric
definitions is a failed run.

---

## Deliverable — write two files

Generate a fresh report on every run — never overwrite or reuse a prior report.
Create Self-Testing-Report/ at repo root; compute a timestamp
YYYY-MM-DD-HHmmss (local time) once, then write the same findings into two
newly created, datetime-stamped files:
1. `Self-Testing-Report/security-self-assessment-<timestamp>.md`
2. `Self-Testing-Report/security-self-assessment-<timestamp>.html`

Do not delete earlier dated reports; each run adds a new pair so the history is
preserved.

Microsoft style: clear heading hierarchy, sentence-case headings, plain active
voice, no marketing tone, every acronym expanded on first use, generation date,
and a one-line "AI-generated, must be verified" note.

Structure (both files): **A** title + metadata (repo, date, model, scope,
method, commit hash, probabilistic disclaimer); **B** executive summary — one
paragraph on what it is and how to read it, counts by verdict, top risks in
plain English; **C** severity table (# | Check | Verdict | Severity | one-line
summary, most-serious first — sort by severity using the order Critical > High >
Medium > Low > N/A, then by check number); **D** one section per check carrying
all SEVEN labelled fields from the Depth bar as separate bold-labelled
paragraphs in this exact order: What this check is / Why we check it (cite OWASP
LLM Top 10, NIST AI RMF, MITRE ATLAS) / What goes wrong if it is not fixed /
Verdict + severity (with the `Impact × Likelihood → cell` derivation) /
Evidence (path:line + quote, 4-5 bullets) / Why this verdict, here / Recommended
fix and why it helps (before/after fenced code + closing blast-radius sentence);
**E** appendix of exact search terms per check; **F** an alphabetised glossary.

Both files must show ALL seven per-check fields with the exemplar's depth. In
the HTML, render them as bold field labels (or `<dl>`) per check, never a
one-line summary. Both files must embed the diagrams: the MD uses fenced
` ```mermaid ` blocks; the HTML uses `<pre class="mermaid">`. Place a
sequenceDiagram inside the detailed section of every **present OR unclear**
finding in BOTH formats, not only in the HTML.

Per-check Markdown layout is fixed — never collapse fields onto one line. Each
of the seven fields is its own paragraph, on its own line, separated by a blank
line, with the label bolded and ending in a period. Evidence is a bullet list,
one cited line per bullet. The fix shows a fenced before/after code block then a
closing blast-radius sentence. Render EXACTLY this skeleton per check:

### Check N — Title

**What this check is.** <1-2 full sentences, plain English, acronyms expanded.>

**Why we check it.** <standard mapping + one sentence on what it means here.>

**What goes wrong if it is not fixed.** <one concrete worst-case sentence.>

**Verdict: present — High (Impact High × Likelihood Medium)**

**Evidence.**
- path/file line 73 — `verbatim quoted code`
- path/file line 198 — `verbatim quoted code`
- path/file line 90 — `verbatim quoted code`
- path/file line 44 — `verbatim quoted code`

**Why this verdict, here.** <2-3 sentences naming the running path, justifying
the Likelihood level, and stating which threat-model scenario drives it.>

**Recommended fix and why it helps.** <one sentence>, then a fenced before/after
code block, then a closing sentence on blast-radius reduction (which Likelihood
level the fix moves you to).

```mermaid
sequenceDiagram
    accTitle: Check N exploit path
    accDescr: Brief description
    participant A as Attacker
    participant B as Agent
    A->>B: Malicious input
    B->>B: Unprotected action
```

Anything shorter — combined labels, no blank lines, no bullets, no code block —
is a failed run. The HTML mirrors this: each field a `<dt>`/`<dd>` or bold
paragraph, evidence a `<ul>`, fix a `<pre><code>` block, never one line.

Write for a reader who is not a security expert. Every field must be full prose,
not a stub, on its own line as its own bold-labelled paragraph. Aim for ~3
sentences per field. Quote real path:line evidence (4-5 separate bullets) and
give a concrete before/after fenced code block. Never combine two fields with a
slash; never run labels and prose together; no single-line filler — say what was
searched and why the verdict holds.

---

## Detail and glossary — make jargon self-explaining

- Expand and explain every acronym / standard on first use and keep a glossary.
  When you cite a framework code, spell out what it actually means in context —
  e.g. OWASP LLM06 = "Excessive Agency: the model can do more than it should";
  NIST AI RMF Govern = "the Govern function — own the risk, set policy"; MITRE
  ATLAS = "real-world AI attack-technique catalogue". Likewise define product
  terms used in fixes: FS jail = "file-system jail: restrict reads and writes to
  one directory tree"; allow-list, PKCE, loopback bind, MCP.
- HTML: every abbreviation gets a hover/focus tooltip. Wrap each term in
  `<abbr class="gloss" tabindex="0" data-tip="full explanation">TERM</abbr>`; on
  mouseover AND keyboard focus show a well-formatted popup (rounded card,
  padding, contrast ≥ 4.5:1, max-width ~320px) with the expansion. Drive every
  tooltip from one central glossary object so terms stay consistent.
- Add an alphabetised glossary section in both files listing every acronym,
  standard, and product term with a one-line definition.

---

## Sorting and search — top of the HTML

- Sort control: a `<select>` to reorder the severity table AND the detailed
  sections live by Check number (1-25) or Severity (most-serious first, using
  Critical > High > Medium > Low > N/A). Re-order the actual DOM finding
  sections, not just the table.
- Search box: a text input that filters checks by title, verdict, severity, and
  evidence text; hide non-matching sections and rows, show a result count, clear
  button, keyboard accessible, debounced.

---

## Diagrams

- One overall Mermaid trust-boundary AND data-flow flowchart — must be detailed,
  not a four-box sketch. Requirements:
  - Discover the actual zones in this codebase. Typical zones include: untrusted
    inputs (user messages, tool results, external data), application trust zone
    (server, agent, tools, governance controls), credential / identity store
    (tokens, keys, managed identity), and external egress (model endpoint,
    external APIs, storage, telemetry). Use whatever zones this codebase
    actually has — do not invent zones.
  - Draw named, directional edges that name the actual data on the wire — e.g.
    "tool result text", "bearer token", "SSE stream", "audit record", "redacted
    telemetry". Quote the edge label so spaces and slashes are safe:
    `A -->|"tool result text"| B`.
  - Mark each crossing of a trust boundary and tag the control or gap on that
    edge (content filter, SSRF validation, rate limit, token budget, credential
    gate).
  - Colour every node with classDef AND keep text labels; ≥ 12 nodes, ≥ 14 edges
    so the trust boundaries and data path are genuinely legible.
- One Mermaid sequenceDiagram exploit path per **present OR unclear** finding,
  embedded in both the Markdown and the HTML: untrusted input → agent →
  tool / credential / channel → impact. accTitle + accDescr, complete unclipped
  labels, viewBox kept, plain-text summary after each.

### Diagram safety — mandatory, must never regress

Mermaid renders a "Syntax error in text" bomb whenever reserved characters leak
into node, message, or label text. To guarantee this never happens:

- Sanitise every dynamic string before it enters any diagram (node labels, edge
  labels, participant aliases, sequence messages). Strip or replace `;` `:` `(`
  `)` `<` `>` `{` `}` `#` `"` `|`, collapse `--` to `-`, and squash whitespace.
  Never interpolate a check title, summary, or evidence string raw into mermaid.
- No semicolons or colons inside message text — they terminate statements. Quote
  any flowchart label that contains spaces / slashes as `["..."]`.
- `accTitle:` and `accDescr:` MUST keep the colon. `accTitle Foo` without a
  colon is a syntax error. Sequence messages must stay free of `: ; ( ) |`.
- Diagram text must be static-safe: prefer fixed, hand-written messages over raw
  field interpolation.
- Validate before finishing: after writing the HTML, confirm every
  sequenceDiagram / flowchart block parses (no `;` `:` `(` `)` `|` in messages,
  every `["..."]` balanced).
- Never re-render rendered output — preserve the source. BEFORE the first
  `mermaid.run()`, stash each diagram's raw definition in a `data-src` attribute
  (or a JS map keyed by id). For full-screen, theme switch, and replay, always
  re-render from that stored source into a fresh `<pre class="mermaid">` node —
  never from the live DOM text.
- Guard the render loop against empty diagram nodes. Filter the node list to
  those that actually have a `data-src` before mapping / `.replace()`.

---

## HTML must be interactive (not a static dump)

Single self-contained file (inline CSS + one pinned Mermaid CDN tag), opens from
the local filesystem. Fluent 2 (Segoe UI Variable, Segoe UI, system-ui,
rounded, tokens). Light + dark via prefers-color-scheme and a keyboard toggle;
Mermaid theme follows. Mermaid init: startOnLoad, securityLevel 'loose',
htmlLabels, useMaxWidth, nodeSpacing 60, rankSpacing 70.

Each diagram in a `<figure>` with a Full-screen `<dialog>` button. The
full-screen dialog MUST fill the viewport and scale diagrams to fit — follow
these rules exactly:

- **Dialog sizing:** Use `dialog[open]` CSS with explicit `width: 95vw;
  height: 90vh` (not just max-width/max-height, which let the dialog shrink to
  content size). Set `display: flex; flex-direction: column`.
- **Content container:** `#fullscreen-content` must have `flex: 1; display:
  flex; align-items: center; justify-content: center; min-height: 0; overflow:
  auto` so it fills the dialog body.
- **SVG scaling:** After `mermaid.run()` resolves (use `.then()`), find the
  rendered `<svg>` inside `#fullscreen-content`, remove its fixed `width`,
  `height`, and `style` attributes, then set `width="100%"`, `height="100%"`,
  and `preserveAspectRatio="xMidYMid meet"`. This forces the SVG to scale up to
  fill the container rather than rendering at its natural (tiny) size.
- **Source:** Always re-render from the stored `data-src` definition (NOT from
  the rendered SVG DOM).
- **Close:** Esc key, Close button, and click-outside-dialog all close.
- **Focus return:** Track which button opened the dialog; on close, return focus
  to that button.
- **Focus trap:** Tab / Shift-Tab must cycle within the dialog while open (close
  button and any other focusable elements).
- Honour `prefers-reduced-motion: reduce`.

WCAG 2.1 AA: landmarks, skip link, single h1 + correct order, focus ring, scope
headers, descriptive links, lang, severity badge = colour AND text label,
contrast ≥ 4.5:1. The N/A severity badge must be visually distinct from the
Low badge and carry the text "N/A".

A toolbar pinned near the top must hold the sort `<select>`, the search input
with result count + clear, and the theme toggle. Sorting and search must
re-order / hide the real `<section>` findings, not just the table. Tooltips
(abbr.gloss) must work on hover and focus, never overflow the viewport.

Embed the AUDIT BLOCKS JSON array in both report files — a fenced `json` block
in the Markdown and a collapsible `<details>` block in the HTML. The array must
contain one object per check (25 total) with this EXACT schema:

```json
[
  {
    "id": "wildcard-tool-exposure",
    "verdict": "present",
    "impact": "High",
    "likelihood": "High",
    "severity": "Critical",
    "evidence": [
      {"file": "src/agent/permissions.py", "line": 42, "quote": "def check_tool(name): return True"}
    ],
    "search_terms": ["shell", "subprocess", "eval", "exec", "tool_map"]
  }
]
```

Schema rules — every field is required:
- `id` is the **check slug** (kebab-case, derived from the title with any
  parenthetical and apostrophes dropped) — e.g. `conflated-context`,
  `documented-defence-that-doesnt-exist`. It is a string, not a number. There
  is **no** `check` field.
- `verdict` is one of: `present`, `not present`, `unclear`, `not applicable`.
- `impact` is the fixed baseline for that check: `High` or `Medium`.
- `likelihood` is one of: `High`, `Medium`, `Low`, or `N/A` (use `N/A` when the
  verdict is not present / not applicable / unclear).
- `severity` is the matrix result: `Critical`, `High`, `Medium`, `Low`, or
  `N/A`. It must equal `impact × likelihood` per the matrix, and `N/A` whenever
  likelihood is `N/A`.
- `evidence` is an array of 1-5 objects (`file`, `line`, `quote`).
- `search_terms` lists the grep / search patterns you actually used.

The slugs, in check order, are: `wildcard-tool-exposure`,
`unauthenticated-tool-channel`, `conflated-context`,
`comment-to-commit-promotion`, `live-fetch-dependency`, `standing-credential`,
`phishable-flow`, `plaintext-journal`, `mutable-reference-trust`,
`unsupervised-perimeter`, `shared-identity-runtime`,
`documented-defence-that-doesnt-exist`, `internal-to-product-gap`,
`poisoned-knowledge-base`, `persistent-memory-poisoning`, `porous-vector-store`,
`unsanitised-output-sink`, `hallucinated-authority`,
`implicit-agent-to-agent-trust`, `system-prompt-as-secret-store`,
`unbounded-consumption`, `unattributable-action`, `rubber-stamp-approval`,
`ungated-model-swap`, `boundaryless-data-egress`.

After writing both files, print the two datetime-stamped paths and the count of
`present` and `unclear` findings.

---

## Self-verification checklist — MANDATORY before finishing

After writing both files, walk through EVERY item below. If ANY item fails, fix
the report before declaring done. Do not skip this section.

### Content completeness
- [ ] All 25 checks have a section in both MD and HTML
- [ ] Every check has all 7 labelled fields as separate paragraphs (not
      combined on one line, not abbreviated)
- [ ] Every check has 4-5 evidence bullets with path:line + verbatim quote
- [ ] Checks 20-25 have the same evidence density as Checks 1-5 (no taper)
- [ ] Every present check has a before/after code block in the fix
- [ ] Every present OR unclear check has a sequenceDiagram in BOTH MD and HTML
- [ ] The overall trust-boundary flowchart has ≥ 12 nodes and ≥ 14 edges
- [ ] The AUDIT BLOCKS JSON array has exactly 25 objects, one per check

### Verdict and severity consistency
- [ ] No probabilistic defence (content filter, prompt shield, groundedness
      check) received "not present" — each is "present" with Likelihood Medium
- [ ] No opt-in or silently-degrading control received "not present" — each is
      "present" with a reduced Likelihood
- [ ] Every "not applicable" cites what was searched and why the surface is
      absent
- [ ] Every "unclear" names the single missing fact
- [ ] Every Impact value equals the fixed baseline for that check (High/Medium)
- [ ] Every severity equals Impact × Likelihood per the matrix, and is N/A for
      not present / not applicable / unclear
- [ ] Every verdict matches the binding decision-rules table
- [ ] The AUDIT BLOCKS JSON verdicts, impacts, likelihoods, and severities match
      the prose exactly

### HTML functionality
- [ ] The file opens from the local filesystem (no server required)
- [ ] Sort control reorders both the severity table AND the finding sections
- [ ] Search box filters findings and shows a result count
- [ ] Theme toggle switches light/dark and re-renders Mermaid diagrams
- [ ] Full-screen dialog opens at 95vw × 90vh and SVG scales to fill (not tiny)
- [ ] Full-screen dialog re-renders from data-src, not from rendered SVG
- [ ] Full-screen dialog has a focus trap (Tab cycles within dialog) and returns
      focus to the trigger button on close
- [ ] Every `<pre class="mermaid">` has a `data-src` attribute with the raw
      definition stashed before first render
- [ ] Glossary tooltips appear on hover AND keyboard focus
- [ ] Skip link, landmarks, lang attribute, single h1, scope headers present
- [ ] Severity badge shows colour AND text; the N/A badge is distinct from Low
- [ ] No Mermaid syntax errors (no `;` `:` `(` `)` `|` in sequence messages)

### Metadata
- [ ] Commit hash from `git rev-parse HEAD` is in the metadata table
- [ ] Timestamp is consistent between the two filenames and the metadata
- [ ] "AI-generated, must be verified" disclaimer is present

When this defensive sweep is done, run a second, adversarial pass with the Tip 3 red-team prompt: it role-plays an attacker and produces an attack playbook rather than a checklist. Have it write its own report file (for example [report folder]/red-team-playbook-YYYY-MM-DD-HHmmss.md and .html) — keep it separate from the twenty-five-check findings table above, because an attack playbook and a per-check verdict table have different shapes and reading one as the other only confuses the result.

Next tip

The prompt pack is structured and predictable, which is a strength and a weakness. Tip 3 → is the unstructured counterpart: a single longer prompt that role-plays an attacker against your tool and tells you what they would try first.

← Back to the index