





















@@ -34,29 +34,68 @@ export function resolvePairingCredentialsDir(env: NodeJS.ProcessEnv = process.en
3434return resolveOAuthDir(env, stateDir);
3535}
363637-/** Sanitize channel ID for use in filenames (prevent path traversal). */
38-export function safeChannelKey(channel: PairingChannel): string {
39-const raw = normalizeLowercaseStringOrEmpty(String(channel));
37+type PairingFilenameKeyKind = "channel" | "account id";
38+39+function describePairingFilenameKeyInput(value: unknown): string {
40+if (value === null) {
41+return "null";
42+}
43+if (Array.isArray(value)) {
44+return "array";
45+}
46+if (typeof value === "string") {
47+const trimmed = value.trim();
48+return trimmed ? `string length ${trimmed.length}` : "empty string";
49+}
50+if (typeof value === "number" && !Number.isFinite(value)) {
51+return "non-finite number";
52+}
53+return typeof value;
54+}
55+56+function invalidPairingFilenameKeyError(
57+kind: PairingFilenameKeyKind,
58+reason: string,
59+value: unknown,
60+): Error {
61+return new Error(
62+`invalid pairing ${kind}: ${reason}; got ${describePairingFilenameKeyInput(value)}`,
63+);
64+}
65+66+function normalizePairingFilenameKey(value: unknown, kind: PairingFilenameKeyKind): string {
67+if (typeof value !== "string") {
68+throw invalidPairingFilenameKeyError(kind, "expected non-empty string", value);
69+}
70+const raw = normalizeLowercaseStringOrEmpty(value);
4071if (!raw) {
41-throw new Error("invalid pairing channel");
72+throw invalidPairingFilenameKeyError(kind, "expected non-empty string", value);
4273}
4374const safe = raw.replace(/[\\/:*?"<>|]/g, "_").replace(/\.\./g, "_");
4475if (!safe || safe === "_") {
45-throw new Error("invalid pairing channel");
76+throw invalidPairingFilenameKeyError(kind, "sanitized filename key is empty", value);
4677}
4778return safe;
4879}
498081+/** Sanitize channel ID for use in filenames (prevent path traversal). */
82+export function safeChannelKey(channel: PairingChannel): string {
83+return normalizePairingFilenameKey(channel, "channel");
84+}
85+5086function safeAccountKey(accountId: string): string {
51-const raw = normalizeLowercaseStringOrEmpty(accountId);
52-if (!raw) {
53-throw new Error("invalid pairing account id");
87+return normalizePairingFilenameKey(accountId, "account id");
88+}
89+90+function resolveOptionalAccountFilenameKey(accountId: unknown): string | null {
91+if (accountId == null) {
92+return null;
5493}
55-const safe = raw.replace(/[\\/:*?"<>|]/g, "_").replace(/\.\./g, "_");
56-if (!safe || safe === "_") {
57-throw new Error("invalid pairing account id");
94+if (typeof accountId !== "string") {
95+throw invalidPairingFilenameKeyError("account id", "expected non-empty string", accountId);
5896}
59-return safe;
97+const normalizedAccountId = normalizeOptionalString(accountId) ?? "";
98+return normalizedAccountId ? safeAccountKey(normalizedAccountId) : null;
6099}
6110062101export function resolveAllowFromFilePath(
@@ -65,14 +104,11 @@ export function resolveAllowFromFilePath(
65104accountId?: string,
66105): string {
67106const base = safeChannelKey(channel);
68-const normalizedAccountId = normalizeOptionalString(accountId) ?? "";
69-if (!normalizedAccountId) {
107+const accountKey = resolveOptionalAccountFilenameKey(accountId);
108+if (!accountKey) {
70109return path.join(resolvePairingCredentialsDir(env), `${base}-allowFrom.json`);
71110}
72-return path.join(
73-resolvePairingCredentialsDir(env),
74-`${base}-${safeAccountKey(normalizedAccountId)}-allowFrom.json`,
75-);
111+return path.join(resolvePairingCredentialsDir(env), `${base}-${accountKey}-allowFrom.json`);
76112}
7711378114export function dedupePreserveOrder(entries: string[]): string[] {
@@ -94,6 +130,9 @@ export function shouldIncludeLegacyAllowFromEntries(normalizedAccountId: string)
94130}
9513196132export function resolveAllowFromAccountId(accountId?: string): string {
133+if (accountId != null && typeof accountId !== "string") {
134+throw invalidPairingFilenameKeyError("account id", "expected non-empty string", accountId);
135+}
97136return normalizeLowercaseStringOrEmpty(accountId) || DEFAULT_ACCOUNT_ID;
98137}
99138此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。