mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-26 09:02:11 +03:00
CodeQL js/incomplete-url-substring-sanitization, alerts #860 and #861: volcengineConsoleAutoLogin accepted any cookie whose `domain` merely *contained* "volcengine.com". That check is an authorization decision, not a string test. The console auto-login harvests `digest`, `AccountID`, `csrfToken` and `userInfo` out of the Playwright context and persists them as the operator's Volcengine credentials, so a cookie set by `volcengine.com.attacker.tld` — or `notvolcengine.com` — was captured and stored as a provider connection. Add `matchesCookieDomain()` (open-sse/utils/cookieDomain.ts): exact host or dot-boundary suffix, leading dots and case normalized on both sides, failing closed on an empty expected domain. Same shape as the existing `isAdobeCookieDomain` in adobeFireflyBrowserLogin.ts, which already got this right. While sweeping the class, inAppLoginService's cookie capture had the identical weakness — `c.domain.includes(domain.replace(/^\./, ""))` — with the identical consequence: a look-alike host's cookie stored as the operator's credential. CodeQL did not flag it because the expected domain comes from TOKEN_EXTRACTION_CONFIGS rather than a literal. Both callsites now share the helper. tests/unit/volcengine-cookie-domain-suffix.test.ts — 5 tests, red before the fix, covering the real domains, seven look-alikes, empty/missing input, and the config-supplied path. Co-authored-by: Xiangzhe <bakryun0718@proton.me>
35 lines
1.3 KiB
TypeScript
35 lines
1.3 KiB
TypeScript
/**
|
|
* Cookie-domain matching for browser-driven credential capture.
|
|
*
|
|
* Every in-app / console login flow harvests cookies out of a Playwright
|
|
* context and persists them as operator credentials, so "is this cookie from
|
|
* the site I sent the browser to?" is an authorization decision. A substring
|
|
* test is not one: `domain.includes("example.com")` also accepts
|
|
* `example.com.attacker.tld` and `notexample.com`, which lets a look-alike host
|
|
* hand us cookies we then store as the operator's real credentials
|
|
* (CodeQL js/incomplete-url-substring-sanitization).
|
|
*
|
|
* A cookie domain is matched by exact host or dot-boundary suffix — nothing
|
|
* else. Leading dots (the RFC 6265 "domain-matches any subdomain" spelling) and
|
|
* case are normalized away on both sides.
|
|
*/
|
|
export function matchesCookieDomain(
|
|
cookieDomain: string | undefined,
|
|
expectedDomain: string | undefined
|
|
): boolean {
|
|
const expected = normalizeCookieDomain(expectedDomain);
|
|
if (!expected) return false;
|
|
|
|
const actual = normalizeCookieDomain(cookieDomain);
|
|
if (!actual) return false;
|
|
|
|
return actual === expected || actual.endsWith(`.${expected}`);
|
|
}
|
|
|
|
function normalizeCookieDomain(domain: string | undefined): string {
|
|
return String(domain || "")
|
|
.trim()
|
|
.replace(/^\.+/, "")
|
|
.toLowerCase();
|
|
}
|