Files
UHD_Media_Tool/packages/adapters/src/redact-consistency.test.ts
T
rootandClaude Opus 5.5 47bd04aec0 fix(adapters,security): S-4 rss/browser redaction use the shared contracts key table
- contracts: isSensitiveQueryParam (whole-name match on SENSITIVE_QUERY_PARAMS
  or isSensitiveKey), the single source for query credential names
- rss/redact: drop local SENSITIVE_QUERY_KEYS; param checks go through
  isSensitiveQueryParam (+ caller extras); REDACTED re-exported from contracts
- browser/redact: CREDENTIAL_PARAM built from SENSITIVE_QUERY_PARAMS, JSON keys
  also checked with isSensitiveKey; keeps only its own rules (path passkey,
  cookie names, a11y password fields); remove stray regex statement
- redact-consistency.test: 24 credential spellings (pass_key, c_secure_pass,
  refresh_token, password, uid_key, auth, ...) masked by contracts, rss and
  browser alike; plain params untouched; rss extract/unredact round-trip

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 00:45:56 +08:00

72 lines
2.3 KiB
TypeScript

import { isSensitiveQueryParam, redactUrl as redactUrlShared } from "@umt/contracts";
import { describe, expect, it } from "vitest";
import { redactBrowserUrl, urlHasCredential } from "./browser/redact.ts";
import { extractSecretParams, redactUrl as redactRssUrl, redactText, unredactUrl } from "./rss/redact.ts";
// 验收 07 S-4:脱敏键表只有一份(@umt/contracts redact.ts)。contracts(logger + 模型上下文)、rss、browser
// 三处对同一组凭证参数给出一致结论;任何一处漏遮都会让 passkey 进日志或模型。
const SECRET = "s3cr3tVALUE42";
const CREDENTIAL_PARAMS = [
"passkey",
"pass_key",
"pass-key",
"PassKey",
"authkey",
"auth_key",
"torrent_pass",
"rsskey",
"rss_key",
"c_secure_pass",
"downhash",
"apikey",
"api_key",
"access_token",
"refresh_token",
"token",
"auth",
"sign",
"signature",
"credential",
"secret",
"password",
"uid_key",
"key",
];
const PLAIN_PARAMS = ["id", "cat", "page", "q", "tokens", "limit", "type"];
describe("脱敏键表三处一致", () => {
it.each(CREDENTIAL_PARAMS)("凭证参数 %s:contracts / rss / browser 都遮蔽", (p) => {
const url = `https://pt.example.org/rss.php?id=7&${p}=${SECRET}&cat=401`;
expect(isSensitiveQueryParam(p)).toBe(true);
for (const out of [
redactUrlShared(url),
redactRssUrl(url),
redactText(`GET ${url} failed`),
redactBrowserUrl(url),
]) {
expect(out, out).not.toContain(SECRET);
expect(out).toContain("id=7");
}
expect(urlHasCredential(url)).toBe(true);
// rss 的去重键可还原:取出的 secret 正好填回被遮的参数
const secrets = extractSecretParams(url);
expect(Object.values(secrets)).toContain(SECRET);
expect(unredactUrl(redactRssUrl(url), secrets)).toContain(`${p}=${SECRET}`);
});
it.each(PLAIN_PARAMS)("普通参数 %s:三处都不遮", (p) => {
const url = `https://pt.example.org/torrents.php?${p}=abc123`;
expect(isSensitiveQueryParam(p)).toBe(false);
expect(redactUrlShared(url)).toBe(url);
expect(redactRssUrl(url)).toBe(url);
expect(redactBrowserUrl(url)).toBe(url);
});
it("URL userinfo 三处都抹掉", () => {
const url = `https://user:${SECRET}@pt.example.org/x`;
for (const out of [redactUrlShared(url), redactRssUrl(url), redactBrowserUrl(url)])
expect(out).not.toContain(SECRET);
});
});