Desktop
Investigation modules#
Use these procedures with this playbook's setup and scope. Each contains the full testing sequence, command cheat sheet, interpretation, and checklist; open its canonical page for cross-target references.
Object, function, and property authorizationTurn identities, tenants, objects, roles, and lifecycle transitions into a reproducible permission matrix.
Open canonical module and reverse references
- 01Establish permission rule
- 02Seed two controlled identities
- 03Replay one changed dimension
- 04Verify durable effect
- 05Revoke and repeat
- 06Record smallest violation
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant
Use accounts A and B, two private tenants, and one synthetic document per tenant. Create a viewer role only if the product supports it. Record the exact build, API version, actor, tenant membership, document visibility, and expected permission rule. The invariant is: the server authorizes the actor, action, object, and each protected property together. UI visibility is supporting context; product policy establishes the boundary. Horizontal, vertical, and context-dependent authorization are separate dimensions. Access-control models.
Numbered test procedure
- Capture a working baseline. As A, create private O1 containing
BB-A-ONLY-01; as B create O2 containingBB-B-ONLY-01. Save each normal read response and a normal reversible rename. Keep separate browser profiles and credentials. Confirm anonymous access to O1 fails. - Test the object dimension. Replay A's O1 read using B's credentials, changing nothing else. Then keep B's identity and replace O1 with O2 as a positive control. Substitute only known controlled identifiers, including parent tenant IDs, child IDs, attachment IDs, and export-job IDs. A nonexistent synthetic ID is the negative control for error handling; it does not replace the cross-account test.
- Test the function dimension. Give B a documented viewer role in A's test tenant. Capture an owner-only operation as A, such as renaming a private test folder, and replay it as B. If the product permits viewer renames, select an actually restricted operation. Inspect durable state from A's browser after the request completes.
- Test the property dimension. Start with a permitted profile update. Add exactly one known server-controlled property from schema or source, such as a role field in the isolated tenant. Read back the stored object, then test one corresponding restricted action. Repeat separately for unauthorized response properties. Automatic binding of request fields can create mass-assignment issues; read-side property disclosure is a different mechanism. OWASP mass assignment.
- Test lifecycle and alternate delivery. Remove B's membership, replay the previously successful request, and check an existing subscription or queued export. Repeat the same permission rule at list/search, download, batch, and versioned endpoints where present. Use two-item batches containing O1 and O2; do not enumerate identifiers.
- Interpret and clean up. Match markers, object IDs, committed changes, and resulting permissions. A 200 with an empty list, a 202 for a rejected job, or a redirect without private content is not proof. Remove synthetic records and memberships after preserving minimal evidence.
Command card
These illustrative routes require your local replica. Create A.header and B.header containing each test account's complete Authorization: Bearer … header; protect the files and exclude them from reports. curl can load headers from a file. curl manual.
mkdir -p evidence/authz
chmod 600 A.header B.header
curl -q --noproxy '*' --max-time 10 --header @A.header \
-D evidence/authz/a.h -o evidence/authz/a.json \
http://127.0.0.1:8000/api/documents/O1
curl -q --noproxy '*' --max-time 10 --header @B.header \
-D evidence/authz/b.h -o evidence/authz/b.json \
http://127.0.0.1:8000/api/documents/O1
curl -q --noproxy '*' --max-time 10 --header @B.header \
-o evidence/authz/control.json \
http://127.0.0.1:8000/api/documents/O2
Report threshold and checklist
Report a demonstrated unauthorized read, action, or protected-property change. Attach the intended rule, both actors' roles, minimal request difference, and independent state verification. UUID disclosure, shared objects, intentionally public exports, stale UI state, and identical status codes are common false positives.
- Ownership and visibility confirmed before testing.
- Object, function, and property decisions tested separately.
- Positive and negative controls captured.
- Background output and revoked membership checked.
- No unrelated customer records accessed.
Local curl examples use --noproxy '*' to bypass inherited proxy variables; -q alone only disables the default curl configuration file. Configure intentional interception separately. curl manual.
Authentication, recovery, federation, and session lifecycleTest registration, recovery, identity changes, invitations, SSO enforcement, and deprovisioning as one account state machine.
Open canonical module and reverse references
- 01Map identity states
- 02Capture valid and invalid controls
- 03Alter one binding
- 04Verify account identity
- 05Test revocation
- 06Restore account settings
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant
Prepare two controlled accounts, separate inboxes, browser profiles, and a recovery route for each. Use an isolated identity provider for federation modifications. Write the accepted identity states: unverified, password accepted, MFA pending, fully authenticated, elevated, recovered, and revoked. The invariant is: each privileged transition requires the right identity proof, and credentials remain bound to their intended account and purpose. Consult documented expiry and revocation semantics before judging residual access. OWASP authentication.
Numbered test procedure
- Inventory entry points. Record registration, email verification, password login, passkey/MFA completion, mobile login, SSO, API-token issuance, email change, linking, reset, and logout. Capture one complete normal sequence with timestamps and account identifiers. An anonymous call to
/api/meand an intentionally invalid credential form negative controls. - Check incomplete authentication. Stop A's normal login immediately before MFA. Use that intermediate cookie or token for a single protected profile read. Compare it with a completed A session and an anonymous request. Also check the observed alternate API route; avoid guessing or spraying credentials. Protected access without the required second factor is the finding, not the mere existence of an intermediate token.
- Check recovery binding. Request one reset for A and one for B. In the local replica, submit A's reset proof with B's account selector, if that selector is client-controlled. After an authorized normal reset of A, replay the consumed proof once. Confirm which controlled account's credential changed by a fresh login. Restore passwords afterward. Single-use, expiry, and account binding are independent checks. OWASP recovery.
- Check session transitions. Save the pre-login identifier, completed session, and any refresh credential separately. Compare identifiers after login and elevation. Perform logout or explicit device revocation and make one old-session profile request. Record access and refresh outcomes separately; a refresh token being revoked does not necessarily revoke already-issued access tokens. Evaluate the actual advertised security behavior. OWASP sessions.
- Check identity changes and pre-existing state. Change A's verified email to a second owned inbox; compare verification, pending state, and recovery delivery before/after confirmation. In the replica, keep an unverified password account open, then complete the legitimate verified/federated signup for the same owned inbox in another profile. Inspect whether the original password, session, or linked identity can still access the now-verified account. Independently test reusing a stale verification link after another email change. A duplicate signup error or two aliases reaching the same mailbox proves no takeover. Do not assume case folding, dot removal, or plus-address handling is universal. Email identity comparison and verification.
- Follow enterprise onboarding and offboarding. Use a disposable tenant with a second recovery administrator. Record invitation recipient/role/tenant, acceptance, cancellation, SSO enforcement, group assignment, and deactivation. Compare a fresh invite with one cancelled invite; confirm whether a changed recipient is allowed by the product contract. After enabling enforced SSO, attempt the observed password, reset/magic-link, and alternate client entry paths once with controlled accounts, then request one protected tenant object. Deliberate break-glass exceptions are separate policy. For SCIM, use the tenant's provisioning credentials and verify deactivation, group removal, token refresh, and existing streams after the configured sync interval. SCIM
activehas provider-defined semantics; it is not a universal immediate-session-kill instruction. SCIM schema. Provisioning credentials must still map to the permitted tenant/resources. SCIM authorization. - Follow the specialized proof branches. Use MFA/passkey lifecycle for enrollment, factor replacement, backup-code reuse, and WebAuthn verifier checks. Use federation/token trust for OAuth PKCE, OIDC claim and account-linking decisions, JWT validation, and SAML. Do not substitute decoded claims for a protected-resource result. Document which providers, authentication methods, and enterprise features were unavailable rather than marking them passed.
Command card
Export A's local cookie jar before using the application's real logout button. These requests compare protected content before and after logout; routes and credential transport must match the replica.
mkdir -p evidence/session
chmod 600 A-before.cookies
curl -q --noproxy '*' --max-time 10 -b A-before.cookies \
-D evidence/session/before.h -o evidence/session/before.json \
http://127.0.0.1:8000/api/me
# Perform the actual logout or device-revocation workflow now.
curl -q --noproxy '*' --max-time 10 -b A-before.cookies \
-D evidence/session/after.h -o evidence/session/after.json \
http://127.0.0.1:8000/api/me
curl -q --noproxy '*' --max-time 10 -o evidence/session/anonymous.json \
http://127.0.0.1:8000/api/me
Report threshold and checklist
Report access as the wrong controlled identity, completion without required authentication, unauthorized account linking, or a material violation of revocation guarantees. Cosmetic logout, unsent browser cookies, decoded JWT contents, and reset forms displaying without completing are common false positives.
- Account and session state recorded for every request.
- Browser binding distinguished from token validity.
- Correct and incorrect proofs compared independently.
- Fresh login verifies any credential or linking change.
- Verification, linking, invitations, and enforced-SSO fallback paths evaluated.
- Provisioning changes checked against configured sync and revocation behavior.
- Recovery settings restored; live tokens removed from report.
Trace input into queries, commands, and templatesProve interpretation at a specific sink using small paired experiments instead of payload spraying.
Open canonical module and reverse references
- 01Identify input and sink
- 02Freeze a synthetic fixture
- 03Send paired controls
- 04Observe sink semantics
- 05Confirm smallest effect
- 06Document root cause
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant
Use a local replica seeded only with synthetic records, a normal request, and an observable sink: database query logs, a template render, or a disposable worker's process log. Identify input type and decoding layers before selecting syntax. The invariant is: untrusted input remains data, or is constrained to an explicitly authorized language. A SQL search field, MongoDB filter, template editor, and process argument have different trust contracts; the same string is not a universal test.
Numbered test procedure
- Trace the route. From the handler, follow validation, normalization, service calls, query builders, template compilation, and subprocess invocation. In a black-box target, infer this from observed feature behavior and errors, then reproduce locally when possible. Record the last point where input is still data and the first interpreter boundary. An ORM can still contain unsafe raw fragments.
- Freeze controls. Create exactly one matching synthetic record and one nonmatching record. Capture the normal response. Choose an input that must produce no match under the intended semantics. Keep actor, tenant, pagination, and cache state unchanged across each pair.
- Test SQL or NoSQL semantics. For a known SQL string context in the lab, compare logically true and false expressions while retaining the same synthetic predicate. Prefer local query logs over timing or data extraction. For a JSON filter expected to accept a string, compare
{"name":"BB-CANARY"}with{"name":{"$eq":"BB-CANARY"}}, then$eqwith a nonexistent marker. Operator acceptance becomes a vulnerability when it escapes the allowed query contract or security predicate; intentional query APIs are different. SQL injection, NoSQL injection. - Test template evaluation. For a documented Jinja/Twig-like local sink, compare literal text,
{{7*7}}, and{{7*8}}. Inspect the raw HTTP response before JavaScript executes. Results 49 and 56 support server interpretation; a single 49 might be ordinary content. If the feature intentionally supports expressions, determine whether a lower-trust actor crosses a promised sandbox or data boundary before reporting. Arithmetic alone does not establish OS execution. Template injection. - Test command interpretation locally. With an identified POSIX shell sink, use a disposable process and a marker-only operation such as
printf BB-CMD-01; compare the literal argument path with the shell-interpreted path in process logs. Avoid file writes, network callbacks, account enumeration, and long sleeps. Also distinguish argument injection into a legitimate executable from starting an additional shell command. Command injection. - Confirm causality and clean up. Repeat the baseline and one successful pair, then switch to the validated/parameterized route in the replica as a negative control. Capture the input-to-sink path, raw values after decoding, and smallest observable impact. Remove fixtures and stop at proof.
Command card
The following performs three bounded template probes against an illustrative local endpoint. Use only when that endpoint is expected to render the parameter as text; do not infer engine identity from one expression.
mkdir -p evidence/injection
curl -q --noproxy '*' --max-time 10 --get --data-urlencode 'name=BB-LITERAL' \
-o evidence/injection/baseline.txt \
http://127.0.0.1:8000/preview
curl -q --noproxy '*' --max-time 10 --get --data-urlencode 'name={{7*7}}' \
-o evidence/injection/expression-a.txt \
http://127.0.0.1:8000/preview
curl -q --noproxy '*' --max-time 10 --get --data-urlencode 'name={{7*8}}' \
-o evidence/injection/expression-b.txt \
http://127.0.0.1:8000/preview
Report threshold and checklist
Report attacker-controlled interpretation that violates the feature's contract, supported by paired results or an instrumented source trace. Syntax errors, WAF blocks, reflection, slow requests, and intentionally supported expression syntax are insufficient alone. State whether the demonstrated impact is query manipulation, expression evaluation, argument injection, or command execution.
- Sink, language, quoting context, and decode order identified.
- Synthetic positive and negative records available.
- One input changed per comparison.
- Raw server output separated from client rendering.
- Evidence stops short of secret retrieval or destructive execution.
Local curl examples bypass environment proxies with --noproxy '*'; -q alone only suppresses curl configuration files. These snippets target loopback and do not follow redirects. curl manual.
Delayed consumers and argument-only execution
A safe database insert does not make the stored value trusted forever. In the replica, save a marker through the normal create/update route, then trigger one observed export, search, scheduled job, or administration view that consumes it. Capture both requests, the stored bytes, and the later interpreter call. Repeat with a literal marker and one context-specific expression; keep the actor and selected synthetic record constant. This checks second-order injection that an immediate-response scan misses. Second-order SQL injection.
Treat process arguments as a separate boundary. A subprocess argument array with shell=False can still pass an attacker-controlled option to the intended executable. In a local wrapper, compare a normal filename-like argument with a harmless, documented mode switch and observe the exact argument vector and output. Prove that the caller was allowed to supply a value but not that mode. Do not test options that write files, run another program, or contact a network. Report shell-command interpretation under CWE-78 and argument/option injection under CWE-88; neither automatically proves the other. CWE-88.
- Stored input followed through its later privileged consumer where one exists.
- Argument vector distinguished from shell command text.
Server-side fetching and network trust boundariesSeparate an intended fetch from a request that reaches a prohibited destination, forwards authority, or exposes a protected response.
Open canonical module and reverse references
- 01Map fetch contract
- 02Start controlled canary
- 03Confirm ordinary fetch
- 04Change one destination rule
- 05Correlate server evidence
- 06Bound the impact
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant
Identify a feature that causes server-side requests: preview, import, webhook verification, document conversion, image fetch, repository import, or AI tool execution. Record allowed schemes, destinations, redirect rules, credentials, and whether response bodies are returned. Use a local replica with a tiny HTTP canary reachable from its worker. If the application runs in a container, localhost means that container; place the canary on an explicitly controlled test network instead of guessing host addresses.
The invariant is: every actual destination and credential transfer follows the feature's intended policy. A URL-fetching feature contacting your public receiver is often normal functionality. SSRF requires an unintended request or trust-boundary violation. SSRF mechanics.
Numbered test procedure
- Document the ordinary path. Submit a permitted static URL on your own receiver, containing a unique marker in its path. Record submission time, job ID, request method, receiver log, worker identity if available, and returned output. Confirm the browser did not fetch the URL directly by repeating through curl with the browser closed.
- Establish controls. Submit a second permitted URL that returns a different marker. Also submit a deliberately rejected scheme or destination from the feature's documented policy. Record whether rejection happens before queueing, at DNS resolution, after connection, or during response handling. A DNS lookup alone establishes resolution, not HTTP access.
- Test one policy boundary in the replica. Compare a permitted receiver with an owner-provided private canary that policy should block. If arbitrary public destinations are intended, concentrate on restricted network ranges, forbidden ports or schemes, or unintended credentials, using only designated canaries. Never use production metadata credentials as proof.
- Test redirect handling. Configure your first controlled endpoint to return one redirect to the second controlled canary. First verify both endpoints independently. Submit the first URL and correlate logs to show whether the second destination was validated. An initial allowlist check is insufficient if the actual redirected destination crosses the rule. Where authorized, study DNS validation/connection consistency in the replica with a controlled resolver; do not perform Internet-wide rebinding experiments. OWASP SSRF prevention.
- Inspect authority and output. Check whether a synthetic authorization header, test cookie, or privileged worker identity reaches the receiver unexpectedly. If the app displays a response, return only a fixed canary string. Distinguish blind connectivity from a readable internal response, and identify whether headers, method, body, or destination are attacker-controlled.
- Interpret and stop. Correlate exact marker, time, source, request, and job. Rule out your browser, webhook delivery by design, scanners, and unfurling services. Stop after the first controlled boundary violation; do not sweep adjacent hosts or ports. Shut down canaries and delete test jobs.
Command card
Run the receiver in a new empty directory in one terminal. This Python server serves only the canary directory and binds loopback. It is a local test receiver, not production hosting. Python HTTP server.
mkdir -p ssrf-canary
printf '%s\n' 'BB-SSRF-CANARY-01' > ssrf-canary/probe.txt
python3 -m http.server 8765 --bind 127.0.0.1 --directory ssrf-canary
In another terminal, send one request to your local application's observed fetch route. The example assumes same-host processes and JSON request data.
curl -q --noproxy '*' --max-time 10 --header @A.header \
-H 'Content-Type: application/json' \
--data '{"url":"http://127.0.0.1:8765/probe.txt?case=BB01"}' \
http://127.0.0.1:8000/api/preview
Report threshold and checklist
Report the prohibited destination or credential boundary actually reached. Label DNS-only evidence and environmental limitations explicitly. An open redirect or successful public image import alone does not prove internal access.
- Feature's destination contract written down.
- Receiver ownership and worker network namespace established.
- Ordinary fetch and rejected control recorded.
- Redirect destination and credential behavior inspected.
- No metadata secrets, unrelated internal addresses, or port sweeps used.
Local curl examples bypass environment proxies with --noproxy '*'; -q alone only suppresses curl configuration files. These snippets target loopback and do not follow redirects. curl manual.
Resolver, retry and connection identity
In source review, trace the URL parser, A/AAAA lookup, selected socket address, redirect handling and retry path as one decision. Verify the checked address is the one actually connected to; a second resolution, IPv6 path, alternate IP representation, redirect or worker retry must not escape the intended policy. For a DNS-change hypothesis, use a controlled resolver and designated canaries in the replica, record returned addresses and actual connections, and compare with a fixed-answer control. Merely accepting an unusual URL spelling does not prove a prohibited connection. Keep TLS verification enabled where HTTPS is used; do not change the client trust model to make the proof succeed. OWASP SSRF prevention.
Browser execution, cross-origin reads, and forged actionsUse real browser behavior to distinguish XSS, CSRF, CORS, and messaging failures from header-only leads.
Open canonical module and reverse references
- 01Locate source and browser sink
- 02Prepare two controlled origins
- 03Establish normal behavior
- 04Run browser-feasible probe
- 05Verify execution or state
- 06Remove stored content
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant
Use two controlled accounts and separate browser profiles. For cross-origin tests, use a researcher page and the local target on different ports; these are different origins but may still be the same site. For conclusions involving SameSite cookies, reproduce with genuinely different sites in an authorized HTTPS setup. Record browser version, cookie attributes, CSP, iframe sandboxing, request initiator, and navigation context.
The invariant is: a lower-trust origin or stored value cannot execute in the application origin, read protected responses, or cause an unauthorized credentialed action. Each of those outcomes is a separate test. XSS, CSRF, CORS.
Numbered test procedure
- Map sources and sinks. Start with a distinctive text marker. Track it through reflected HTML, attributes, JavaScript strings, DOM insertion, Markdown preview, stored comments, embedded documents, and
postMessagehandlers. Record what is escaped and whether rendering occurs server-side or in JavaScript. Do not copy one payload across unrelated contexts. - Build a normal and inert control. As A, save plain text into a private object B is allowed to view. Verify B sees it. An HTML-escaped version is the inert comparison. For a local HTML text sink, a marker-only example is
<img src=x onerror="document.documentElement.dataset.bb='BB-XSS-01'">. Inspect B's DOM for the resulting attribute; the payload never reads cookies or sends data. If CSP blocks execution, preserve that outcome instead of claiming execution. - Test CSRF delivery. Capture B's normal reversible preference change. From the controlled other origin, reproduce it using a browser-deliverable form or fetch request without a manually inserted target bearer token. Verify which cookies were actually sent, whether preflight occurred, and whether the setting changed in a fresh target tab. Use an invalid CSRF token or logged-out profile as a control. A manually edited HTTP request is not by itself a browser exploit.
- Test CORS reads. From the researcher page, call a private test endpoint with
credentials: 'include'and attempt to read the response body. Compare an allowed application origin, the researcher origin, and an anonymous request. Save the actual browser result and credential behavior. A request reaching the server is different from JavaScript reading its response; wildcard origins do not enable credentialed response access. Browser CORS behavior. - Test messages and embedding where present. For an observed
postMessagehandler, send the expected message shape from your controlled page and change only the sender origin. Verify a harmless state marker or a restricted action on your account. For iframe interaction, prove the sensitive action is practically inducible, rather than reporting absent framing headers alone. - Interpret delivery and cleanup. Record which account planted content, which viewed it, the effective origin, and the boundary crossed. Remove stored payloads and reverse preference changes. A payload that only executes after the tester pastes JavaScript into DevTools has not established an application injection path.
Command card
Create browser-probe/index.html locally with this content, then serve it using the command below. Open it while the separate controlled target session exists; confirm that your browser's cookie policy permits the intended experiment.
<!doctype html><meta charset="utf-8"><pre id="result"></pre>
<script>
fetch('http://127.0.0.1:8000/api/me', {credentials: 'include'})
.then(r => r.text())
.then(t => { document.querySelector('#result').textContent = t; })
.catch(() => { document.querySelector('#result').textContent = 'Read blocked or request failed'; });
</script>
python3 -m http.server 8766 --bind 127.0.0.1 --directory browser-probe
Report threshold and checklist
Report cross-user execution, a browser-readable private response, or a forged protected state change with realistic delivery. Reflection, arbitrary Origin acceptance in curl, missing CSRF tokens, and same-site laboratory results generalized to cross-site attacks are common false positives.
- Real browser reproduces the claimed behavior.
- Origin and site relationships recorded separately.
- Credentials, CSP, preflight, and redirects observed.
- Benign marker demonstrates only the required impact.
- Stored content and account settings cleaned up.
Uploads, downloads, paths, archives, and XMLFollow a file through acceptance, storage, processing, publication, and retrieval while preserving ownership and path boundaries.
Open canonical module and reverse references
- 01Map file lifecycle
- 02Seed harmless canaries
- 03Change one file property
- 04Check parser and destination
- 05Verify another controlled reader
- 06Remove every derivative
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant
Use a local replica, a tiny supported file, two controlled accounts, and a dedicated scratch directory. Identify upload handler, object store, converter, background worker, CDN/download route, and deletion path. Place a unique harmless canary just outside the permitted download root, through normal local filesystem access. The invariant is: user-controlled file metadata or contents cannot escape storage, interpretation, or ownership rules.
Separate filename, MIME declaration, actual bytes, storage key, filesystem path, and retrieval origin. Each component may normalize them differently. Acceptance of an unusual extension alone does not prove unsafe execution. Upload handling.
Numbered test procedure
- Record a supported baseline. Upload a tiny text/image fixture through the documented route. Save its object ID, resulting filename, declared and detected type, processor status, retrieval headers, and response bytes. Verify A can retrieve it and B cannot when the file is private.
- Change one property. Resubmit the same harmless content with a different filename or MIME declaration, independently. For a type-restricted image endpoint, text labeled
image/pngshould either fail or be safely handled. Inspect the stored result and actual browser rendering. Do not upload executable shells or server configuration files to demonstrate an extension mismatch. - Check path containment. In the local replica, request the planted out-of-root marker using one relevant
../path. Compare a valid in-root file and a nonexistent sibling path. Preserve the raw request target and server-resolved path; a client or proxy may normalize dot segments before they reach the application. If testing writes, use only a dedicated disposable output directory and check that an outside marker is not created. Path traversal. - Check ownership through derivatives. As B, request A's controlled original, thumbnail, conversion result, archive export, and any observed signed URL. Record whether the URL is intentionally a share capability and its documented lifetime. Deleting the primary record does not prove every derivative became inaccessible; evaluate the product's promised behavior.
- Check archive and XML processing locally. For an extractor, create a tiny archive with one ordinary member and one canary path outside its designated extraction directory; inspect resolved paths without overwriting anything important. For an XML parser, compare a plain literal field with one external entity referencing the controlled HTTP canary from the SSRF module. Correlate callback logs and returned marker. Do not use recursive entities, oversized documents, or operating-system files. XML acceptance or a DOCTYPE error alone is not external-entity resolution. XXE.
- Interpret downstream effects. Wait for the single test job to finish. Compare bytes, filesystem location, ownership, browser origin, and any parser callback. Source review should locate the exact join/extraction/parse operation and whether containment checks occur after normalization. Delete originals, derivatives, exports, and queued test jobs.
Command card
This creates a local download fixture. Configure the replica's allowed download directory as file-lab/public; illustrative endpoint and parameter names must match the application.
mkdir -p file-lab/public evidence/files
printf '%s\n' 'BB-INSIDE-01' > file-lab/public/inside.txt
printf '%s\n' 'BB-OUTSIDE-01' > file-lab/outside.txt
curl -q --noproxy '*' --max-time 10 --get --header @A.header \
--data-urlencode 'name=inside.txt' \
-o evidence/files/baseline.txt \
http://127.0.0.1:8000/api/download
curl -q --noproxy '*' --max-time 10 --get --header @A.header \
--data-urlencode 'name=../outside.txt' \
-o evidence/files/traversal.txt \
http://127.0.0.1:8000/api/download
Report threshold and checklist
Report a proven outside-root read/write, unauthorized controlled-file access, unsafe browser/server interpretation, or prohibited parser fetch. A reflected filename, harmless stored extension, public sharing by design, and asynchronous rejection are frequent false positives.
- Upload and retrieval rules documented independently.
- Only tiny, synthetic files and dedicated canaries used.
- Filename, MIME, contents, and ownership varied separately.
- Raw request and resolved destination retained.
- Processed derivatives included in evidence and cleanup.
Local curl examples bypass environment proxies with --noproxy '*'; -q alone only suppresses curl configuration files. These snippets target loopback and do not follow redirects. curl manual.
Containment beyond dot segments
For a link-capable filesystem or archive format, test a tiny symbolic-link fixture in the disposable directory as a separate case. Point it only to the already-planted harmless outside canary, then exercise the product's real reader/extractor. Record the final resolved destination and the identity that opened it. A lexical .. rejection does not establish containment when links, absolute member paths, platform separators, or case collisions change the destination. Do not generalize a local administrator-created link into a remote vulnerability: show how the stated attacker can create or influence that link through the product.
Pin the runtime and actual extraction API/filter. Python's tar extraction filters can reject outside destinations and links, but filter availability, explicit overrides, and additional resource limits matter. A failed extraction may also leave earlier members on disk; inspect and remove only the disposable output. If a TOCTOU claim depends on replacing a path between validation and opening, demonstrate that timing separately in the replica; a static link test does not prove a race. Python extraction filters and limitations.
- Final resolved file or link target stays inside the intended root.
- Actual runtime/filter and partial-extraction cleanup documented.
Workflow invariants, replay, idempotency, and racesFind security consequences in workflow ordering and concurrency, with serial controls and durable-state evidence.
Open canonical module and reverse references
- 01Write business invariant
- 02Capture normal transitions
- 03Change ordering or replay
- 04Compare serial control
- 05Try bounded lab concurrency
- 06Verify committed state
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant
Choose one reversible workflow in a local or nonfinancial sandbox: accepting an invitation, creating one test resource, applying a test credit, approving a draft, or consuming a one-time action. Write the exact invariant, for example: one invitation grants at most one membership, to its intended recipient and role. Record preconditions, state transitions, actor permissions, durable result, retries, and cleanup. Logic findings depend on intended rules, not whether the request is technically valid. Business logic.
Numbered test procedure
- Draw the normal transition table. List draft → submitted → approved → completed, or the product's actual equivalent. For each edge, record actor, required prior state, immutable fields, and transaction identifier. Perform one normal run with synthetic objects and save before/after state, not just HTTP responses.
- Test skipped and reordered transitions. On a fresh disposable object, attempt the observed completion request before approval. Then change only a value that should be frozen after approval, such as recipient or test quantity. Compare with a fully approved control and an invalid object ID. If a transition is intentionally optional, adjust the invariant rather than treating missing UI steps as a bug.
- Test serial replay. Repeat the same completed operation once after it settles. For documented idempotency keys, compare the same key/same body with the same key/different body, using sandbox data. Determine whether keys are scoped by actor, tenant, endpoint, and expiry. Returning the first result again can be correct idempotent behavior.
- Test boundary values selectively. Use zero, a single negative value, or a precision boundary only where the sandbox permits it. Record server-calculated totals and immutable values. Avoid real billing, inventory, refunds, transfers, or customer notifications. A strange displayed number without a committed consequence is not enough.
- Test concurrency only in an authorized setting. First demonstrate expected serial behavior. Reset a test object, then send exactly two simultaneous requests as a small experiment. The local example below uses a barrier; it does not guarantee server arrival within the vulnerable interval. If there is no overlap, record inconclusive timing rather than absence of a race. More aggressive synchronization belongs in the isolated lab. Race conditions.
- Inspect after completion. Wait for both requests and relevant workers. Count durable memberships/resources or inspect the ledger of synthetic events. Two success responses may correspond to one commit. Repeat only to resolve a specific uncertainty, then restore the object and stop at the first demonstrated invariant failure.
Command card
This sends exactly two requests to a disposable local invitation endpoint. Put a valid local test bearer token in A.token, with restrictive permissions, and replace the route with the observed one. Create a fresh invitation for each serial/concurrent comparison. Run against localhost only.
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from threading import Barrier
from urllib.request import Request, build_opener, ProxyHandler, HTTPRedirectHandler
from urllib.error import HTTPError
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
gate = Barrier(2)
token = Path('A.token').read_text().strip()
def attempt(index):
req = Request('http://127.0.0.1:8000/api/invitations/TEST-INVITE/accept',
data=b'{}', method='POST',
headers={'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'})
gate.wait()
try:
with build_opener(ProxyHandler({}), NoRedirect()).open(req, timeout=10) as response:
return index, response.status, response.read(1024).decode(errors='replace')
except HTTPError as error:
return index, error.code, error.read(1024).decode(errors='replace')
with ThreadPoolExecutor(max_workers=2) as pool:
print(list(pool.map(attempt, range(2))))
Report threshold and checklist
Report a durable violation of the stated rule, with serial controls, initial conditions, minimal request sequence, and final state. Duplicate responses, transient UI counters, expected retries, and intentionally cumulative operations are common false positives. Describe the demonstrated consequence; do not infer financial loss from a sandbox counter alone.
- Business rule and expected actor written down.
- Normal and serial-replay controls completed.
- Fresh fixture used for each experiment.
- Only bounded authorized concurrency used.
- Durable state and asynchronous effects verified.
The Python example disables environment proxies and redirect following. A redirect is captured by the HTTPError branch; inspect its destination before any separate request.
Events that arrive after the request
Apply the same invariant to workers and integration events, not only the HTTP endpoint. Record a deduplication key's scope and when it becomes durable relative to the protected effect. A claim written before a worker fails can lose work; a claim written after the effect can permit duplicates. Use an instrumented fault point in the replica to distinguish these cases, and inspect committed state after retry. Then run the webhook event integrity procedure for signature, event ownership and out-of-order delivery checks. Do not assume provider delivery time is a reliable object version or that an HTTP acknowledgement proves processing completed.
REST, GraphQL, gRPC, WebSocket, and SSE permission parityCarry the same permission invariant across protocols, nested fields, streams, and alternate API versions.
Open canonical module and reverse references
- 01Inventory observed operations
- 02Choose one permission invariant
- 03Compare controlled identities
- 04Test alternate protocol path
- 05Check stream lifecycle
- 06Record semantic result
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant
Start from normal client traffic and supplied API schemas. Create two controlled accounts and private synthetic objects. Map REST paths, GraphQL operations/fields, gRPC methods, and WebSocket message types to the same business action. The invariant is: changing transport, API version, batching, or connection lifetime does not widen an actor's permission. Reuse the authorization matrix and session modules; protocol-specific testing adds delivery details rather than duplicating those foundations.
Numbered test procedure
Build a semantic inventory. Record operation name, method/path, actor, tenant selector, object ID, request schema, response fields, and side effects. Observe one success and one forbidden controlled-object request per available protocol. Preserve response bodies, GraphQL errors, gRPC status/trailers, or socket messages; HTTP status alone does not define application success.
Check REST parity. Replay the same controlled-object rule through observed v1/v2 routes, nested resources, exports, and a two-item batch. Compare read, update, and server-controlled properties independently. An alternate route being live is not a vulnerability unless its behavior violates the intended permission or lifecycle rule.
Check GraphQL resolvers and fields. Begin with the client's existing query. As B, change only A's known object ID, then request one protected field omitted from B's normal response. Try the observed global-node route if present. Use at most two aliases as a bounded comparison of resolver behavior; do not use recursive depth or massive aliases. A schema or introspection response helps discovery but is not a confidentiality breach by itself. Where cookie authentication and alternate HTTP methods exist, check a reversible mutation through the observed transport: GraphQL GET is for queries, not mutations. If claiming CSRF, prove delivery in a real browser using the browser-trust procedure. Persisted query IDs are operation selectors, not permission proofs. GraphQL HTTP semantics. GraphQL testing.
Check gRPC context. Use the provided
.protofile or authorized reflection to identify one method. Compare channel identity, per-call credentials, tenant metadata, and the selected resource. Repeat a forbidden request with B's genuine credentials, not a token fabricated from decoded claims. For streaming methods, check whether a permitted start improperly authorizes later messages for another controlled resource. Channel credentials and call credentials serve different roles. If an HTTP/JSON transcoding gateway or gRPC-Web route is actually deployed, replay this same invariant there: a translated request can travel through different authentication middleware. Preserve the gateway path and backend method instead of inferring exposure from a.protoannotation. HTTP/JSON transcoding. gRPC authentication.Check WebSocket and SSE lifecycle. Capture a normal handshake and one subscription message. In B's profile, request A's private room using its known ID; have A send a single harmless marker. Remove B from a previously shared room and test one further message on the existing socket. For cross-site hijacking, use a real controlled origin and actual browser cookies. An accepted forged Origin header from a nonbrowser client does not prove browser delivery. OWASP WebSocket security. For server-sent events, use the observed event endpoint with A and B, then remove B and inspect one new synthetic event. A reconnect cursor or
Last-Event-IDselects delivery position; test that it cannot select another controlled tenant's history. Close the browser EventSource during cleanup because it normally reconnects automatically. SSE connection and reconnection.Interpret and consolidate. Read partial data as well as error arrays; an error on one field can coexist with leaked data elsewhere. Correlate socket subscription IDs and asynchronous messages. Group instances by shared root cause only when the evidence supports it, and name untested transports explicitly. Close sockets and remove test resources.
Command card
Prepare protected A.header and B.header files as described in the authorization module. The example assumes the local schema exposes document(id:). Replace it with the already observed query, not an invented production operation. The second request changes only identity.
mkdir -p evidence/protocols
cat > document-query.json <<'JSON'
{"query":"query($id:ID!){document(id:$id){id title}}","variables":{"id":"O1"}}
JSON
curl -q --noproxy '*' --max-time 10 --header @A.header \
-H 'Content-Type: application/json' --data @document-query.json \
-o evidence/protocols/a.json http://127.0.0.1:8000/graphql
curl -q --noproxy '*' --max-time 10 --header @B.header \
-H 'Content-Type: application/json' --data @document-query.json \
-o evidence/protocols/b.json http://127.0.0.1:8000/graphql
For a local socket, use the browser's Network → WS view to copy the observed message schema. Change one controlled room ID and send one message; protocol libraries often add framing, acknowledgements, or subscription IDs that a generic text frame would omit.
Report threshold and checklist
Report forbidden controlled data/action, a protected-field leak, or unauthorized ongoing delivery after the applicable revocation point. Reflection, introspection, websocket connectivity, and partial errors without forbidden output are discovery observations.
- Normal client operation and schema preserved.
- Same permission rule compared across available transports.
- Application-level errors and partial output inspected.
- Authentication at connection and message levels distinguished.
- Every stream closed and fixture cleaned up.
OSS lab: pin, install, prove, resetTurn an eligible release into a repeatable synthetic environment with preserved versions, realistic controls, and an explicit reset recipe.
Open canonical module and reverse references
- 01Qualify release
- 02Freeze inputs
- 03Review installation
- 04Build fixtures
- 05Compare controls
- 06Reset and repeat
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Start with a reproducibility contract#
Record repository, product edition, supported release, immutable commit, package locks, image digests, operating system, database, authentication mode, and enabled extensions. Keep tested, inferred, and unknown versions separate. A source tag does not identify the image running in a vendor deployment. Your initial output is a manifest plus a working baseline, before any vulnerability attempt.
Use a disposable VM for an unfamiliar project's build. Review its Dockerfiles, build backends, package lifecycle scripts, Makefiles, and downloaded installers first. Provide synthetic credentials and fixtures. Separate dependency preparation from validation; disable unnecessary external integration calls during validation. Preserve the installation instructions you followed and every deviation. This makes it possible to distinguish a supported insecure default from a flaw introduced by the research setup.
Freeze source without overwriting work#
These are adaptable local examples, not a claimed execution. Start with an existing reviewed clone at ./repo; create a fresh output directory. Substitute a real eligible release in the second command. A detached worktree allows a separate copy at that revision while sharing Git objects. Git worktrees.
mkdir -p ./evidence
git -C ./repo rev-parse --verify 'v1.2.3^{commit}'
git -C ./repo worktree add --detach ../lab-source v1.2.3
git -C ./lab-source rev-parse HEAD > ./evidence/source-commit.txt
git -C ./lab-source status --porcelain=v1 > ./evidence/source-status.txt
git -C ./lab-source submodule status --recursive > ./evidence/submodules.txt
Expected: a full commit identifier and an initially clean status file. A submodule entry is inventory, not confirmation its source has been obtained or reviewed. Save compiler/runtime/tool versions separately. Never silently replace a failing eligible release with current main.
Review the effective deployment#
Compose configuration can interpolate environment values, so use a synthetic lab environment and inspect output before sharing. Render first; check host mounts, image references, ports, capabilities, external volumes, privileged mode, and startup scripts. A project name alone does not isolate explicitly named shared volumes. Docker Compose config.
docker compose -p bounty-lab -f ./lab-source/compose.yaml config \
> ./evidence/compose-effective.yaml
docker compose -p bounty-lab -f ./lab-source/compose.yaml \
up -d --wait --wait-timeout 120
Run up only after review and required dependency preparation. --wait depends on declared health checks for application readiness; a running process alone is insufficient. Establish login, ordinary creation, retrieval, and a worker task through the real interface. Docker Compose up.
| Fixture | Normal operation | Boundary case |
|---|---|---|
| Tenant A and B | Each reads its own document | A requests B's synthetic document |
| Member and administrator | Administrator manages invitations | Member attempts the same operation |
| Import directory | Valid sample stays inside destination | Malformed sample attempts a harmless boundary crossing |
| Background worker | Queued operation completes once | Membership changes before job execution |
Preserve the failing and passing stories#
Example trace: member A requests an export, the route returns a job ID, and the worker reads a document owned by B. Capture the request, job record, worker resolution, synthetic output, and authorized B control. A direct call to the export helper explains the implementation but does not establish route reachability. Continue in source data flow and reuse the authorization matrix.
Diagnose setup failures before testing: a migration error invalidates database assumptions; disabled workers invalidate asynchronous conclusions; missing enterprise features limit edition claims. Record unresolved differences instead of compensating by disabling security controls.
Reset and report gate#
Prefer restoring a dedicated VM snapshot or documented fixture/database snapshot. Include queues, object storage, cache, local mail, and generated files in the reset recipe. Container restart is not state reset. Repeat the minimal reproduction after restoration; a result dependent on an earlier experiment is not yet isolated.
- Eligible revision, dependency locks, and deployment differences recorded.
- Normal user workflow and comparison account work.
- Required worker/integration fixtures are present.
- Synthetic evidence survives outside reset storage.
- Clean restoration reproduces the same boundary failure.
- Report distinguishes product behavior from lab-only configuration.
Source review: entry point to protected operationTrace attacker control and authorization through handlers, services, queues, and sensitive operations; promote only reachable violations of a stated invariant.
Open canonical module and reverse references
- 01State invariant
- 02Find entry
- 03Trace transformations
- 04Check guards
- 05Exercise interface
- 06Retest sibling paths
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Write the invariant first#
Choose a business rule precise enough to falsify: “a member can export documents only from a project where membership is currently active.” Record actor, object, operation, tenant, and when authorization must hold. A suspicious function becomes useful when you identify who can reach it and what protected state it changes. Reuse the authorization matrix for principal/object combinations and injection tracing for interpreter-specific input handling.
Start forward at a real interface: HTTP route, websocket event, CLI argument, uploaded project, plugin callback, or queue producer. Then work backward from the sensitive read, write, process invocation, parser, or credential use. Join the two traces; unresolved dynamic dispatch is an explicit gap. Check route registration and feature gating rather than assuming a named handler is exposed.
A worked asynchronous trace#
The following is a hypothetical review worksheet, not a finding against a named project.
| Hop | Value or context | Question to resolve |
|---|---|---|
POST /exports |
Authenticated member A supplies document_id |
Does the route check document membership or only login? |
ExportService.create |
A job stores document_id, requested_by |
Is the trusted tenant copied from the session or accepted from input? |
| Queue serialization | Job survives after request completes | Can another low-trust producer alter the fields? |
run_export(job) |
Worker reloads document by ID | Is current membership enforced at execution time? |
| Object storage write | Export receives a download reference | Who can fetch it, and can authorization expire? |
A useful candidate is “worker rehydrates an object globally after a tenant-scoped request.” Confirm whether the earlier guard binds the same immutable identifier, whether membership revocation should affect queued jobs, and whether the output is actually accessible. Business requirements may deliberately snapshot permissions; document that distinction rather than treating every delayed check as defective.
Local search and modeling cheat sheet#
Examples inspect a reviewed local repository. Patterns are leads, not a complete inventory. Place evidence outside the scan root; prepare local rules before offline/private-code analysis.
rg -n --hidden -g '!.git' \
'route\(|router\.|websocket|add_task|enqueue|\.delay\(' ./repo
rg -n --hidden -g '!.git' \
'pickle\.loads|yaml\.load|subprocess\.|child_process|exec\(' ./repo
semgrep scan --oss-only --config ./rules --metrics=off \
--disable-version-check --json \
--output ./evidence/source-leads.json ./repo
The Semgrep command uses reviewed local rules and the OSS engine. Inspect stderr, skipped files, and scan completion; an empty result with parser failures is not a clean audit. Rule licenses and engine capabilities are separate concerns. Semgrep CLI reference.
Model sources, propagation, context-specific sanitizers, and sinks. A path validator does not sanitize a shell argument; a role check does not validate serialized data. Custom wrappers can hide a source or sink from default rules. Create one vulnerable fixture and one safe sibling to validate your model. Static taint analysis does not automatically establish tenant policy or full runtime reachability. Semgrep taint model.
Prove the boundary through supported use#
Instrument the local application to log identifiers, decisions, and function names without credentials. Reproduce from the ordinary interface with synthetic accounts. Keep four observations together: allowed own-object request; denied or expected-to-deny foreign-object request; actual protected effect; resulting output access. Repeat without instrumentation changing security behavior. If only an internal helper is callable, qualify the finding as a library/caller-contract issue until a consumer path is established.
Inspect alternate callers after proving one flaw: bulk API, export service, websocket subscription, scheduled task, and administrative UI may reuse the same operation with different guards. Group equivalent paths by root cause and record tested variants. Do not inflate one shared defect into five independent findings.
Completion and report threshold#
- Invariant identifies actor, object, operation, and trust boundary.
- Attacker-controlled field is tracked through transformations and aliases.
- Every guard is evaluated with concrete fixture values.
- Feature, edition, role, and configuration prerequisites are explicit.
- Real interface reproduces the protected effect beside an authorized control.
- Safe sibling and patch retest preserve legitimate behavior.
Submit the smallest connected argument: entry point → attacker control → ineffective boundary → sensitive operation → demonstrated impact. A sink search, scanner trace, or direct helper call alone belongs in the hypothesis queue.
CI and releases: trace untrusted input into privileged workAudit workflow events, shell interpolation, untrusted builds, artifact consumers, and release identity as one privilege chain.
Open canonical module and reverse references
- 01Identify event trust
- 02Map executable input
- 03Inspect privileges
- 04Follow artifacts
- 05Validate locally
- 06Verify release identity
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Draw the actual privilege chain#
For every release-capable workflow, record event actor, workflow-file revision, checked-out revision, executable inputs, token permissions, reachable secrets, runner persistence, and final consumer. Distinguish data review from code execution: a checked-out contribution becomes executable through Makefiles, package install hooks, test discovery, plugins, and local actions as well as explicit shell scripts.
Use one row per job. Include reusable workflows and composite actions; apparent isolation at the top-level YAML may disappear inside their implementation. A permissive token setting without a lower-trust entry is a hardening observation, not a completed takeover claim.
| Boundary | Local evidence to collect | Comparison control |
|---|---|---|
| Metadata → generated shell | Original expression and resulting script | Environment variable passed as data |
| Fork content → privileged build | Exact fetched revision and executed file | Trusted base revision only |
| Test artifact → publisher | Producer identity, run association, consumed path | Artifact from expected trusted build |
| Build → attestation → release | Artifact digest and accepted signer identity | Wrong-source artifact rejected |
Triage commands and a safe coding pattern#
These examples inspect local files; they have not been run against a target. Grep-style matching deliberately overselects candidates.
rg -n --hidden -g '*.yml' -g '*.yaml' \
'pull_request_target|workflow_run|issue_comment|permissions:|id-token:' \
./repo/.github
rg -n --hidden -g '*.yml' -g '*.yaml' \
'download-artifact|upload-artifact|cache-mode|head\.sha|npm install|make ' \
./repo/.github
Passing untrusted metadata through an environment variable avoids inserting its contents into the generated shell program. Quote it and do not subsequently eval it. This pattern does not solve dangerous argument semantics in downstream tools. GitHub secure use.
- name: Inspect title as data
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: printf '%s\n' "$PR_TITLE"
Check current protections before declaring a pwn request#
GitHub currently documents fork-ref checkout protection for pull_request_target and read-only default cache access for that event. Inspect action revision, explicit allow-unsafe-pr-checkout, cache-mode, manual fetches, repository policy, and actual execution. The trigger name alone proves neither execution nor writable cache. Controls can differ from older published examples. Current pull_request_target guidance.
Hypothetical trace: an unprivileged test job uploads report.zip; a privileged follow-up selects an artifact by a generic name; an extracted helper is executed before publication. Validate run/repository binding, extraction destination, and consumer behavior in your own fixture with an inert marker and dummy credentials. A successful workflow conclusion does not authenticate every byte it produced. An artifact parsed strictly as data may break the supposed execution chain. GitHub Security Lab workflow analysis.
Follow identity to the shipped artifact#
For a known public artifact, this read-only verification example contacts GitHub. Replace repository, workflow, and commit with expected identities established independently; do not copy identities out of the untrusted artifact and then “verify” against them.
gh attestation verify ./dist/product.tar.gz \
--repo OWNER/REPO \
--signer-workflow OWNER/REPO/.github/workflows/release.yml \
--source-digest EXPECTED_COMMIT \
--format json > ./evidence/attestation.json
A reusable workflow may be the signer rather than the calling workflow. Verified provenance supports an origin claim; it does not prove the builder was uncompromised or the software is vulnerability-free. GitHub CLI verification.
Trusted publishing reduces long-lived publish-token exposure, but a compromised authorized workflow can still obtain publishing authority. Review environment approval, workflow identity, build separation, and remaining legacy credentials. PyPI trusted publisher security model.
Report and retest gate#
- A permitted low-trust actor controls the demonstrated input.
- Exact workflow/action revisions and security defaults are captured.
- Inert local reproduction reaches the privileged consumer.
- Real permissions are supported by configuration, not guessed from a trigger.
- Artifact run binding and signer/source identity are checked.
- Patched data handling rejects the case while normal releases still work.
Report the reachable capability and its prerequisites. Do not run target publishing steps or use discovered tokens to prove a local workflow hypothesis; maintainers can confirm live permissions through a coordinated reproduction when needed.
For GitLab repositories, also use GitLab pipeline boundaries: parent-project merge-request execution, job-token permissions and runner isolation have distinct semantics.
Dependencies: from advisory match to reachable impactSeparate composition, advisory matching, runtime reachability, and exploit prerequisites so dependency reports describe product impact.
Open canonical module and reverse references
- 01Inventory layers
- 02Resolve advisory
- 03Trace consumer
- 04Check prerequisites
- 05Compare fixed behavior
- 06Bound affected versions
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Keep four questions separate#
An SBOM answers what a tool identified in an artifact. An advisory matcher asks whether a component version matches known vulnerability data. Reachability asks whether the relevant implementation is used. Exploitability asks whether an attacker can satisfy the conditions and cross a security boundary. Preserve each conclusion independently; a negative result at one layer does not answer every other layer.
Inventory source locks, installed runtime, development/build dependencies, and final release output separately. A vulnerable parser in a lockfile may be absent from the production image. A development-only package may still run inside a privileged publisher. Record ecosystem, package identity, resolved version, origin, dependency path, optional features, and artifact digest. Use CI and release analysis for build impact and source data flow for runtime impact.
Inventory and match locally#
Examples require installed tools, reviewed inherited configuration, existing local paths, and prepared OSV ecosystem databases. They are command templates, not claimed results. ./release-root means an already extracted, reviewed release tree.
mkdir -p ./evidence
syft dir:./repo -o cyclonedx-json=./evidence/source.cdx.json
syft dir:./release-root -o cyclonedx-json=./evidence/release.cdx.json
osv-scanner scan source --offline -r --format json \
./repo > ./evidence/osv-source.json
Syft supports directory inventory and structured SBOM output; detected composition depends on catalogers and available metadata. Compare the two inventories rather than assuming they represent the same execution environment. Syft documentation.
OSV source scanning uses supported manifests and lockfiles. Full --offline requires cached databases and avoids network activity; --offline-vulnerabilities has a narrower meaning and can permit other network features. Missing caches are an incomplete scan. Preserve database age and scanner stderr alongside findings. OSV source scanning, offline behavior.
Build a reachability worksheet for each serious candidate#
| Question | Concrete evidence | If unresolved |
|---|---|---|
| Is the resolved component affected? | Exact advisory range, vendored source, backport patch | Version match only |
| Is affected code present? | Release tree, build flags, linked module | Source-only candidate |
| Is the vulnerable API called? | Consumer call site and dynamic trace | Reachability unknown |
| Can an attacker control its argument? | Ordinary route/file/CLI input and transformations | No attacker path established |
| Are prerequisites satisfied? | Parser mode, operating system, feature, privilege | Conditional exposure |
| What changes across the boundary? | Synthetic output or bounded local fault | Impact unproven |
Hypothetical example: a document service ships an affected archive library, but the advisory concerns symbolic-link extraction. Trace upload acceptance → import job → actual extraction API → filter configuration → destination. A safe filter can defeat that specific path even when the version matcher is correct. Conversely, an optional compatibility flag may restore the unsafe behavior. Test supported configurations explicitly and reuse the pinned lab.
Check package origin as well as version#
For dependency confusion, first inspect registry selection, namespace ownership, fallback, lockfiles, integrity hashes, and install hooks. pip documents that candidates from configured locations are considered together, rather than treating an extra index as a guaranteed lower-priority fallback. pip install index behavior.
Reproduce with two local registries and synthetic names that belong to your lab. Use packages containing metadata and harmless marker files; record which origin wins under the target's resolver configuration. Do not publish a real organization's missing package name. Resolver selection is one proof step; a product-impact claim must also show an affected build/consumer and applicable package controls.
Control, report, and completion#
Run the same smallest input against the affected and patched dependency in equivalent consumers. If swapping versions also changes application behavior or configuration, isolate that difference before crediting the patch. Test normal input to ensure the “fix” is not simply a broken integration. Avoid claiming every historical release is affected from one sampled version.
- Source, runtime, build, and release inventories are labeled.
- Advisory and patch are checked for the exact resolved component.
- Backports, vendoring, and optional features are considered.
- Attacker input reaches the affected API through supported use.
- Local impact and a fixed/control case are preserved.
- Tested versions, inferred exposure, and unknowns are separated.
The reporting threshold is a reproducible vulnerable use or the program's explicitly accepted dependency criterion. An old version number alone should remain a triage lead.
Libraries and parsers: bounded fuzzing to a minimal reproducerBuild a realistic local harness, instrument the target, bound fuzzing resources, and turn unique faults into defensible library or consumer reports.
Open canonical module and reverse references
- 01Choose contract
- 02Build harness
- 03Instrument target
- 04Fuzz within bounds
- 05Minimize root cause
- 06Verify consumer impact
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Choose an attacker-relevant parser contract#
Select a public API and a realistic input boundary: imported image, archive, document, packet, model file, project metadata, or CLI input file. Record who supplies bytes, the consumer's preprocessing, format limits, parser options, and the privilege of the process. Fuzzing an internal helper with impossible arguments may reveal a harness defect rather than a violated library contract.
Keep the first harness narrow: one parse operation, deterministic configuration, fresh state for each input, bounded allocations, and cleanup on both success and failure. Remove network and uncontrolled filesystem side effects. Seed with a few valid, small representative samples to reach deeper code; an all-random corpus may spend every iteration failing the magic-header check.
Harness and build cheat sheet#
The following is a template: product_parser.h, ProductDocument, and the parser functions are placeholders to replace with the target's documented API. Return/error ownership must follow that API. The commands have not been executed here.
#include <cstddef>
#include <cstdint>
#include "product_parser.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size > 65536) return 0;
ProductDocument* doc = product_parse(data, size);
if (doc != nullptr) product_document_free(doc);
return 0;
}
Build the target library itself with compatible sanitizer and coverage instrumentation; instrumenting only the harness misses much of the parser's behavior. Replace the source list with its reviewed build recipe. The example assumes Clang and a compatible C++ project. AddressSanitizer build guidance.
clang++ -g -O1 -fno-omit-frame-pointer \
-fsanitize=fuzzer,address,undefined -fno-sanitize-recover=undefined \
./fuzz_parser.cc ./parser.cc -o ./fuzz_parser
mkdir -p ./corpus ./artifacts ./evidence
./fuzz_parser ./corpus -max_total_time=120 -timeout=2 \
-rss_limit_mb=512 -max_len=65536 -artifact_prefix=./artifacts/
Add reviewed seed files before running. libFuzzer's time, input-length, timeout, and RSS settings bound the campaign; they are not a VM resource boundary. Run inside your local disposable environment with external memory/CPU limits appropriate for the workload. Track new coverage and distinct root causes rather than raw executions. libFuzzer reference.
Interpret the failure precisely#
| Observation | Immediate question | Defensible first claim |
|---|---|---|
| ASan heap read overflow | Does the public API permit this byte sequence? | Out-of-bounds read in the tested build |
| ASan write overflow | Which length check and allocation disagree? | Out-of-bounds write; execution impact unproven |
| Use-after-free | Did the harness violate ownership rules? | Lifetime defect if public usage is valid |
| UBSan arithmetic finding | Does overflow reach an unsafe allocation/index? | Undefined operation plus traced consequence |
| Timeout or RSS limit | Is it reproducible at modest input sizes? | Bounded evidence of resource exhaustion |
-fno-sanitize-recover=undefined makes the selected UBSan failures terminate instead of allowing a fuzzing run to continue after a diagnostic, which helps preserve reproducible failing inputs. UBSan checks selected undefined behaviors, not every arithmetic error. Unsigned integer wrap is defined in C/C++ and is not part of the default undefined group; choose additional checks when the target's threat model requires them. UndefinedBehaviorSanitizer.
Minimize and verify the consumer#
Copy one candidate into ./artifacts/repro.bin, then minimize with a unique output path. Preserve the original input, toolchain, build flags, revision, and symbolized trace.
./fuzz_parser -timeout=2 -rss_limit_mb=512 ./artifacts/repro.bin
./fuzz_parser -minimize_crash=1 -max_total_time=60 -timeout=2 -rss_limit_mb=512 \
-exact_artifact_path=./artifacts/minimized.bin ./artifacts/repro.bin
Replay the minimized bytes through a supported consumer in the pinned OSS lab. If the consumer rejects them before calling the library, report the library contract separately and leave consumer exposure unproven. A sanitizer crash is not proof of remote code execution, and its absence in an optimized build does not erase a confirmed memory-safety defect.
Hypothetical root cause: a header count is multiplied into a narrow allocation size, then a wider loop trusts the original count. Preserve the values, allocation site, failing access, and missing bound. The fix should bind accepted count to available input and checked allocation size; merely suppressing the crash is insufficient.
- Public API and ownership contract respected.
- Target code, not only harness, instrumented.
- Resources, seeds, revision, and toolchain recorded.
- Minimal input reproduces the same root cause.
- Normal input succeeds in affected and fixed builds.
- Library impact and real consumer exposure are stated separately.
Replay timeout and memory artifacts under the same resource limits as discovery. A minimized crash must retain the original root cause; a shorter input that fails differently is not equivalent evidence.
Client storage, logs, backup and transport trustMeasure where sensitive client state goes, who can retrieve it, and whether transport rejects invalid peers.
Open canonical module and reverse references
- 01Define secret and reader
- 02Capture lifecycle baseline
- 03Observe exposure path
- 04Run trust controls
- 05Validate impact
- 06Remove lab material
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant#
Use a clean owned device or desktop VM with the scoped release, two synthetic accounts, a scoped interception setup, and a unique marker such as BB-CLIENT-01. Define the prospective reader: another ordinary app, another OS user, someone receiving an exported diagnostic bundle, an authorized backup recipient, or a network adversary. A debugger/root reader is a separate model.
Invariant: credentials and private records do not enter a channel readable by an unintended principal, and the release rejects an unauthenticated network peer. Prioritize actual token/data movement; a string called api_key, a backup-enabled manifest, or absent certificate pinning alone is insufficient.
Procedure and command sheet#
Record the lifecycle. Start logged out; sign in, perform one marked operation, background/lock, switch accounts, then log out. For each state record relevant preferences, databases, caches, clipboard, notifications and user-exportable diagnostics. Use identity/session for token validity and revocation, and authorization checks for backend effects.
Locate only relevant sinks. Search a local source copy and explicitly acquired lab exports. The bounded file-size limit reduces accidental scanning of large databases; use filenames first when output may contain secrets.
rg -n 'Log\.|NSLog|os_log|console\.log|SharedPreferences|UserDefaults' ./source
rg -l --max-filesize 2M 'BB-CLIENT-01' ./lab-export
apkanalyzer manifest print ./app.apk
On Android, inspect allowBackup, fullBackupContent, dataExtractionRules, and the referenced XML. Test the supported restore path using synthetic data. Cloud backups and device transfers can follow different rules; neither follows reliably from one flag across all versions/OEMs. Do not use a legacy adb backup recipe as a universal modern test. Android backup rules.
Prove reader access. If the marker appears in logs, show who can obtain that release log under the stated prerequisites. Modern log access is restricted; ADB output does not prove every app can read it. If a diagnostic export includes an owned token, demonstrate the expected exclusion with an otherwise identical export that lacks a session. Share a redacted excerpt and file location, not a full personal log. Android log disclosure.
Use a transport control matrix. In a local endpoint or approved test environment, compare a valid certificate, an untrusted issuer, and a wrong-host certificate. Keep request content identical and send only synthetic data. Record the release's exact trust store, endpoint, handshake result and whether an application request is sent. A refusal caused by connectivity is not validation evidence; the valid control must connect. Custom trust code can mistakenly accept invalid chains. Unsafe trust managers.
Separate lab changes. A user-installed CA, debug-only trust anchor, disabled trust callback, or instrumentation can make interception intentional. Android debug overrides may bypass pins when their debug CA is used; they are ignored for a non-debuggable build. Therefore verify bad-peer acceptance on the unchanged eligible release before reporting a certificate-validation defect. Network security configuration.
Evidence, decisions and cleanup#
Retain state before/after, sensitivity, exact reader privileges, minimum exposed marker, and reproducible impact. Intended account-owner backup access and approved offline storage are not automatically vulnerabilities. Use file boundaries when exposure depends on permissions or export paths.
- Compare account A, account B, and logged-out state.
- Demonstrate the reader without granting extra privileges.
- Distinguish missing pinning from accepting invalid certificates.
- Remove test CAs/proxies, delete synthetic exports, clear test clipboard values, revoke owned sessions, and restore the lab snapshot.
Privacy surfaces and metadata-only leakage#
Add a reader-specific matrix instead of reporting absent UI flags. Seed one private message BB-PRIVATE-VIEW and a public title. Compare unlocked/locked, foreground/background and A/B account-switch states. Inspect the app switcher snapshot, lock-screen notification preview, clipboard, accessibility text and a diagnostic export through supported OS interfaces. Record the precise reader privileges, user settings and platform version. A photograph taken by an already authorized user or access after explicitly granting a privileged accessibility service is a different threat model. MASVS-STORAGE-2 covers unintended sensitive-data leakage; product guarantees and eligibility determine the report. OWASP storage leakage control.
For analytics/telemetry, place a synthetic marker in a private field and inspect only the lab app's own traffic or configured local sink. Compare the documented consent states and identify the receiving component and fields. Do not test the analytics vendor. A tracking SDK or a privacy-label mismatch alone does not demonstrate the claimed access boundary. OWASP privacy minimization.
For a timing/size/existence hypothesis, first implement it locally using a small blinded set of synthetic present/absent records. Randomize order, keep request sizes and authentication equal, retain every sample, and evaluate the prediction on held-out fixtures. Show what private fact is inferable, from which observable, with what error rate. Ordinary latency variation, a single paired sample and a marker already supplied in the query establish no disclosure. This is an experimental design to investigate a suspected side channel, not evidence that one exists.
- Private content is actually available to the stated unintended reader.
- User consent, OS grants and already-delivered copies are accounted for.
- A metadata-only claim reports false positives and fresh validation cases.
- Remove notifications, clipboard markers, exports and synthetic analytics data.
Desktop IPC, Electron bridges and native protocolsFollow page, file and local-process inputs into privileged desktop capabilities with a realistic caller.
Open canonical module and reverse references
- 01Map process authority
- 02Inventory entry points
- 03Record trusted call
- 04Exercise untrusted caller
- 05Validate capability
- 06Restore snapshot
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant#
Prepare a disposable desktop VM or owned Mac, a standard-user account, a clean application profile and synthetic files in a dedicated lab directory. Record release, Electron/runtime version when present, OS and installation mode. Capture the normal process tree: renderer, preload, main process, extension service worker, native host, helper or service.
Invariant: a lower-trust caller gets only explicitly authorized operations with constrained arguments. A local listener, broad preload API, or privileged process becomes actionable when a realistic caller can reach a sensitive capability. Console execution manually injected into a trusted renderer does not establish that route.
Procedure and command sheet#
- Inventory actual handlers. In local source or extracted JavaScript, locate windows, preload APIs, IPC registrations and protocol handlers. These are read-only searches:
rg -n 'ipcMain\.(handle|on)|contextBridge|nodeIntegration|contextIsolation|sandbox' ./source
rg -n 'openExternal|setAsDefaultProtocolClient|open-url|second-instance|protocol\.handle' ./source
rg -n 'connectNative|sendNativeMessage|allowed_origins|externally_connectable' ./extension
For each hit record input origin, current process identity, argument schema, authorization check, and final file/shell/helper operation. Include auxiliary windows rather than assuming the main window's settings apply everywhere.
Capture a valid transaction. Open an intended file or settings panel and trace one documented bridge method. Record sender frame/origin, method, arguments, and result. Electron recommends validating IPC senders, including relevant frame identity; navigation can change which document is using a renderer. Electron security.
Change one trust condition. Exercise the same method from an attacker-controlled page/frame reachable through an actual app feature, or from an ordinary local client for a native IPC endpoint. Compare with the trusted baseline and a disallowed method/argument. Keep the proof to reading a seeded nonsensitive file or writing one marker in the test directory.
contextIsolationdoes not repair an overbroad bridge; inspect the capability exposed and the main-process checks. Context isolation.Trace external protocols. Invoke a discovered handler through the OS-supported route with a harmless synthetic URI. On macOS, after verifying the registered scheme:
open 'labapp://open?item=BB-DESKTOP-01'
Record URI parsing and argument construction before reaching native functionality. Compare a normal item with a malformed/unknown item; the latter should be rejected without launching unintended functionality. Test packaged behavior because protocol registration differs by OS and packaging. Never infer command execution merely from shell.openExternal being present. Electron deep links, Shell API.
- Extend into native hosts. For a browser extension, inspect native-host
allowed_origins, executable path, message schema and downstream arguments. An allowed extension still needs to constrain untrusted page messages it forwards. A native host invocation has the user's process privileges unless another actual boundary is shown. Chrome native messaging.
Evidence, reuse and cleanup#
Preserve the complete sequence from realistic entry point to capability, with sender identity, target identity, exact input, expected denial and result. Reuse browser trust for origin/navigation validation, file boundaries for path handling and authorization matrix for action ownership. Backend token effects belong to identity/session.
- Reproduce from a reachable untrusted context, without developer-console privilege.
- Separate same-user capability abuse from administrator/system escalation.
- Compare valid sender, untrusted sender, and invalid arguments.
- Remove test protocol registrations if created, close local clients, delete marker fixtures and restore the clean profile/snapshot.
Extension-specific continuation#
For page forwarding, browser-supplied sender metadata, navigation races, worker restarts and optional permission changes, run extension message boundaries. Return here only when the verified path crosses into a native host, local protocol or privileged helper. Preserve the same originating page and owned synthetic marker across both procedures.
Update signatures, rollback and privileged installationTest the update decision and installation boundary while preserving production signer and privilege assumptions.
Open canonical module and reverse references
- 01Observe genuine update
- 02Identify policy and consumer
- 03Build local controls
- 04Test rejection points
- 05Confirm minimum effect
- 06Restore clean state
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant#
Use a disposable installation, the eligible release, one genuine update, a standard-user attacker account and a separate administrative setup account. For source-available projects, create a local updater harness or documented test feed that exercises the real verifier. Record any configuration change: redirecting production traffic or granting the attacker an update signing key would change the claim.
Invariant: installation consumes exactly the artifact authorized by the expected publisher policy, and lower-privileged users cannot substitute content before a privileged consumer uses it. Version rollback is a defect only when it violates a defined security policy or restores a demonstrable vulnerability under realistic attacker control.
Procedure and command sheet#
- Observe a genuine update. Record installed version, requested channel, feed metadata, downloaded filename/hash, expected publisher identity, temporary path, verifier result and installing process identity. Inspect local artifacts without executing them:
apksigner verify --verbose --print-certs ./update.apk
codesign -dv --verbose=4 ./Update.app
codesign --verify --deep --strict --verbose=2 ./Update.app
The Apple commands require macOS; the APK command requires Android build tools. A valid signature establishes properties checked by that tool, not that the product updater enforces its intended publisher. APK signer, Apple code-signing inspection.
Identify the actual updater. Read the shipped update library/version and packaging configuration. Electron update behavior depends on platform and package mechanism; a generic claim about all Electron applications is too broad. Android platform update signing is separate from application-managed downloadable modules or scripts. Electron autoUpdater, Android application signing.
Build three local controls. Use a genuine authorized artifact as the positive control, a one-byte-corrupted copy as the integrity control, and an artifact signed by an unrelated lab publisher as the identity control where the harness supports it. Keep the latter inert. Change only the relevant input and record the precise rejection/acceptance decision. Acceptance of metadata alone does not establish installation; distinguish download, verification, staging and activation.
Inspect the privilege boundary. Determine who can write the staged artifact, parent directory, updater configuration and helper executable. On Windows, these commands inspect signatures and ACLs:
Get-AuthenticodeSignature -FilePath 'C:\Lab\Update.exe' | Format-List
icacls 'C:\Program Files\ExampleApp'
icacls 'C:\Lab\Update.exe'
Use the actual observed staging path, not the illustrative path above. A broad ACL matters only if the standard user can control bytes that a more privileged process actually consumes. Demonstrate a harmless marker operation in a lab path; keep service identity and write access in the evidence. Authenticode inspection, ACL inspection.
- Test rollback semantically. Compare a supported older genuine release with the current release in the local harness. Check whether the defined version policy rejects it and whether an attacker can supply it without administrative or signing authority. Do not force an ADB downgrade and report the explicitly requested platform behavior as an updater bypass. Reuse file boundaries for staging/extraction and identity/session when a rollback changes credential protections.
Completion and cleanup#
- Record exact accepted bytes and identity, not just a green verification UI.
- Preserve genuine, tampered, wrong-publisher and applicable rollback results.
- Explain prerequisite control, installing privilege and demonstrated effect.
- Label locally modified verifier behavior as a limitation until release equivalence is established.
- Restore original feed/configuration, remove inert fixtures, restore the VM snapshot and confirm the normal update path works.
MFA and passkeys: enrollment, proof, replacement, and recoveryFollow each authenticator from account binding to removal, and validate WebAuthn assertions without mistaking client UI or counter behavior for a security guarantee.
Open canonical module and reverse references
- 01Record assurance policy
- 02Enroll controlled factors
- 03Compare fresh and stale proof
- 04Check assertion bindings
- 05Replace and recover
- 06Verify removal
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and invariant
Reuse identity/session fixtures. Prepare two disposable accounts, recovery access, separate browser profiles, and one reversible protected operation. Record whether the product promises MFA, passwordless login, recent reauthentication, or a particular authenticator class; these are different contracts. The invariant is: a proof must authorize the intended account, ceremony, and assurance level, including factor management. Capture the server's stored factor list and audit events, not just a green browser badge.
Numbered procedure
- Test enrollment before authentication. Register A's first and second factors through the legitimate UI. Compare an authenticated registration with anonymous, MFA-pending, and stale-session attempts to complete that same observed operation. For an account-bound enrollment flow, begin in A and attempt completion in B, changing only the account/session context. Confirm the final factor owner with a fresh login. For username-less authentication, successful login to the factor's rightful owner is expected; it must not be reported as cross-account bypass.
- Exercise replacement and removal. Capture normal add, rename, delete, recovery-code regeneration, and fallback changes. In the replica, replay one sensitive factor change without the required recent proof, then with a fresh valid proof. Verify whether a newly added factor can log in and whether the removed factor still authenticates. Preserve a recovery route throughout. A factor addition that only changes a label proves nothing. Factor replacement deserves the same care as initial login. OWASP MFA management.
- Test OTP and recovery consumption separately. Use A's successful OTP once more in a fresh pending login during its validity window; use an invalid code as the negative control. Test one recovery code, then attempt the consumed code again. Regenerate the recovery-code set and check one old code. Do not brute-force codes or trigger real push/SMS floods. Record which transaction succeeded, not merely whether the numeric value appeared twice. TOTP verification must prevent reuse after successful validation; an identical code displayed during one time step is normal. RFC 6238. Saved recovery credentials also need explicit lifecycle controls. NIST authenticator management.
- Build a real passkey baseline. In the local application, open DevTools → More tools → WebAuthn, enable the virtual authenticator, select CTAP2 and the required resident-key/user-verification capabilities, then register through the application's normal page. Record the RP ID and credential owner. This tests server integration; it does not demonstrate compromise of a hardware authenticator. Chrome virtual-authenticator workflow.
- Separate signature checks from semantic checks. Compare a fresh assertion with a consumed assertion and one with an invalid signature. For username-first login, verify the credential belongs to the intended account; for discoverable credentials, verify the returned user handle resolves consistently to the credential owner. Trace the verifier's challenge, ceremony type, origin, RP-ID hash, signature, and presence/verification decisions. A local authenticator fixture can sign a deliberately mismatched case; changing signed JSON without re-signing only tests signature rejection. WebAuthn assertion fields.
- Check policy-sensitive WebAuthn branches. Verify UV when the ceremony requires user verification; a touch proving user presence does not by itself prove UV. Account for an explicitly configured related-origin deployment and applicable legacy AppID handling before calling an origin/RP mismatch a bypass. Counter anomalies are risk signals, not standalone clone proof; some authenticators keep zero counters. Synced credentials and absent attestation are not automatically weaknesses. Evaluate the deployed library and RP policy against the pinned WebAuthn Level 3 Recommendation.
Local inspection card
Export an owned test assertion in the application's JSON transport format to assertion.json. This prints selected metadata and a challenge fingerprint; it does not verify the signature or establish a vulnerability. If your wrapper uses different keys, adapt the field path.
python3 - <<'PYCODE'
import base64, hashlib, json
from pathlib import Path
x = json.loads(Path('assertion.json').read_text())
s = x['response']['clientDataJSON']
c = json.loads(base64.urlsafe_b64decode(s + '=' * (-len(s) % 4)))
print(json.dumps({
'type': c.get('type'), 'origin': c.get('origin'),
'crossOrigin': c.get('crossOrigin'),
'challenge_sha256': hashlib.sha256(c['challenge'].encode()).hexdigest()
}, indent=2))
PYCODE
Evidence and completion
Report a newly usable unauthorized factor, authentication below a required assurance level, or acceptance of a proof for the wrong account/transaction. Attach the expected rule, before/after factors, request order, independent fresh-login result, and cleanup. Missing notification alone is hardening evidence; a successful server rejection is a passed check.
- Enrollment and replacement evaluated independently.
- OTP, backup codes, and passkey ceremonies have separate controls.
- Username-first and discoverable flows distinguished.
- Signature rejection separated from signed-context validation.
- Removed test factors and credentials no longer authenticate.
- Virtual authenticator exports and live recovery secrets excluded from reports.
OAuth, OIDC, and SAML: transaction binding and token trustSeparate browser transaction binding, issuer trust, token purpose, and identity linking using controlled federation fixtures.
Open canonical module and reverse references
- 01Map issuers and clients
- 02Capture authorized flow
- 03Alter one binding
- 04Verify token semantics
- 05Check replay and linking
- 06Remove test grants
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and trust record
Use two controlled accounts, two registered lab clients when available, and a test identity provider. Record authorization server, client ID/type, exact redirect URI, grant, scopes, resource/audience, browser transaction, issuer/subject, and resulting local account. Pin deployed protocol profiles and libraries. Reuse the session lifecycle and authorization matrix; this procedure adds federation-specific decisions.
The invariant is: a valid credential for one transaction, client, issuer, or purpose must not silently authorize another. A decoded token, valid cryptographic signature, and successful login are three different observations.
Numbered procedure
- Establish browser binding. Capture a normal authorization-code login and a separate account-link operation. In the replica, deliver A's response into B's pending transaction and compare the resulting local account/link with untouched controls. Track which session initiated each transaction. Missing
statealone is not decisive when transaction-specific PKCE or another valid mechanism provides the required binding. Validate a registered redirect with exact matching, accounting for the native loopback-port exception. RFC 9700. - Check code and PKCE handling independently. On separate fresh authorization codes, compare the correct verifier, a wrong verifier, and a missing verifier. Then replay one already redeemed code. Where the flow supports PKCE, inspect whether removing the challenge can create a downgrade path. Keep client authentication, client ID, and redirect constant so rejection can be attributed correctly. A verifier is secret; a public challenge is not. Generate a new verifier for each real transaction. PKCE specification.
- Validate OIDC context. With the lab issuer, produce otherwise valid fixtures with one wrong issuer, client audience, expired time, or mismatched nonce when that flow requires it. Check profile-specific
azp, hash, and authentication-age rules rather than assuming every claim is universal. Local identity should use the issuer/subject pair; equal email claims do not establish that two identities may be merged. Compare UserInfo subject with the ID-token subject when UserInfo is used. OpenID Connect Core. - Test JWT trust and purpose. First show rejection of one invalid signature. Then use validly signed local fixtures for another resource or purpose: an ID token at an access-token endpoint, or an email-verification token at a session endpoint, where the implementation exposes that path. In source review, inspect algorithm allowlists, issuer-selected key material, key-ID lookup, and mutually exclusive validation rules. Do not turn a token-supplied key URL into a production callback experiment. A signature-valid but unauthorized token is a semantic trust failure, not necessarily broken cryptography. RFC 8725.
- Check refresh replay under the actual design. Record original and rotated refresh credentials separately. After one normal rotation, attempt the previous credential once from another local client and inspect both the returned token and a harmless resource read. If tokens are sender-constrained, test the relevant missing/wrong sender proof instead. Public-client refresh tokens require rotation or sender constraint; documented retry grace affects interpretation but does not erase demonstrated independent unauthorized access. OAuth refresh-token protection.
- Follow SAML from verified bytes to identity. In a local IdP/SP pair, compare a normal response with independently changed signed content, a wrongly targeted assertion, an expired assertion, and one replay. Trace the exact signed element from which the SP consumes identity and attributes; verifying an unrelated signed element is insufficient. For SP-initiated SSO check request correlation. For supported IdP-initiated SSO, missing request correlation is expected, but replay and recipient/trust checks still matter. Keep RelayState handling separate from assertion integrity. OWASP SAML processing.
Local command card
Prepare expected.header and other-resource.header using valid tokens minted by your lab issuer; each contains its complete Authorization: Bearer … line. The second token is intentionally valid only for a different resource under the issuer policy. Preserve the same subject and request where possible. The protected route is illustrative. These requests do not test a public provider.
mkdir -p evidence/federation
chmod 600 expected.header other-resource.header
curl -q --noproxy '*' --max-time 10 --header @expected.header \
-D evidence/federation/expected.h -o evidence/federation/expected.json \
http://127.0.0.1:8000/api/me
curl -q --noproxy '*' --max-time 10 --header @other-resource.header \
-D evidence/federation/wrong-resource.h -o evidence/federation/wrong-resource.json \
http://127.0.0.1:8000/api/me
A 401/403 is a useful control, but also inspect bodies and resulting identity. Successful use of the expected token establishes only the baseline. Token transport and audience selection must match the actual local deployment.
Evidence and checklist
Capture one invalid condition, accepted identity or capability, minimal synthetic response, and the intended trust configuration. Document any test signing keys you supplied: possession of the real IdP signing key already grants authority and is not a new bypass. A rejected code, intentional multi-audience policy, or unconsumed altered claim is not takeover evidence.
- Browser binding and token validity tested separately.
- Correct, wrong, omitted, and consumed proof cases kept distinct.
- Token purpose and issuer/audience validation separated from signature validation.
- SAML consumed identity traced to the validated signed element.
- Grants, sessions, test keys, and transient credentials removed after evidence capture.
Browser extensions: message authority and document lifetimeTrace real page messages into extension capabilities and test sender identity, navigation races, permission changes and worker restarts.
Open canonical module and reverse references
- 01Identify installed release
- 02Map message channels
- 03Capture allowed action
- 04Change sender or document
- 05Confirm recipient and effect
- 06Restore profile
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Setup and security invariant#
Use the scoped store release, a disposable browser profile and two controlled local web origins. Record extension ID/version, browser version, Manifest version and granted optional permissions. Keep a separate unpacked copy for source tracing; changing its manifest, ID or permission grants changes the reproduction assumptions. Seed one private marker and one public marker in the extension's supported lab data. Invariant: a page cannot borrow extension or native-host authority beyond the specific operation delegated to that page and document.
Procedure, controls and interpretation#
Separate the message channels. Draw page → DOM event/content script → service worker → browser API/native host. Inventory
window.postMessage, custom events,runtime.onMessage, external-message listeners and ports separately. An external-message allowlist does not control an ordinary content script forwarding page data. Use only the channel actually reachable from a normal page. Chrome documents that content scripts are a lower-trust context for extension messages. Message passing, external messaging configuration.Capture a legitimate request. Trigger one supported page action and record action name, input schema, browser-provided sender ID/origin, tab/frame/document and the protected operation. Distinguish browser-supplied metadata from fields named
origin,sender,tabIdortrustedinside the message body. A content-script sender ID identifying the extension does not establish that its forwarded page input was authorized. Keep sensitive values out of the page until its entitlement is established.Vary one authority dimension. Compare the valid page with the second controlled origin, a child frame, an unsupported action and a different known synthetic resource. Invoke through the product's real page listener; do not paste privileged extension APIs into its developer console. For a fetch broker, compare the intended public fixture with the protected marker destination and inspect who receives the response. Chrome's network guidance specifically warns against letting an untrusted sender choose arbitrary fetch destinations. Reuse browser trust and SSRF boundaries for URL normalization and redirect decisions. Extension network requests.
Test document lifetime. Pause a local mock response, navigate the initiating tab to the second origin, then release the response. Observe whether the reply or permission decision follows the original document or merely a reused tab/frame. Include same-tab navigation and a child-frame case separately. Chrome exposes
documentIdandoriginon message senders; those fields need browser-version-aware handling. The inference being tested is whether the product preserves the identity it authorized across an asynchronous gap. A dropped reply is a valid denied control. MessageSender, document-specific tab messaging.Restart and revoke. Let the service worker stop naturally or stop it through the local debugging UI; repeat the denied case after restart. Separately revoke an optional permission through supported browser controls. Record whether trusted state, grants or pending replies are re-created safely. Service-worker global variables are not durable storage. A debugging session can alter worker lifetime, so repeat the minimum case with developer tools closed. Service worker lifecycle.
Confirm the capability. Record the actual returned marker or reversible operation. For native messaging, continue with desktop IPC; distinguish extension-to-user-process access from a demonstrated administrator boundary. Broad permissions, a public resource, or a rejected unknown action are not standalone findings.
Local command sheet#
These inspect an acquired lab copy without loading it or invoking browser APIs:
python3 -I -S -m json.tool ./extension/manifest.json
rg -n 'postMessage|addEventListener|onMessage|onConnect|externally_connectable' ./extension
rg -n 'sender\.|documentId|frameId|tabs\.(sendMessage|connect)|sendNativeMessage' ./extension
rg -n 'fetch\(|host_permissions|optional_host_permissions|storage\.(local|session)' ./extension
For each match, save an evidence row: initiating origin/document, message fields, trusted metadata, decision, receiving document and resulting capability. The source scan identifies routes to exercise; a match itself proves no reachability.
Completion checklist#
- Allowed and denied origins produce the expected baseline decisions.
- The candidate works through the shipped page/content-script entry.
- Body-supplied identities are distinguished from browser metadata.
- Navigation and restart results include their event order.
- Optional permission grants and user interaction are documented.
- Remove local page fixtures and restore the disposable profile after retaining redacted evidence.
Vulnerability reference#
Broken object-level authorization (BOLA / IDOR) · Broken function-level authorization (BFLA) · Broken property authorization: private response fields · Mass assignment of server-controlled properties · Authentication or MFA state bypass · Session fixation across login · Residual access after explicit session revocation · Password reset binding or single-use failure · OAuth login or account-linking confusion · JWT signature, issuer, audience, or token-purpose validation failure · SQL injection · NoSQL operator or query injection · OS command injection · Server-side template injection (SSTI) · Reflected or stored cross-site scripting · DOM-based cross-site scripting · Cross-site request forgery (CSRF) · CORS private-data exposure · Cross-origin messaging trust failure · Server-side request forgery (SSRF) · Unsafe upload interpretation or placement · Path traversal outside an allowed root · XML external entity injection (XXE) · Archive extraction path escape · Race condition in a security-sensitive transition · Workflow order or approval bypass · Single-use action or idempotency replay failure · GraphQL resolver or field authorization failure · WebSocket message or subscription authorization failure · Cross-site WebSocket hijacking · Hard-coded product credentials · CI artifact poisoning across a workflow trust boundary · Dependency confusion and unintended package origin · Unsafe object deserialization · JavaScript prototype pollution · Out-of-bounds memory read · Out-of-bounds memory write · Use after free in a public API or parser lifecycle · Integer overflow affecting allocation or bounds · Parser resource exhaustion and decompression amplification · Regular-expression denial of service · Predictable temporary files and unsafe creation · Untrusted executable, library, or module search path · Local authentication does not gate the protected operation · Backup or transfer exposes unintended sensitive client state · Sensitive client state leaks through accessible diagnostics · TLS peer certificate or hostname verification failure · Electron IPC exposes main-process authority to an untrusted sender · Desktop protocol handler misinterprets external input · Updater accepts an artifact outside its publisher trust policy · Privileged updater consumes lower-user-controlled staging content · Attacker-controlled rollback restores a prohibited security state · Predictable security token or capability identifier · Password storage without an adequate adaptive hash · Encrypted data accepted without integrity authentication · Unauthorized authenticator enrollment, replacement, or removal · WebAuthn assertion accepted for an invalid account or ceremony context · Verification or account merge preserves another actor's access · Enforced SSO bypass through an alternate login path · Argument or option injection without shell interpretation · Page message borrows unintended browser-extension authority · Delayed extension result reaches a different document · Private client content leaks through a lower-trust display or sharing channel
Scope and learning route#
Desktop scope must identify the product, platform, architecture, installer/package format, release channel, version, and publisher identity. Include separate entries for installers, updaters, background services, privileged helpers, plugins, browser extensions, native-messaging hosts, and local APIs. For extensions, record store listing, extension ID, version, browser build, Manifest version, and whether the tested copy is store-installed or unpacked. Explicitly distinguish a user application, system service, browser extension, and operating-system vulnerability.
An OSS clone supports local source review; it does not establish authorization for the vendor’s update infrastructure. An installer available for download does not automatically make all distribution hosts or dependencies bounty-eligible. Record whether local privilege escalation, user-assisted file attacks, denial of service, enterprise-only deployment, beta releases, and unsupported versions qualify before committing time.
Recommended progression: installer/package inspection → process and privilege mapping → local IPC and protocol handlers → update verification → Electron/extension bridges → native parser fuzzing. The useful skill is explaining precisely which lower-trust principal gains a higher-trust capability.
Lab and evidence model#
Use disposable Windows and Linux VMs and an owned Mac or supported macOS virtualization environment. Snapshot before installation; use a standard user for attacker modeling and a separate administrative account only for setup. Keep shared folders and personal credentials out of the experiment. Instrument only the target’s processes and synthetic files where practical.
Create a boundary ledger: entry point, attacker control, consumer identity, validation, protected capability, observation, and negative control. Local administrator modification of an application is not evidence of privilege escalation. A missing DLL in a trace, broad file permission, or unsigned resource becomes significant only when a lower-privileged actor can influence a path actually trusted by a more privileged consumer. Windows Process Monitor records file, registry, and process activity useful for establishing that chain. Microsoft Process Monitor.
Native desktop playbook#
- Inventory installation and updates. Compare package contents, install destination, post-install actions, services/helpers, scheduled tasks, and registration of custom schemes/file types. Observe a normal install/update in the snapshot. Gate: retain exact installer hash, version, signer, and default configuration.
- Map local authority. On Windows, review service configuration, named-pipe access, registry and filesystem ACLs. On macOS, inspect launch agents/daemons, XPC services, entitlements, helper identity validation, and container boundaries. On Linux, examine service users, Unix sockets, D-Bus/polkit interfaces, package scripts, capabilities, file ownership, and writable search paths. Gate: identify a real lower-trust caller or writable object used by the target, rather than assuming every local IPC endpoint is unprotected.
- Trace file and command inputs. Review document import, archive extraction, plugin loading, URL handlers, temporary files, argument construction, and working-directory assumptions. Use unique harmless markers in a dedicated test directory. Gate: show the unintended operation and effective identity; do not infer command execution from a parsing error.
- Verify the update trust chain. Determine whether metadata, downloaded bytes, publisher identity, and applicable version/rollback policy are verified before installation or execution. Model tampered artifacts only in a local updater harness or explicitly supported test feed. Gate: a production-equivalent decision accepts an unauthorized artifact or violates the intended version policy. A valid outer installer signature does not prove all subsequent update decisions are correct. Apple’s archived code-signing note supports signature inspection, but modern runtime/notarization behavior must be verified on the actual supported OS. Apple TN2206.
- Fuzz exposed native parsers. Select a reachable file/protocol parser, isolate it behind a deterministic harness, seed representative valid inputs, and measure coverage. Use an instrumented local build and suitable sanitizers; bound CPU, memory, disk, and runtime. Gate: minimize and reproduce the crash, identify root cause, and explain how an attacker delivers the input to a supported release. A crash alone does not prove code execution or bounty eligibility. AFL++ fuzzing guide.
- Retest with realistic privileges. Restore the clean snapshot and reproduce the smallest effect without debugger-granted access or administrative preconfiguration that the attack would not possess. Include user interaction, restart requirements, and deployment differences in the report.
Electron and browser-extension playbook#
For Electron, map remote/local content → renderer → preload/context bridge → main-process IPC → filesystem, shell, credentials, or privileged helper. Inspect window-specific settings rather than assuming defaults apply everywhere. Trace navigation, child windows, custom protocols, shell.openExternal, and permission decisions. Validate IPC sender frame/origin and arguments; contextIsolation: true does not repair a bridge exposing arbitrary privileged methods. Gate: demonstrate an attacker-controlled input reaching a protected operation through the shipped bridge, rather than executing code manually in developer tools. Electron security, Context Isolation.
For extensions, map page → content script → service worker/extension page → privileged browser API. Inspect host permissions, optional permissions, externally_connectable, web-accessible resources, CSP, and message handlers. Check sender identity, origin/frame association, action allowlists, and argument constraints across asynchronous navigation. Broad permissions increase the potential impact but do not themselves demonstrate abuse. Gate: a controlled web page or authorized test extension causes an unintended privileged action without first receiving privileges unavailable to a realistic attacker. Chrome extension security, Message passing.
If a native-messaging host exists, extend the analysis into its manifest, allowed extension origins, executable location, framing/parser, and command handling. The native host runs outside the browser process; extension-side validation is only one part of the chain. Gate: preserve the complete web-to-extension-to-native sequence and its actual privileges, or report only the narrower verified boundary. Chrome native messaging.
Desktop hypotheses#
| Hypothesis | Local validation | Evidence gate |
|---|---|---|
| A privileged service trusts a writable file | Trace one normal operation and inspect its ACL | Standard-user control, consumer identity, meaningful effect |
| Update verification covers the wrong object | Exercise a local fixture through equivalent verification code | Accepted bytes, expected signer/policy, acceptance point |
| URL/file handler misinterprets input | Use a synthetic URI/document with a harmless marker | Entry path, argument interpretation, resulting operation |
| Electron IPC trusts an untrusted frame | Trace a controlled frame through a shipped bridge | Sender origin, method reachability, protected capability |
| Extension forwards arbitrary privileged requests | Exercise a local page through its normal content script | Real sender, requested action, cross-boundary result |
| Native parser has a memory-safety defect | Minimize a local sanitizer-triggering input | Reproducible fault, root cause, supported attack path |
Desktop checklist#
Native applications: apply each item to Windows, macOS, or Linux as relevant.
- Product, platform, architecture, channel, version, hash, signer recorded.
- Installer, updater, service/helper, and plugin scope separated.
- Clean snapshot and standard-user baseline captured.
- Default installation paths and post-install actions mapped.
- Service/helper effective identity documented.
- IPC caller authentication and object-level authorization reviewed.
- Writable executable, configuration, temporary, and search paths checked.
- Windows registry/filesystem ACLs and named-pipe access inspected.
- macOS XPC, launch services, entitlements, and signing checks inspected.
- Linux sockets, D-Bus/polkit, service users, capabilities, and ownership inspected.
- Protocol handlers, file associations, imports, and archive boundaries exercised.
- Update metadata, artifact, signer, and rollback policy traced.
- Parser fuzzing limited to a local harness with resource limits.
- Crashes minimized; impact distinguished from code-execution speculation.
- Reproduction repeated without artificial administrator privileges.
Electron and extensions
- Shipped Electron/browser version and extension ID/version captured.
- Store-installed and modified/unpacked evidence distinguished.
- Every renderer/window’s security settings inspected.
- Context isolation and sandbox assumptions verified.
- Preload methods and main-process IPC handlers inventoried.
- IPC sender frame/origin and argument checks inspected.
- Navigation, child-window, and external-protocol handling reviewed.
- Host/API/optional permissions and intended usage mapped.
- External messaging and web-accessible resources restricted appropriately.
- Page-origin data treated as untrusted through content-script messages.
- Privileged service-worker actions require bounded inputs.
- Native-messaging host origin list, path, and parser reviewed.
- Local browser profile contains only synthetic application data.
- Result reproduced through a realistic entry point, without console-only access.
Desktop command cheat sheet#
These are local inspection examples. Paths refer to lab copies. A successful signature check establishes signature validity under the tool’s rules, not absence of vulnerabilities. PowerShell Authenticode inspection is Windows-specific; Apple commands require macOS. Microsoft Authenticode, icacls, Apple signature verification, GNU readelf.
# Windows / PowerShell: inspection only.
Get-AuthenticodeSignature -FilePath 'C:\Lab\App.exe' | Format-List
icacls 'C:\Lab\App.exe'
icacls 'C:\Program Files\ExampleApp'
# macOS: inspect a lab copy; do not re-sign it for the baseline.
plutil -p ./Example.app/Contents/Info.plist
codesign -dv --verbose=4 ./Example.app
codesign -d --entitlements :- ./Example.app
codesign --verify --deep --strict --verbose=2 ./Example.app
# Linux or another host with GNU binutils: read ELF metadata without execution.
readelf -hW ./lab-app
readelf -lW ./lab-app
readelf -dW ./lab-app
# Search local source/extracted JavaScript; matches are investigation leads.
rg -n 'ipcMain|contextBridge|nodeIntegration|contextIsolation|openExternal' ./source
rg -n 'onMessage|externally_connectable|host_permissions|nativeMessaging' ./extension
For an already prepared AFL++ lab, first inspect afl-fuzz -h. A campaign skeleton is afl-fuzz -i ./seeds -o ./fuzz-results -- ./parser-harness @@; @@ supplies the current input filename. It runs until stopped unless bounded by supported runtime options. Configure limits before starting, and interrupt with Ctrl-C. The harness must avoid network calls, production credentials, and side effects outside the disposable lab. AFL++ execution and resource guidance.
Extension cases to complete before closing the review#
The extension message procedure adds a full case matrix for page forwarding, browser-supplied identity, changed document after navigation, worker restart and optional permission revocation. Run it before native IPC if the product contains an extension. Chrome's message metadata and service-worker lifetime are separate concerns; preserve both in the evidence. MessageSender, worker lifecycle.
| Boundary | Positive control | Negative control |
|---|---|---|
| Page to extension | Intended action from allowed page | Same action from second controlled origin |
| Delayed response | Original document remains | Tab navigates before result delivery |
| Worker lifetime | Authorized operation before stop | Denied operation remains denied after restart |
| Optional permission | User grants required capability | Capability revoked through browser controls |
Backend sign-in shared with mobile/web belongs in federation/token trust and authenticator lifecycle. Preserve the native callback or extension route in the reproduction instead of reporting only a generic server test.
Sources#
All sources accessed 2026-09-11. “Undated” means no reliable publication/update date was established from the retrieved page. Dates are page metadata, not a guarantee of completeness for every current OS version. Archived Apple sources are used for established mechanisms and command syntax, not blanket claims about current platform enforcement.
| ID | Publisher / exact source | Date | Use |
|---|---|---|---|
| MD01 | OWASP, OWASP MASVS | Undated; living standard | Mobile control organization |
| MD02 | OWASP, OWASP MASTG | Undated; living guide | Testing methodology and technique catalog |
| MD03 | OWASP, Mobile Application Security Weakness Enumeration | Undated; living catalog | Weakness taxonomy and version caution |
| MD04 | Google, Android Debug Bridge | Updated 2026-09-02 | Device/package inspection commands |
| MD05 | Google, apkanalyzer | Updated 2026-03-05 | APK metadata and manifest CLI |
| MD06 | Google, apksigner | Updated 2026-03-05 | APK certificate/signature inspection |
| MD07 | Google, android:exported | 2024-09-24, indexed page date | Component exposure and version dependence |
| MD08 | Google, Unsafe use of deep links | 2024-10-24, indexed page date | Deep-link validation |
| MD09 | Google, WebView – Native bridges | Updated 2024-10-15 | Frames, native bridges, origin boundaries |
| MD10 | Google, Network security configuration | Updated 2026-08-28 | Trust anchors and debug overrides |
| MD11 | Google, Content provider basics | Undated in this registry | Provider access model |
| MD12 | skylot/JADX maintainers, JADX repository and CLI | Undated; living README | Decompiler usage and limitations |
| MD13 | Apktool maintainers, CLI Parameters | Undated | Local package decoding |
| MD14 | OWASP, iOS Security Testing | Undated | iOS lab/platform distinctions |
| MD15 | Apple, Keychain data protection | Published 2024-12-19 | Access groups, classes, authentication constraints |
| MD16 | Apple, Support Universal Links | Updated 2016-12-15; archived | App/website association model |
| MD17 | OWASP, MASTG-TOOL-0062: Plutil | Undated | Property-list inspection tooling |
| MD18 | OWASP, MASTG-TECH-0111: Extracting Entitlements from MachO Binaries | Undated | Signing/entitlement inspection |
| MD19 | Apple, TN2206: macOS Code Signing In Depth | Page updated 2016-09-13; archived | Signature verification command and boundaries |
| MD20 | Microsoft / Mark Russinovich, Process Monitor | Published 2026-09-10 | Local process, registry, filesystem observation |
| MD21 | Microsoft, Get-AuthenticodeSignature | Undated; PowerShell 7.5 documentation | Windows signature inspection |
| MD22 | Microsoft, icacls | Updated 2025-06-09 | Windows ACL inspection |
| MD23 | OpenJS Foundation / Electron contributors, Security | Undated; living documentation | Renderer, navigation, IPC boundaries |
| MD24 | Electron contributors, Context Isolation | Undated | Bridge limitations and API exposure |
| MD25 | Google, Stay secure | Page states updated 2018-03-06 | Extension permissions and untrusted messages |
| MD26 | Google, Message passing | Page states updated 2025-12-03 | Extension messaging contexts |
| MD27 | Google, Native messaging | Page states updated 2023-02-27 | Native host manifest and execution boundary |
| MD28 | AFL++ maintainers, Fuzzing in Depth | Undated; living guide | Harness, corpus, execution, resource limits |
| MD29 | GNU Binutils / Sourceware, readelf | Undated | Non-executing ELF metadata inspection |