Skip to content

fix(api): prevent cross-origin credential leakage#207

Merged
pchuri merged 2 commits into
mainfrom
fm/review-confcli-k4
Jun 24, 2026
Merged

fix(api): prevent cross-origin credential leakage#207
pchuri merged 2 commits into
mainfrom
fm/review-confcli-k4

Conversation

@pchuri

@pchuri pchuri commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Intent

The developer first wanted an autonomous, evidence-backed improvement audit of the confluence-cli Node.js codebase, grounded in npm install, tests, lint, npm audit, npm outdated, and direct code investigation, with a standalone prioritized report grouped by High/Medium/Low. After the audit, they promoted only the F1 finding to a shipping task: fix the api/rawRequest credential-origin leak by enforcing same-origin for absolute URLs, starting from a clean base on branch fm/review-confcli-k4 and adding a regression test. They explicitly constrained the work to that single security fix, with no unrelated changes, no pushing or PR during the initial scout phase, and work confined to the disposable worktree except for specified report/status files. For validation, they wanted the no-mistakes pipeline driven through to completion, using the plain git push workaround to trigger the gate, then monitoring/responding to any gate feedback and reporting the final PR URL once checks were green.

What Changed

  • Added a same-origin guard for absolute URLs passed through the API/raw request path so credentials are only sent to the configured Confluence origin.
  • Updated API command handling and Confluence client behavior to reject cross-origin full URLs while preserving configured-origin absolute URL support.
  • Documented the origin restriction and added regression coverage for both the API origin guard and Confluence client request behavior.

Risk Assessment

✅ Low: The change is narrowly scoped to applying an existing same-origin guard before authenticated rawRequest full-URL calls, with matching CLI/README messaging and focused regression coverage.

Testing

Installed dependencies from the lockfile, ran the targeted raw client and API command tests, captured CLI/server evidence showing same-origin absolute URLs succeed with Basic auth while cross-origin absolute URLs are rejected before the foreign server receives a request, then removed transient node_modules from the worktree.

Evidence: CLI origin guard transcript
# confluence api origin guard manual verification

Configured origin: http://127.0.0.1:58915
Foreign origin: http://127.0.0.1:58916

$ node bin/index.js api http://127.0.0.1:58915/rest/api/content/1 --include
exit: 0
signal: <none>
error: <none>
timedOut: false
stdout:
HTTP 200
content-type: application/json
date: Wed, 24 Jun 2026 11:45:46 GMT
connection: keep-alive
keep-alive: timeout=5
transfer-encoding: chunked

{
  "ok": true,
  "label": "configured-origin",
  "path": "/rest/api/content/1"
}
stderr:
<empty>

$ node bin/index.js api http://127.0.0.1:58916/exfil
exit: 1
signal: <none>
error: <none>
timedOut: false
stdout:
<empty>
stderr:
Error: Refusing to send credentials to "http://127.0.0.1:58916": origin does not match the configured Confluence origin "http://127.0.0.1:58915". This may indicate a tampered or misconfigured API response, or an http downgrade against an https-configured client.

Observed server requests (credential values redacted at capture time):
{
  "configured": [
    {
      "label": "configured-origin",
      "method": "GET",
      "url": "/rest/api/content/1",
      "authorizationPresent": true,
      "authorizationScheme": "Basic",
      "cookiePresent": false
    }
  ],
  "foreign": []
}

Assertions:
{
  "sameOriginSucceeded": true,
  "sameOriginReachedConfiguredServerWithBasicAuth": true,
  "crossOriginRejected": true,
  "foreignServerReceivedNoRequests": true
}
Evidence: Redacted server request log
{
  "configuredOrigin": "http://127.0.0.1:58915",
  "foreignOrigin": "http://127.0.0.1:58916",
  "sameOrigin": {
    "command": "node bin/index.js api http://127.0.0.1:58915/rest/api/content/1 --include",
    "exitCode": 0,
    "signal": null,
    "error": null,
    "timedOut": false,
    "stdout": "HTTP 200\ncontent-type: application/json\ndate: Wed, 24 Jun 2026 11:45:46 GMT\nconnection: keep-alive\nkeep-alive: timeout=5\ntransfer-encoding: chunked\n\n{\n  \"ok\": true,\n  \"label\": \"configured-origin\",\n  \"path\": \"/rest/api/content/1\"\n}",
    "stderr": ""
  },
  "crossOrigin": {
    "command": "node bin/index.js api http://127.0.0.1:58916/exfil",
    "exitCode": 1,
    "signal": null,
    "error": null,
    "timedOut": false,
    "stdout": "",
    "stderr": "Error: Refusing to send credentials to \"http://127.0.0.1:58916\": origin does not match the configured Confluence origin \"http://127.0.0.1:58915\". This may indicate a tampered or misconfigured API response, or an http downgrade against an https-configured client."
  },
  "receivedRequests": {
    "configured": [
      {
        "label": "configured-origin",
        "method": "GET",
        "url": "/rest/api/content/1",
        "authorizationPresent": true,
        "authorizationScheme": "Basic",
        "cookiePresent": false
      }
    ],
    "foreign": []
  },
  "assertions": {
    "sameOriginSucceeded": true,
    "sameOriginReachedConfiguredServerWithBasicAuth": true,
    "crossOriginRejected": true,
    "foreignServerReceivedNoRequests": true
  }
}

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • npm ci
  • npm test -- --runTestsByPath tests/api-origin-guard.test.js tests/confluence-client.test.js --runInBand
  • npm test -- --runTestsByPath tests/api-command.test.js --runInBand
  • Manual Node harness started configured and foreign local HTTP origins, then ran node bin/index.js api http://127.0.0.1:&lt;configured&gt;/rest/api/content/1 --include and node bin/index.js api http://127.0.0.1:&lt;foreign&gt;/exfil; transcript and redacted request log were written to the evidence directory.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

pchuri added 2 commits June 24, 2026 19:56
…ential leak

A full URL passed to `confluence api` was sent through the authenticated
axios client, whose default headers carry the Authorization (and Cookie)
credentials. A URL pointing at a different origin — or an http:// downgrade
of an https host — therefore leaked the API token to an arbitrary server.

Guard the absolute-URL branch of rawRequest with assertSameOrigin, mirroring
the existing download-path protection. Relative and absolute-path endpoints
resolve against the configured host and are unaffected, so legitimate use
(including scoped tokens on api.atlassian.com) keeps working.
@pchuri pchuri merged commit 1d614b2 into main Jun 24, 2026
6 checks passed
github-actions Bot pushed a commit that referenced this pull request Jun 24, 2026
## [2.14.1](v2.14.0...v2.14.1) (2026-06-24)

### Bug Fixes

* **api:** prevent cross-origin credential leakage ([#207](#207)) ([1d614b2](1d614b2))
* clarify bodyless Confluence content errors ([#206](#206)) ([62fee88](62fee88))
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.14.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant