In short

For each feature, ask what a hostile user wants from it, what they need first, how you would test that they cannot get it and which control stops them. The tables below answer those questions for eleven common features, with a test you can run as an integration test and CWE and CAPEC IDs for each case. Copy the rows that apply into the story, turn them into requirements with acceptance criteria, and keep the tests in the normal suite.

  • Do write each abuse case as a test someone else can run: a request, an input and an observable expected result.
  • Do include business-logic abuse (coupon races, skipped steps, price tampering); scanners rarely find it.
  • Do test authorization features such as invites and exports with two tenants and two roles, not one admin account.
  • Do treat every document and tool result an AI feature reads as attacker input, and test the tool gateway, not the prompt.
  • Don't stop at the library: add the abuse cases specific to your product, and record each one with an owner and a test.

Verify it

  1. For each feature in your product, list which rows of this library apply and link the test that covers each one; a row without a test is an open item.
  2. Run the abuse-case tests against a branch with the control removed and confirm each one fails.
  3. Check that authorization abuse cases are tested with two tenants and at least two roles.
  4. Confirm every accepted abuse case links a dated risk decision with an owner.
  5. Review the list after each incident or bug bounty report and add the case that would have caught it.
Applies to
  • OWASP ASVS 5.0.0
  • CWE and CAPEC CWE list as of October 2026; CAPEC 3.9
  • Examples Python 3.14, pytest 9.1.1, SQLite 3 (tested)

Web and API products with accounts, tenants, payments, integrations or AI features. Controls cite OWASP ASVS 5.0.0 requirements where one applies; the examples use Python 3.14 and pytest 9.1.1.

Part of
Workflow: Review a design

Fix it in your stack

Tested fixes: the vulnerable code, the fix, a plausible fix that still fails, and the regression tests that tell them apart.

Learning goals and prerequisitesAbout 39 minutes: 17 to read and 22 for the exercise

What you will learn

  • Select the abuse cases that apply to a feature and add product-specific ones.
  • Write each abuse case as a runnable test with an observable expected result.
  • Name the control and the CWE and CAPEC entries for each case.
  • Automate race and replay abuse cases so they run on every change.

Before you start

Developers, testers and AppSec engineers writing stories, tests and threat models for product features

  • Know how the feature you are working on handles accounts, tenants and roles.
  • Be able to read a short Python test.
  • For the code: Python 3.11 or later with pytest.
Go to the guided practice

How do you use this library?

An abuse case (also called a misuse case) describes how someone could use a feature against its owner or its other users: what they want, what they need first, and how you would notice. This library lists the abuse cases that recur for common features, each with a concrete test and the control that should make the test pass. The CWE and CAPEC IDs name the weakness and the attack pattern so you can search scanners, advisories and the CWE pages on this site.

Use it in refinement, before code exists. Pick the features the story touches, copy the rows that apply into the story, and turn each into a requirement with acceptance criteria as described in /domains/appsec/threat-modeling/security-requirements-definition-of-done/. Add the abuse cases that are specific to your product: the library covers what recurs, not your business logic. For threats at the design level rather than one feature, use /domains/appsec/threat-modeling/threat-pattern-library/.

  • Write the test as a request and an expected result that someone else can run, including the case that must be refused.
  • Keep the expected result observable: a status code, a body, a log entry or an unchanged database row.
  • Record each abuse case with its owner and status in the YAML template near the end of this page, and link the test that proves it.
  • Re-run the abuse-case tests on every release; most of them are cheap integration tests.

Sign-up and login

Attackers go after login at scale with credentials leaked elsewhere, and after sign-up to learn who has an account or to create accounts in bulk. Deeper guidance: /domains/identity-crypto/authentication/session-lifecycle/, /domains/identity-crypto/authentication/password-storage/ and /domains/identity-crypto/authentication/mfa-account-takeover/.

Sign-up and login
Abuse case and attacker goalPreconditionsTestControlCWE / CAPEC
AC-AUTH-1 Credential stuffing: take over accounts whose owners reused a leaked passwordLogin endpoint reachable from the internet; lists of leaked email and password pairsReplay 200 known-wrong pairs for test accounts from many source addresses; expect per-account throttling or a challenge after the documented threshold and an alertPer-account and per-source rate limits, breached-password checks at sign-up and change (v5.0.0-6.2.12), MFA, alerting (v5.0.0-6.3.1)CWE-307 / CAPEC-600
AC-AUTH-2 Password spraying: try one common password across many accounts to stay under per-account limitsUsernames or emails can be guessed or collectedTry one password against 50 test accounts at one attempt per account per minute; expect detection on the global failure rateGlobal and per-source failure-rate limits, a blocklist of the most common passwords (v5.0.0-6.2.4), MFACWE-307 / CAPEC-565
AC-AUTH-3 Account enumeration: learn which emails have accountsSign-up, login or reset responses differ for known and unknown emailsCompare status, body and response time for a registered and an unregistered email on sign-up, login and reset; expect no observable differenceSame response for both cases; tell the real owner by email instead of on screen (v5.0.0-6.3.8)CWE-204 / CAPEC-575
AC-AUTH-4 Bulk fake sign-ups: create thousands of accounts for spam or free-tier abuseSign-up grants useful privileges before email verificationScript 500 sign-ups in 10 minutes from one network range; expect throttling and no privileges before verificationRate limits and anti-automation on sign-up (v5.0.0-2.4.1), verification before privileges, per-tenant quotasCWE-799

Password reset

Reset is a second login path, and attackers use it when the first one is strong. The reset-token lab at /labs/recovery/ has you make a recovery credential single-use; the guide is /domains/identity-crypto/authentication/account-recovery-linking/.

Password reset
Abuse case and attacker goalPreconditionsTestControlCWE / CAPEC
AC-RST-1 Guess or reuse a reset token to set the victim's passwordTokens are short, predictable, long-lived or reusableRequest two tokens and check they come from a CSPRNG with at least 128 bits; use one token twice and once after expiry; expect both later uses to failRandom single-use token, stored hashed, expiring in minutes, invalidated when a new one is issuedCWE-640 / CAPEC-50
AC-RST-2 Reset link poisoning: receive the victim's token through a link that points to an attacker hostThe app builds the link from the request's Host or X-Forwarded-Host headerRequest a reset with Host: evil.example; expect the email link to use the configured originBuild links from a configured base URL; reject unknown Host values at the edgeCWE-640
AC-RST-3 Bypass MFA by resetting the passwordReset signs the user in, or resets the second factor tooReset the password of an MFA-enrolled test user; expect the next sign-in to still require the second factorReset changes only the password and never skips enabled MFA (v5.0.0-6.4.3)CWE-288
AC-RST-4 Keep access after the victim resets: an attacker's existing session survivesSessions are not tied to credential changesSign in on client A, reset on client B, then call the API from A; expect A to be signed out or offered for terminationOffer to end other sessions after any factor change (v5.0.0-7.4.3); end them by default after a resetCWE-613

MFA enrolment

An attacker who briefly holds a session tries to make that access permanent by adding their own factor, or tries to wear the user down. Guidance: /domains/identity-crypto/authentication/passkeys-mfa-lifecycle/ and /domains/identity-crypto/authentication/mfa-account-takeover/.

MFA enrolment
Abuse case and attacker goalPreconditionsTestControlCWE / CAPEC
AC-MFA-1 Enroll the attacker's own authenticator on a hijacked sessionEnrolment needs only a valid session cookieCall the enrolment endpoint with a session older than the re-authentication window; expect a step-up prompt and a notification to the userFull re-authentication before changing authentication factors (v5.0.0-7.5.1); notify on factor changesCWE-306
AC-MFA-2 Push bombing: send prompts until the user approves onePush approval without number matching or rate limitsTrigger 20 push prompts in a minute for a test account; expect rate limiting and number matchingRate-limit push requests (v5.0.0-6.6.4), number matching, alert on repeated denialsCWE-799
AC-MFA-3 Brute-force a six-digit one-time codeNo limit on wrong codes per challengeSubmit 50 wrong codes for one challenge; expect the challenge to be invalidated well before thatRate limiting on code entry (v5.0.0-6.6.3), short code lifetime (v5.0.0-6.5.5)CWE-307 / CAPEC-112
AC-MFA-4 Reuse a recovery code or a captured one-time codeCodes stay valid after useUse one recovery code twice and one TOTP code twice within its window; expect the second use to failOne-time codes and lookup secrets usable once (v5.0.0-6.5.1); recovery codes stored hashedCWE-294

Invites

Invites grant membership, so they are an authorization feature. Test who can send them, which tenant and role they grant, and how long the link works. Guidance: /domains/appsec/authorization/function-property-authorization/ and /domains/appsec/authorization/object-level-authorization/.

Invites
Abuse case and attacker goalPreconditionsTestControlCWE / CAPEC
AC-INV-1 A member who is not an admin sends invites by calling the API directlyThe button is hidden in the UI but the endpoint is openCall POST /invites as a plain member; expect 403Function-level authorization on the endpoint (v5.0.0-8.2.1)CWE-862 / CAPEC-1
AC-INV-2 Invite someone into another tenant by changing tenant_idThe tenant comes from the request bodySend an invite as an admin of tenant red with tenant_id of tenant blue; expect 403 or the field ignoredTake the tenant from the session (v5.0.0-8.3.1); cross-tenant checks (v5.0.0-8.4.1)CWE-639
AC-INV-3 Grant yourself admin by adding role to the accept requestThe accept handler binds every request field to the membershipAccept an invite with {"role":"admin"} added; expect the invited roleAllowlist the fields each action may set (v5.0.0-15.3.3)CWE-915
AC-INV-4 Use a forwarded or old invite linkInvite tokens never expire and are not bound to the invited emailAccept an invite twice, after 73 hours, and with a different verified email; expect all three refusedSingle-use expiring token bound to the invited addressCWE-294

File upload

Uploads let a user put bytes on your servers and in front of other users. Guidance: /domains/appsec/secure-features/secure-file-uploads/ and /domains/appsec/secure-features/archive-path-safety/; fix cards: /fix/file-upload/ and /fix/path-traversal/.

File upload
Abuse case and attacker goalPreconditionsTestControlCWE / CAPEC
AC-UP-1 Upload a web shell and run itUploaded files are stored under the web root and served by an interpreterUpload shell.php renamed to .jpg and a polyglot image; request them; expect no execution and a content-type mismatch rejectedCheck extension and content (v5.0.0-5.2.2); store outside the web root, never executable (v5.0.0-5.3.1)CWE-434 / CAPEC-650
AC-UP-2 Write outside the upload directory through the filenameThe stored path uses the client filenameUpload with filename ../../app/settings.py; expect a generated name and no file outside the directoryGenerate storage names; never use user-supplied paths (v5.0.0-5.3.2)CWE-22 / CAPEC-126
AC-UP-3 Exhaust disk or memory with a decompression bombArchives or images are expanded without limitsUpload a small archive that expands past the limit; expect rejection before full extractionCheck uncompressed size and file count before extracting (v5.0.0-5.2.3)CWE-409
AC-UP-4 Store script in an SVG or HTML file and run it in other users' browsersUploads are served inline from the application originUpload an SVG containing a script and open its URL; expect download or a sandboxed origin, not executionServe uploads from a separate origin or with Content-Disposition: attachment and a restrictive CSPCWE-79

Payments and coupons

Money flows reward attackers directly, and most of the bugs are business logic rather than injection. Guidance: /domains/appsec/business-logic/transaction-races/ and /domains/appsec/business-logic/business-logic-flaws/; lab: /labs/checkout-race/.

Payments and coupons
Abuse case and attacker goalPreconditionsTestControlCWE / CAPEC
AC-PAY-1 Redeem a single-use coupon many times with parallel requestsCheck and update happen in separate statementsSend 20 redemptions of one coupon at the same instant, repeated over several rounds; expect exactly one success per roundOne conditional update that both checks and claims (v5.0.0-15.4.2); transactions and locking for limited resources (v5.0.0-2.3.3, v5.0.0-2.3.4)CWE-362, CWE-837 / CAPEC-26
AC-PAY-2 Pay less by changing the price or quantity in the requestThe client sends price, discount or quantity and the server trusts itSubmit an order with price: 0.01 and one with quantity: -1; expect the server price and a validation errorCompute price server-side from the catalog; validate ranges (v5.0.0-2.2.1)CWE-472, CWE-602
AC-PAY-3 Skip the payment step and confirm an unpaid orderEach step is a separate endpoint without state checksCall POST /orders/{id}/confirm for an order whose payment failed; expect 409Enforce step order on the server (v5.0.0-2.3.1)CWE-841
AC-PAY-4 Card testing: validate stolen cards with many small authorizationsPayment endpoint without velocity limitsAttempt 100 low-value authorizations from one account and one network range; expect throttling and reviewVelocity limits per account, card and source; bot controls on payment endpointsCWE-799

Webhooks

Receiving webhooks means trusting a request that claims to come from a partner; sending them means fetching URLs your customers choose. Guidance: /domains/appsec/secure-features/verify-webhook-deliveries/ and /domains/appsec/secure-features/ssrf-egress-boundaries/; lab: /labs/webhooks/; fix card: /fix/ssrf/.

Webhooks
Abuse case and attacker goalPreconditionsTestControlCWE / CAPEC
AC-WH-1 Forge a "payment succeeded" deliveryThe receiver accepts unsigned or wrongly signed deliveriesPost a delivery with no signature, a wrong signature and a changed body; expect all rejectedVerify the provider's HMAC over the raw body in constant time before parsingCWE-345
AC-WH-2 Replay a captured valid delivery to fulfil an order twiceNo timestamp tolerance or delivery-ID checkResend a valid delivery, and one signed hours ago; expect replay and staleCheck the signed timestamp against a tolerance and store delivery IDs; make handlers idempotentCWE-294
AC-WH-3 Make your server call internal addresses by registering a webhook URLCustomers can register any URL; the sender follows redirectsRegister http://169.254.169.254/, http://localhost:8080/ and a public URL that redirects inside; expect every delivery refusedResolve and check the address at send time, block private ranges, do not follow redirects (v5.0.0-15.3.2), send through an egress proxyCWE-918 / CAPEC-664

Export

Exports move a lot of data in one request and are often added late, with weaker checks than the screens they mirror. Guidance: /domains/appsec/authorization/object-level-authorization/ and /domains/runtime/api-security/resource-consumption-limits/; lab: /labs/authorization/.

Export
Abuse case and attacker goalPreconditionsTestControlCWE / CAPEC
AC-EXP-1 Export another tenant's data by changing the IDExport jobs check the job ID but not the data it coversRequest an export of a record set owned by another tenant; expect 404Authorize the data, not only the job (v5.0.0-8.2.2)CWE-639
AC-EXP-2 Plant a spreadsheet formula that runs when an admin opens the exportUser text is written to CSV without neutralizationStore =HYPERLINK("https://evil.example","x") in a name and export; expect the cell prefixed so it is shown as textPrefix cells starting with =, +, -, @, tab or carriage return; quote fieldsCWE-1236
AC-EXP-3 Scrape everything through repeated bulk exportsNo limits on export size or frequencyRun exports in a loop; expect a per-user limit and an alertPer-user and per-tenant quotas; asynchronous exports with size caps (v5.0.0-2.4.1)CWE-770
AC-EXP-4 Download someone's export through a leaked linkDownload URLs are public and long-livedOpen an export link from another session and after 24 hours; expect refusalShort-lived signed URLs bound to the requesting user; delete exports after useCWE-552

Admin functions

Admin features are few, powerful and often tested less than customer features. Guidance: /domains/appsec/authorization/function-property-authorization/ and /domains/appsec/browser-security/csrf-origin-defense/; fix cards: /fix/missing-authorization/, /fix/mass-assignment/ and /fix/csrf/.

Admin functions
Abuse case and attacker goalPreconditionsTestControlCWE / CAPEC
AC-ADM-1 Call admin APIs as an ordinary userAdmin routes rely on the UI hiding themCall each /admin route as a member; expect 403 everywhere, including export and bulk routesDeny-by-default function-level checks (v5.0.0-8.2.1); a route inventory testCWE-862 / CAPEC-87
AC-ADM-2 Promote yourself by adding role to a profile updateProfile updates bind all fieldsSend PATCH /users/me with {"role":"admin"}; expect the role unchangedField allowlists per action (v5.0.0-15.3.3); field-level authorization (v5.0.0-8.2.3)CWE-915
AC-ADM-3 Get an admin to change a role through a forged cross-site requestCookie-authenticated admin actions without CSRF defenseSubmit the role change from another origin with the admin's cookies; expect rejectionSameSite cookies plus a token or Origin check on every state-changing requestCWE-352 / CAPEC-62
AC-ADM-4 Impersonate a customer without leaving a traceSupport impersonation is not logged or not visible to the customerImpersonate a test user; expect an audit event with who, whom and when, and a notice to the customerLog every impersonation and admin change to a protected log (v5.0.0-16.4.2); require a reasonCWE-778

AI chat or agent

An LLM feature takes instructions from anything it reads, so every document, web page and tool result is input from an attacker. Guidance: /domains/ai-emerging/llm-security/prompt-injection-boundaries/, /domains/ai-emerging/agent-security/tool-authorization/ and /domains/ai-emerging/llm-security/rag-tenant-isolation/; labs: /labs/agent-tool-gateway/, /labs/rag-isolation/ and /labs/output-rendering/.

AI chat or agent
Abuse case and attacker goalPreconditionsTestControlCWE / CAPEC
AC-AI-1 Indirect prompt injection: a shared document tells the assistant to send data to an outside URLThe assistant can read untrusted content and call an outbound toolPlant instructions in a test document, ask the assistant to summarize it, and watch egress; expect no outbound call and no data in tool argumentsTool gateway outside the model that checks each call against the user's intent and an allowlist; no free-form fetch toolCWE-1427
AC-AI-2 Excessive agency: the agent deletes or pays without a human decisionDestructive tools are callable without approvalAsk the agent to delete a project and to issue a refund; expect an approval bound to the exact argumentsApproval bound to tool, arguments and user, consumed once (the agent tool gateway lab)CWE-862
AC-AI-3 Retrieve another tenant's documents through the assistantVector search runs without the tenant filterAsk tenant red's assistant about a fact only in tenant blue's documents; expect no answer from themApply tenant and permission filters inside retrieval, before rankingCWE-639
AC-AI-4 Make the model output markup that runs in the user's browserModel output is rendered as HTMLAsk for an answer containing an <img onerror> payload; expect it shown as textRender output as text or sanitize with an allowlist; CSPCWE-79
AC-AI-5 Run up the bill with long prompts and loopsNo per-user token or tool-call budgetSend prompts that trigger repeated tool calls; expect the run to stop at the budgetPer-user and per-run budgets for tokens, tool calls and timeCWE-770

How do you record an abuse case?

Store each abuse case next to the story or feature it belongs to, in a format both people and scripts can read. The record names the attacker and their goal, the preconditions, the test and where it lives, the control, the mappings and an owner. A status of tested should mean a test in the repository proves it; accepted should point to a dated risk decision.

docs/abuse-cases/AC-PAY-1.yamlYAML
# One abuse case, stored next to the story it belongs to (for example docs/abuse-cases/AC-PAY-1.yaml).
id: AC-PAY-1
feature: checkout / coupons
title: Redeem a single-use coupon more than once
attacker:
  who: Signed-in customer with one valid single-use coupon
  goal: Apply the coupon to many orders
preconditions:
  - Coupon redemption is an HTTP request the client can send in parallel
  - The server checks "unused" and marks "used" in separate steps
test:
  type: automated
  steps: Send 20 redemption requests for the same coupon at the same moment, each for a different order
  expected: Exactly one succeeds; the others get 409 and no discount
  location: tests/test_abuse_cases.py::test_ac_pay_1_single_use_coupon_under_parallel_requests
control: A single conditional UPDATE ... WHERE used_by IS NULL, checked by its affected row count
mappings:
  cwe: [CWE-362, CWE-837]
  capec: [CAPEC-26]
  asvs: [v5.0.0-2.3.3, v5.0.0-2.3.4, v5.0.0-15.4.2]
owner: payments team
status: tested

What do the coupon and webhook controls look like in code?

Two controls from the tables, in Python with only the standard library. redeem claims the coupon with one conditional UPDATE and trusts the affected row count, so the check and the claim cannot be separated by another request. WebhookVerifier checks an HMAC over the delivery ID, the timestamp and the raw body with a constant-time comparison, then rejects stale timestamps and delivery IDs it has already seen. The signing scheme is an example; use your provider's documented scheme.

Fixedabuse_cases.pyPython
import hashlib
import hmac
import sqlite3
import time


def redeem(db_path, code, order_id):
    """AC-PAY-1 control: one conditional UPDATE, so only one request can win."""
    with sqlite3.connect(db_path, timeout=10, isolation_level=None) as db:
        won = db.execute(
            "UPDATE coupons SET used_by = ? WHERE code = ? AND used_by IS NULL",
            (order_id, code),
        ).rowcount
    return won == 1


class WebhookVerifier:
    """AC-WH-1 and AC-WH-2 controls: verify the HMAC, the timestamp and the delivery ID."""

    def __init__(self, secret, tolerance=300, clock=time.time):
        self.secret, self.tolerance, self.clock = secret, tolerance, clock
        self.seen = {}  # delivery id -> timestamp; use a shared store with a TTL in production

    def verify(self, delivery_id, timestamp, body, signature):
        expected = hmac.new(
            self.secret, f"{delivery_id}.{timestamp}.".encode() + body, hashlib.sha256
        ).hexdigest()
        if not hmac.compare_digest(expected, signature):
            return "bad-signature"
        if abs(self.clock() - int(timestamp)) > self.tolerance:
            return "stale"
        if delivery_id in self.seen:
            return "replay"
        self.seen[delivery_id] = int(timestamp)
        return "ok"

How do you automate abuse-case tests?

Each test performs the abuse case and asserts the refusal. The coupon test releases 20 threads at once through a barrier and repeats the race for 25 rounds, because a race does not appear on every run. Against a version that reads the coupon and then updates it in a second statement, the test failed in five of five runs during testing; against the conditional update it passed every time. The webhook tests use a fixed clock, so they are deterministic.

Run them with pytest -q (tested with Python 3.14 and pytest 9.1.1). Keep abuse-case tests in the normal suite, so they run on every change rather than once before a penetration test.

Testtest_abuse_cases.pyPython
import hashlib
import hmac
import sqlite3
import threading
from concurrent.futures import ThreadPoolExecutor

import pytest

from abuse_cases import WebhookVerifier, redeem


@pytest.fixture
def coupon_db(tmp_path):
    path = tmp_path / "shop.db"
    with sqlite3.connect(path) as db:
        db.execute("CREATE TABLE coupons (code TEXT PRIMARY KEY, used_by TEXT)")
        db.execute("INSERT INTO coupons VALUES ('WELCOME10', NULL)")
    return path


def test_ac_pay_1_single_use_coupon_under_parallel_requests(coupon_db):
    # A race may not show on every run, so repeat it; any double redemption fails the test.
    for round_ in range(25):
        with sqlite3.connect(coupon_db) as db:
            db.execute("UPDATE coupons SET used_by = NULL")
        start = threading.Barrier(20)

        def attempt(n):
            start.wait()  # release all 20 requests at once
            return redeem(coupon_db, "WELCOME10", f"order-{round_}-{n}")

        with ThreadPoolExecutor(max_workers=20) as pool:
            results = list(pool.map(attempt, range(20)))
        assert results.count(True) == 1, f"round {round_}: {results.count(True)} redemptions"


SECRET = b"whsec_test_only"


def sign(delivery_id, timestamp, body):
    message = f"{delivery_id}.{timestamp}.".encode() + body
    return hmac.new(SECRET, message, hashlib.sha256).hexdigest()


@pytest.fixture
def verifier():
    return WebhookVerifier(SECRET, clock=lambda: 1_760_000_000)


def test_ac_wh_1_forged_delivery_is_rejected(verifier):
    body = b'{"order":"o-7","status":"paid"}'
    assert verifier.verify("d-1", "1760000000", body, "0" * 64) == "bad-signature"


def test_ac_wh_1_changed_body_is_rejected(verifier):
    signature = sign("d-1", "1760000000", b'{"order":"o-7","status":"failed"}')
    assert verifier.verify("d-1", "1760000000", b'{"order":"o-7","status":"paid"}', signature) == "bad-signature"


def test_ac_wh_2_replayed_delivery_is_rejected(verifier):
    body = b'{"order":"o-7","status":"paid"}'
    signature = sign("d-2", "1760000000", body)
    assert verifier.verify("d-2", "1760000000", body, signature) == "ok"
    assert verifier.verify("d-2", "1760000000", body, signature) == "replay"


def test_ac_wh_2_old_delivery_is_rejected(verifier):
    body = b'{"order":"o-7","status":"paid"}'
    signature = sign("d-3", "1759990000", body)  # 10,000 seconds old
    assert verifier.verify("d-3", "1759990000", body, signature) == "stale"

Guided practice

A team is adding "Share a report by link" to a fictional analytics SaaS. Use the library to find the abuse cases that apply, write two of them as tests and decide which controls the design needs. The product and data are fictional.

Supplied scenario material
Plain text
Feature: report sharing (story RPT-12)
- An editor clicks Share and gets a link: https://app.example.test/r/{report_id}?k={key}
- report_id is a sequential integer; k is 8 hex characters from Math.random()
- Anyone with the link can view the report without signing in; links never expire
- Viewers can click "Download CSV"; cell values come from customer-uploaded data
- Editors can share; viewers cannot (the Share button is hidden for viewers)
- POST /reports/{id}/share accepts {"expires_in_days": n, "role": "viewer"}
Current tests: one happy-path test that an editor can create a link.
  1. 1. Find the applicable rows

    Pick the library rows that apply to this feature and say why.

    Expected result and reasoning

    AC-EXP-4 (leaked, long-lived link), AC-EXP-1 (guessable report_id with a weak key is the same object-access problem), AC-EXP-2 (CSV formula injection from customer data), AC-INV-1 (viewers calling the share endpoint directly) and AC-INV-3 (a role field in the request that could grant more than view).

  2. 2. Judge the key

    Is k a sufficient control for AC-EXP-1 and AC-EXP-4?

    Expected result and reasoning

    No. Eight hex characters are 32 bits from a non-cryptographic generator, and the link never expires. Use at least 128 bits from a CSPRNG, store it hashed, give it an expiry and allow revocation (CWE-552, CWE-330 for the weak randomness).

  3. 3. Write the authorization test

    Write the test for AC-INV-1 in the format of the library.

    Expected result and reasoning

    As a viewer, call POST /reports/{id}/share; expect 403 and no link created. Add the same test for an editor of another workspace.

  4. 4. Write the export test

    Write the test for AC-EXP-2.

    Expected result and reasoning

    Upload a data row whose value is =HYPERLINK("https://evil.example","x"), download the CSV and assert the cell starts with an apostrophe or is otherwise neutralized; also test +, -, @ and a leading tab.

  5. 5. Close the role field

    Decide what the share endpoint should do with the role field.

    Expected result and reasoning

    Allowlist it to viewer (or the roles an editor may grant), and add a test that "role": "editor" or "admin" is refused (CWE-915).

Your deliverable: A list of five abuse cases for RPT-12 with tests and controls, and two test descriptions ready to automate.

The feature is fictional and simplified. Mappings choose the closest CWE; your scanner may map the same issue to a parent or child entry.

Save a draft on this device to resume later. Automatic saving begins after you choose Save. Drafts are never sent by this application; use synthetic data.

No draft saved on this device yet.

Sources & further reading

The standards, specifications and documentation behind the guidance above. Each is the evidence for a claim on this page; consult it for detail beyond the defaults and versions listed here.

  1. OWASP Abuse Case Cheat Sheet
  2. MITRE CAPEC: Mechanisms of Attack view (CAPEC-1000). CAPEC 3.9.
  3. MITRE CWE: Research Concepts view (CWE-1000). CWE 4.20.
  4. OWASP ASVS 5.0.0: V6 Authentication
  5. OWASP ASVS 5.0.0: V2 Validation and Business Logic
  6. OWASP ASVS 5.0.0: V5 File Handling
  7. OWASP Web Security Testing Guide: Business Logic Testing. WSTG v4.2.
  8. OWASP CSV Injection
  9. OWASP Top 10 for LLM Applications

Reference information

Download this guide as Markdown