vuln: email route lacks the ownership check the read route has

code:
def get_invoice(user, invoice_id):
    inv = db.get_invoice(invoice_id)
    if inv.owner_id != user.id:
        raise Forbidden()
    return inv

def email_invoice(user, invoice_id, destination):
    inv = db.get_invoice(invoice_id)
    mailer.send(destination, inv.contents)

must_hit:
owner
ownership
authoriz
permission
access control
tenant

must_hit: missing no not lacks without fails absent never

capability:
another user
other user
any invoice
not own
does not own
arbitrary
other tenant
someone else

fix: check verify validate enforce policy scope same

distractors:
index
performance
efficien
slow query


vuln: role assigned from client-controlled payload (privilege escalation)

code:
def update_profile(request, current_user):
    payload = request.json
    current_user.display_name = payload["display_name"]
    current_user.role = payload["role"]
    db.save(current_user)

must_hit:
privileg
escalat
admin
authority
elevat
mass assign
mass-assign

capability:
any role
become admin
administrator
elevate
escalate
grant themselves
set themselves

fix:
allowlist
whitelist
separate
remove
do not
should not
only admin
exclude
never

distractors: sql injection xss


vuln: stored user URL later fetched server-side (SSRF)

code:
def set_webhook(user, url):
    user.webhook_url = url
    db.save(user)

def notify(user, event):
    http.post(user.webhook_url, json=event)

must_hit:
ssrf
server-side request
server side request
internal
metadata
loopback
localhost
private
arbitrary url
attacker-controlled url
attacker controlled url
outbound request
unvalidated url
url is fetched

capability:
internal
metadata
169.254
private network
scan
reach

fix: allowlist whitelist validate scheme block deny restrict

distractors: xss sql


vuln: validation runs before decoding, so traversal appears after (path traversal)

code:
def save_file(filename, data):
    if ".." in filename:
        raise ValueError("invalid")
    decoded = unquote(filename)
    path = UPLOAD_DIR / decoded
    path.write_bytes(data)

must_hit: traversal .. directory escape outside

capability:
arbitrary
overwrite
outside
escape
any file
system file

fix:
canonical
resolve
realpath
after decod
normaliz
confine
validate the decoded


vuln: amount mutable after approval; execution trusts stale status

domain: general

code:
def approve(p): p.status = "approved"; save(p)
def edit(p, amount): p.amount = amount; save(p)
def execute(p):
    if p.status == "approved":
        provider.transfer(p.amount)

must_hit:
after approv
approved amount
changed after
edit
mutat
snapshot
immutab

capability:
arbitrary amount
larger
any amount
more than
increase
different amount

fix: freeze immutab snapshot invalidate re-approv reauthor version

distractors: sql xss


vuln: reset token never marked used (reusable)

code:
def reset_password(token_value, new_password):
    token = db.get_reset_token(token_value)
    if token.expires_at < now():
        raise InvalidToken()
    user = db.get_user(token.user_id)
    user.password_hash = hash_password(new_password)
    save(user)

must_hit:
reus
once
single use
single-use
consum
invalidat
not marked
remains valid
multiple times
again
double-use
double use
twice
re-use
reused
not consumed
double use of token

capability:
again
repeatedly
multiple
takeover
reset again
future

fix:
consume
mark
invalidat
delete
single use
single-use
transaction


vuln: check-then-act on stock is not atomic (race / oversell)

code:
def purchase(product_id, customer):
    p = db.get_product(product_id)
    if p.stock <= 0:
        raise OutOfStock()
    charge(customer, p.price)
    p.stock -= 1
    db.save(p)

must_hit:
race
concurren
atomic
toctou
simultaneous
time-of-check
time of check

capability:
oversell
oversold
negative
two
double
more than
twice

fix:
atomic
lock
transaction
constraint
compare-and-swap
conditional update
where stock

distractors: xss injection


vuln: identity taken from a client-suppliable header

code:
def current_user(request):
    user_id = request.headers["X-User-ID"]
    return db.get_user(user_id)

must_hit: forge spoof supply client arbitrary trust impersonat tamper

capability:
any user
impersonat
other user
admin
act as

fix:
gateway
strip
sign
verify
session
token
authenticat
not trust
should not


vuln: worker trusts mutable amount/id from the queue message

code:
def process_refund(message):
    provider.refund(payment_id=message["payment_id"],
                    amount=Decimal(message["amount"]))

must_hit: trust authoritative message queue validat verify

must_hit: amount payment_id state

capability:
arbitrary
tamper
replay
inject
forge
duplicate
any amount

fix:
load
authoritative
database
verify
validate
operation id
idempot
re-check
recheck


vuln: only balance>=amount checked; negative amount inverts the transfer

code:
def transfer(source, recipient, amount):
    if source.balance < amount:
        raise ValueError("insufficient funds")
    source.balance -= amount
    recipient.balance += amount

must_hit:
negative
less than zero
below zero
non-positive
must be positive
not positive
positive

capability:
increase
gain
steal
drain
inflate
own balance
victim

fix:
positive
greater than zero
> 0
>0
amount <= 0
validate


vuln: authorization fails open on timeout

code:
def may_access(user, resource):
    try:
        return authz_service.check(user.id, resource.id)
    except TimeoutError:
        return True

must_hit:
fail open
fail-open
returns true
grant
allow
open

capability:
unauthorized
any user
bypass
all access
everyone

fix:
fail closed
fail-closed
deny
false
cache
degrade

distractors:
performance
latency only


vuln: secret compared with non-constant-time equality

domain: cryptography

code:
def check_api_key(provided):
    expected = load_expected_key()
    if provided == expected:
        return True
    return False

must_hit:
timing
constant time
constant-time
side channel
side-channel
byte by byte
early return

capability: recover guess brute infer leak extract


vuln: support route issues a refund bypassing the controlled workflow

code:
@app.post("/orders/<oid>/refund")
@finance_required
def refund_order(oid):
    return refund_service.refund(oid)

@app.post("/support/orders/<oid>/resolve")
@support_required
def resolve_order(oid):
    o = db.get_order(oid)
    if o.customer_complaint:
        provider.refund(o.payment_id)
    o.status = "resolved"; save(o)

must_hit:
support
second
alternate
another path
bypass
duplicate
two routes
separate route
bypasses

capability:
twice
duplicate
multiple
repeatedly
without finance
lower privileg
bypass

fix:
single
one workflow
shared
centraliz
same service
idempot
state check


vuln: delete route authenticates but does not authorize the object

code:
@app.delete("/files/<fid>")
@login_required
def delete_file(fid):
    db.delete(db.get_file(fid))
    return {"ok": True}

must_hit:
owner
ownership
authoriz
permission
idor
object-level
object level
tenant

must_hit:
missing
no
not
lacks
without
only auth
authenticat

capability:
any file
another user
other user
arbitrary
not own
anyone

fix: check verify scope policy enforce

distractors: index performance