Files
wehub-resource-sync 79031da543
Spell checking / Report (Push) (push) Blocked by required conditions
Spell checking / Report (PR) (push) Blocked by required conditions
Spell checking / Check Spelling (push) Has been cancelled
Spell checking / Update PR (push) Has been cancelled
Publish Dev Docs Website / build (push) Failing after 1s
Publish Dev Docs Website / deploy (push) Has been skipped
chore: import upstream snapshot with attribution
2026-07-13 12:16:02 +08:00

78 lines
2.7 KiB
JavaScript

// Regression test for the GITHUB_TOKEN allowlist (security fix).
// Run with: node test-token-allowlist.js
//
// Proves that the bearer token is attached ONLY to allowlisted GitHub API hosts
// and is NEVER sent to arbitrary URLs extracted from untrusted issue content.
import assert from "node:assert/strict";
import { shouldSendGitHubToken, headersForUrl } from "./auth.js";
const TOKEN = "PT_MCP_TEST_FAKE_TOKEN";
let failures = 0;
function check(name, fn) {
try {
fn();
console.log(` ok - ${name}`);
} catch (err) {
failures++;
console.error(` FAIL - ${name}: ${err.message}`);
}
}
// Hosts that MUST receive the token.
const allowed = [
"https://api.github.com/repos/microsoft/PowerToys/issues/1",
"https://api.github.com/repos/microsoft/PowerToys/issues/1/comments?per_page=100&page=1"
];
// Hosts that MUST NOT receive the token (attacker-controlled or non-API hosts,
// including GitHub's own user-content hosts which never need the API token).
const denied = [
"http://127.0.0.1:8000/capture.png",
"http://127.0.0.1:8000/PowerToysReport.zip?download=1",
"https://evil.example.com/capture.png",
"https://evil.example.com/PowerToysReport.zip",
"http://api.github.com/repos/microsoft/PowerToys/issues/1", // not HTTPS
"https://api.github.com.attacker.com/x", // look-alike host
"https://objects.githubusercontent.com/some/asset.zip",
"https://user-images.githubusercontent.com/1/screenshot.png",
"not a url"
];
for (const url of allowed) {
check(`token sent to allowlisted host: ${url}`, () => {
assert.equal(shouldSendGitHubToken(url), true);
const h = headersForUrl(url, TOKEN);
assert.equal(h["Authorization"], `Bearer ${TOKEN}`);
assert.equal(h["User-Agent"], "issue-images-mcp");
});
}
for (const url of denied) {
check(`token withheld from untrusted host: ${url}`, () => {
assert.equal(shouldSendGitHubToken(url), false);
const h = headersForUrl(url, TOKEN);
assert.equal("Authorization" in h, false, "Authorization header must not be present");
assert.equal(h["User-Agent"], "issue-images-mcp");
});
}
check("no token configured => no Authorization even for allowlisted host", () => {
const h = headersForUrl("https://api.github.com/x", undefined);
assert.equal("Authorization" in h, false);
});
check("extra headers are preserved", () => {
const h = headersForUrl("https://api.github.com/x", TOKEN, { "Accept": "application/vnd.github+json" });
assert.equal(h["Accept"], "application/vnd.github+json");
assert.equal(h["Authorization"], `Bearer ${TOKEN}`);
});
console.log("");
if (failures > 0) {
console.error(`${failures} test(s) failed.`);
process.exit(1);
}
console.log("All token-allowlist regression tests passed.");