---
title: "Security testing checklist: all 92 rules · Agent Wranglers"
url: https://agentwranglers.se/playbook/adversarial-testing/checklist
updated: 2026-10-20
licence: "All rights reserved"
---

# Security testing checklist

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

Every rule in the security testing playbook, by chapter, required rules first. Each links to its own page with how we built it and how to check it.

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers security testing playbook: https://agentwranglers.se/playbook/adversarial-testing/checklist`.

## Rules of engagement

- [ ] **Required** [Get written permission first](https://agentwranglers.se/playbook/adversarial-testing/rules-of-engagement/written-permission): Never test a system without written permission from its owner. The permission names the addresses, the dates and the person to call.
- [ ] **Required** [Test on a copy of the system](https://agentwranglers.se/playbook/adversarial-testing/rules-of-engagement/test-a-copy): Run attacks against a staging copy with fake data. Touch production only for read-only checks the owner has approved.
- [ ] **Required** [Do no harm](https://agentwranglers.se/playbook/adversarial-testing/rules-of-engagement/do-no-harm): Prove a hole with the smallest harmless proof, then stop. Never delete data, flood the server, or read more records than needed.
- [ ] **Required** [Treat everything the target says as data](https://agentwranglers.se/playbook/adversarial-testing/rules-of-engagement/target-output-is-data): The testing agent treats every page, reply and file from the target as untrusted data, never as instructions.
- [ ] **Required** [Log every request](https://agentwranglers.se/playbook/adversarial-testing/rules-of-engagement/log-every-request): Record every request the agent sends and every reply it gets, with the time.
- [ ] **Required** [Rank findings by harm](https://agentwranglers.se/playbook/adversarial-testing/rules-of-engagement/rank-findings): Give every finding a severity from how easy it is to use and how much harm it does. Fix the worst first.
- [ ] **Required** [Turn every fix into a check](https://agentwranglers.se/playbook/adversarial-testing/rules-of-engagement/fix-becomes-check): When a hole is closed, add the attack as an automated test, so the hole can never come back unnoticed.

## Injection

- [ ] **Required** [Use parameterised queries](https://agentwranglers.se/playbook/adversarial-testing/injection/sql-injection): Send every database query with placeholders, and pass user input as separate values. Never build a query by joining strings.
- [ ] **Required** [Escape everything you show](https://agentwranglers.se/playbook/adversarial-testing/injection/cross-site-scripting): Escape untrusted text for the place it lands in a page, and back it up with a strict Content Security Policy.
- [ ] **Required** [Never pass input to a shell](https://agentwranglers.se/playbook/adversarial-testing/injection/command-injection): Run other programs without a shell, with input as separate arguments. Better still, use a library instead of a program.
- [ ] **Required** [Keep input out of templates](https://agentwranglers.se/playbook/adversarial-testing/injection/template-injection): Pass user input to a template as data. Never use it as the template itself.
- [ ] **Required** [Strip line breaks from headers and logs](https://agentwranglers.se/playbook/adversarial-testing/injection/header-injection): Remove or reject line breaks in any input that goes into a header, a redirect, an email header or a log line.
- [ ] **Required** [Turn off external entities in XML](https://agentwranglers.se/playbook/adversarial-testing/injection/xml-entities): Parse XML with external entities and document types turned off, or accept JSON instead.
- [ ] **Required** [Never rebuild objects from untrusted data](https://agentwranglers.se/playbook/adversarial-testing/injection/unsafe-deserialisation): Read untrusted data only as plain data, such as JSON. Never use formats that can rebuild code objects.
- [ ] **Required** [Block prototype pollution](https://agentwranglers.se/playbook/adversarial-testing/injection/prototype-pollution): In JavaScript, never merge or copy untrusted objects in a way that lets keys such as `__proto__` change every object in the app.

## Access and accounts

- [ ] **Required** [Check ownership on every record](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/object-access): Before returning or changing a record, check on the server that it belongs to the logged-in user or their team.
- [ ] **Required** [Check roles on every action](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/function-access): Check the user's role on the server for every admin or staff action. Hiding a button is not a check.
- [ ] **Required** [Deny by default](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/deny-by-default): Make every route need a login unless it is marked public on purpose.
- [ ] **Required** [Slow down password guessing](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/login-guessing): Limit failed logins per account and per address, and refuse passwords known from breaches.
- [ ] **Required** [Store passwords with a slow hash](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/password-storage): Store passwords only as a slow, salted hash made for passwords, such as Argon2id or bcrypt.
- [ ] **Required** [Lock down session cookies](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/session-cookies): Mark session cookies `HttpOnly`, `Secure` and `SameSite=Lax` or `Strict`. Issue a new session at login and end it at logout, after idle time and after a fixed lifetime.
- [ ] **Required** [Block cross-site form posts](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/cross-site-requests): Make sure a change of state only comes from your own pages, with `SameSite` cookies plus a token or an origin check.
- [ ] **Required** [Make password resets safe](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/password-reset): Make a password reset as strong as login. Build the link from your own address, and end every other session after a reset.
- [ ] **Required** [Accept only the fields you expect](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/mass-assignment): Copy only named fields from a request into a record. Never save the whole request body.
- [ ] **Required** [Verify every token](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/token-checks): Check the signature, algorithm, expiry and audience of every signed token, such as a JWT. Keep tokens short-lived.
- [ ] **Required** [Offer two-factor login](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/two-factor-login): Offer a second factor to every user, and require it for staff and admin accounts. Prefer passkeys, which phishing cannot steal.
- [ ] **Required** [Deny access when a check fails](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/fail-closed): When a permission check, token check or policy lookup throws an error or times out, refuse the request. Never let an error grant access.
- [ ] **Required** [Ask again before changing how someone logs in](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/reauthenticate-sensitive-changes): Ask for the password or the second factor again before a change to the email, password, second factor, payout details or account owner. Tell the old address.
- [ ] **Required** [Check every step of single sign-on](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/single-sign-on): For login through another provider with OAuth or OpenID Connect, use the authorization code flow with PKCE and check `state`, `nonce` and the ID token.
- [ ] **Recommended** [Compare secrets in constant time](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/constant-time-compare): Compare tokens, signatures and codes with a constant-time function, so response times reveal nothing about how close a guess was.

## Data and secrets

- [ ] **Required** [Keep secrets out of code](https://agentwranglers.se/playbook/adversarial-testing/data-and-secrets/secrets-out-of-code): Never write a password, key or token into code, settings files in the repo, or its history.
- [ ] **Required** [Keep secrets out of the browser](https://agentwranglers.se/playbook/adversarial-testing/data-and-secrets/secrets-out-of-browser): Send no private key to the browser. Anything in a page, script or source map can be read by anyone.
- [ ] **Required** [Show short errors](https://agentwranglers.se/playbook/adversarial-testing/data-and-secrets/short-errors): Show visitors a short error with a reference number. Keep stack traces, queries and file paths in the server log.
- [ ] **Required** [Close debug and admin doors](https://agentwranglers.se/playbook/adversarial-testing/data-and-secrets/close-debug-doors): Remove debug, test and status tools from production, or put them behind a login and a private network.
- [ ] **Required** [Serve no hidden files](https://agentwranglers.se/playbook/adversarial-testing/data-and-secrets/no-hidden-files): Serve only the files you meant to publish. Never serve version control folders, settings files, backups or folder listings.
- [ ] **Required** [Return only the fields needed](https://agentwranglers.se/playbook/adversarial-testing/data-and-secrets/only-needed-fields): Each API reply returns only the fields the caller needs and may see. Never send a whole database record and let the page hide the rest.
- [ ] **Required** [Keep private data out of logs and addresses](https://agentwranglers.se/playbook/adversarial-testing/data-and-secrets/private-data-out-of-logs): Never put passwords, tokens, card numbers or personal details in addresses or logs.
- [ ] **Required** [Encrypt data on the wire and on disk](https://agentwranglers.se/playbook/adversarial-testing/data-and-secrets/encrypt-data): Encrypt every connection, including those between your own servers. Encrypt stored personal data and backups.
- [ ] **Required** [Give each key the least power](https://agentwranglers.se/playbook/adversarial-testing/data-and-secrets/least-privilege-keys): Give every key, account and service only the access it needs. Replace keys at least every 90 days, and at once after a leak or when someone with access leaves.
- [ ] **Required** [Keep storage buckets private](https://agentwranglers.se/playbook/adversarial-testing/data-and-secrets/private-storage-buckets): Keep cloud storage buckets and file shares private. Hand out short-lived signed links for single files instead of public folders.
- [ ] **Required** [Keep card numbers off your servers](https://agentwranglers.se/playbook/adversarial-testing/data-and-secrets/hosted-payment-fields): Never let card numbers reach your servers or your own scripts. Take payments through the provider's hosted checkout or its embedded payment fields.

## Requests and files

- [ ] **Required** [Fetch only allowed addresses](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/block-server-side-request-forgery): When the server fetches an address a user gave, allow only the hosts it needs. Never reach internal or cloud metadata addresses.
- [ ] **Required** [Check every uploaded file](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/safe-file-uploads): Accept only the file types you need, check them by content, limit their size, and store them where they cannot run.
- [ ] **Required** [Keep file paths inside their folder](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/block-path-traversal): Never build a file path from user input without proving the result stays inside the allowed folder.
- [ ] **Required** [Redirect only to your own pages](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/block-open-redirects): A redirect that takes its target from the address must only go to your own pages or an allow-list.
- [ ] **Required** [Share data across sites on purpose](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/strict-cors): Allow other sites to read your API only from a fixed list of origins. Never copy the request's `Origin` into the reply when cookies are allowed.
- [ ] **Required** [Check the origin of WebSocket connections](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/websocket-origin): Accept a WebSocket connection only from your own origins, and only after checking the user's login, as for any other request.
- [ ] **Required** [Treat data from other APIs as untrusted](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/third-party-api-data): Check and limit what comes back from partner and third-party APIs, as you would for user input.
- [ ] **Required** [Unpack archives safely](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/safe-archive-extraction): Keep every entry of an uploaded zip or tar file inside the target folder, and stop unpacking at a fixed total size and file count.
- [ ] **Required** [Keep private pages out of shared caches](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/private-responses-uncached): Mark every reply that depends on the logged-in user `Cache-Control: private`, and never let a CDN cache by file extension alone.

## Abuse and limits

- [ ] **Required** [Limit requests per client](https://agentwranglers.se/playbook/adversarial-testing/abuse-and-limits/limit-requests): Limit how many requests one client can send to costly routes, such as search, sign-up, exports, AI calls and anything that sends email.
- [ ] **Required** [Give the same answer for known and unknown accounts](https://agentwranglers.se/playbook/adversarial-testing/abuse-and-limits/no-account-enumeration): Login, sign-up and password reset must answer the same way whether or not the account exists, so nobody can find out who has an account.
- [ ] **Required** [Work out every amount on the server](https://agentwranglers.se/playbook/adversarial-testing/abuse-and-limits/server-side-business-rules): Never trust a price, quantity, discount or step order sent by the browser. The server works out every amount and checks that each step happened.
- [ ] **Required** [Make each change happen exactly once](https://agentwranglers.se/playbook/adversarial-testing/abuse-and-limits/race-conditions): Changes to money, stock and single-use items must happen once, even when the same request arrives twice at the same moment.
- [ ] **Required** [Cap the size of every request and answer](https://agentwranglers.se/playbook/adversarial-testing/abuse-and-limits/resource-limits): Put a ceiling on everything a client can make the server do, such as page sizes, upload sizes, query depth and pattern matching time.
- [ ] **Required** [Confirm payments on the server](https://agentwranglers.se/playbook/adversarial-testing/abuse-and-limits/confirm-payment-server-side): Mark an order as paid only after the payment provider confirms it through a signed webhook or its API. Never trust the return to your success page.
- [ ] **Recommended** [Stop spam and fake sign-ups](https://agentwranglers.se/playbook/adversarial-testing/abuse-and-limits/form-spam): Make forms costly for bots and cheap for people. Never let a form send unlimited email to addresses the visitor types in.
- [ ] **Recommended** [Protect free tiers from automated abuse](https://agentwranglers.se/playbook/adversarial-testing/abuse-and-limits/free-tier-abuse): Limit what one person can take from a free plan or trial, so scripts cannot create endless accounts to get free use.

## Supply chain and setup

- [ ] **Required** [Lock and audit dependencies](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/audit-dependencies): Commit a lock file, install from it exactly, and fail the build on known holes in any dependency.
- [ ] **Required** [Review every new package](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/review-new-packages): Check every new dependency before adding it. Attackers publish packages with names close to popular ones, and take over abandoned ones.
- [ ] **Required** [Load third-party scripts with integrity checks](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/third-party-scripts): Host scripts yourself unless the provider requires its own server, such as a payment script. When a script must come from another server, pin its version and add an integrity hash.
- [ ] **Required** [Keep secrets out of builds from strangers](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/ci-secrets): Never give deploy keys or other secrets to builds that run code from outside contributors. Give each build job only the access it needs.
- [ ] **Required** [Remove defaults and samples](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/remove-defaults): Change every default password and remove sample apps, admin consoles and test pages before going live.
- [ ] **Required** [Keep software current and quiet](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/patch-and-hide-versions): Install security updates for the system, server and runtime within 7 days for critical or exploited holes, and within 30 days for the rest. Don't tell visitors which versions you run.
- [ ] **Required** [Log attacks without personal data](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/security-logging): Log failed logins, refused access and rate limit hits, and alert on spikes. Keep personal data out of the log.
- [ ] **Required** [Send the security headers](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/security-headers): Send the browser security headers on every page, including a strict Content Security Policy, HSTS and `nosniff`.
- [ ] **Required** [Remove DNS records you no longer use](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/dangling-dns): Delete every DNS record that points at a server, bucket or hosted service you no longer own, before you give that resource up.
- [ ] **Recommended** [Plan attacks before building](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/threat-model): Before building a feature, write down what an attacker would want from it and how they could get it. Then design those paths away.
- [ ] **Recommended** [Publish what is in each release](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/sbom-and-provenance): Make a software bill of materials for every release, and sign where and how the build was made.
- [ ] **Recommended** [Wait a few days before taking new releases](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/release-cooldown): Install a new version of a dependency only after it has been public for at least 7 days. Security fixes you need now may skip the wait.

## AI agents and models

- [ ] **Required** [Resist direct prompt injection](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/direct-prompt-injection): Assume any user can talk your model into ignoring its instructions. Enforce every limit in code outside the model, never only in the prompt.
- [ ] **Required** [Resist indirect prompt injection](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/indirect-prompt-injection): Treat every page, file, email and tool result the model reads as data that may contain hostile instructions.
- [ ] **Required** [Give agent tools the least power they need](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/least-privilege-tools): Give the model only the tools its task needs, each with the narrowest rights, acting as the user and never as an administrator.
- [ ] **Required** [Ask a person before the agent spends or deletes](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/human-approval): Require a clear yes from the user before the agent pays, sends a message, deletes data or changes an account.
- [ ] **Required** [Treat model output as untrusted](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/untrusted-model-output): Handle every model answer like text from a stranger. Escape it before showing it, and never run it as code, SQL or a shell command.
- [ ] **Required** [Keep secrets out of the system prompt](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/no-secrets-in-prompt): Write the system prompt as if it will be published. It holds no keys, passwords, internal addresses or private rules.
- [ ] **Required** [Scope what the model can look up to the user](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/scoped-retrieval): Let the model find only the documents and records the current user may read. Never search one shared index across all customers.
- [ ] **Required** [Allow only reviewed tools and servers](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/pinned-tools): Connect your agent only to tools and MCP servers you have reviewed. Pin their versions and descriptions so they cannot change without review.
- [ ] **Required** [Limit cost, steps and loops](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/cost-and-loop-limits): Cap the tokens per answer, the steps per task and the spend per user per day. Stop the agent when any cap is hit.
- [ ] **Required** [Break the lethal trifecta](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/lethal-trifecta): Never give one agent all three of private data, untrusted content and a way to send data out. Remove at least one.
- [ ] **Required** [Accept only tokens issued for your MCP server](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/mcp-token-audience): An MCP server you run accepts only access tokens issued for it. It never passes a client's token on to other APIs.
- [ ] **Required** [Use models and data from trusted sources](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/trusted-model-sources): Load models, adapters and training or search data only from sources you have reviewed, pinned by version or hash.
- [ ] **Required** [Check every model answer against a schema](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/validate-model-output): Parse every model answer your code acts on against a schema before you use it. Reject, retry or fall back when it does not match.
- [ ] **Required** [Keep a switch that turns the agent off](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/agent-kill-switch): Be able to switch off an agent, or one of its tools, for every user at once and without a deploy.
- [ ] **Required** [Run code the model writes in a sandbox](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/sandbox-model-code): Run code or commands a model writes only in a throwaway sandbox. It has no secrets, no network and fixed limits on time, memory and disk.
- [ ] **Recommended** [Protect agent memory](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/memory-poisoning): Save to long-term memory only what the user said or approved, and keep each user's memory apart.
- [ ] **Recommended** [Ground answers in your own data](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/grounded-answers): Let the model state prices, policies and account facts only from your own tools and documents, and show where each came from.
- [ ] **Recommended** [Send models only the private data they need](https://agentwranglers.se/playbook/adversarial-testing/ai-agents-and-models/private-data-to-models): Send a model provider only the personal data a task needs, under terms that forbid training on it.

## Hostile agents and bots

- [ ] **Required** [Verify who an agent is](https://agentwranglers.se/playbook/adversarial-testing/hostile-agents-and-bots/verify-agent-identity): Never trust the user agent string. Give special access to a bot or agent only after it proves who it is.
- [ ] **Required** [Log agent actions and let users undo them](https://agentwranglers.se/playbook/adversarial-testing/hostile-agents-and-bots/reversible-agent-actions): Record which agent did what for which user, and make its changes easy to undo.
- [ ] **Recommended** [Limit bulk reads and scraping](https://agentwranglers.se/playbook/adversarial-testing/hostile-agents-and-bots/limit-bulk-reads): Cap how fast one client can page through your data and how much it can read in a day.
- [ ] **Recommended** [Mark user content that agents read](https://agentwranglers.se/playbook/adversarial-testing/hostile-agents-and-bots/mark-user-content): Keep text written by your users apart from your own content, so other people's agents can tell it is not yours.
