fix(rc5): harden frontend runtime and diagnostics
This commit is contained in:
@@ -12,6 +12,17 @@
|
||||
|
||||
export const REDACTED = "<redacted>";
|
||||
|
||||
/**
|
||||
* ENV-флаги, чьи имена похожи на секрет, но значения являются диагностически
|
||||
* полезным публичным состоянием. Исключения здесь только точные: общий
|
||||
* детектор секретов нельзя ослаблять совпадением по префиксу или суффиксу.
|
||||
*/
|
||||
const NON_SECRET_ENV_KEYS = new Set(["HY2XS_FORCE_PASSWORD_CHANGE"]);
|
||||
|
||||
/** Сохранённый контракт прежнего ENV-детектора, но применённый после разбора key. */
|
||||
const ENV_SECRET_KEY_PATTERN =
|
||||
/(PASSWORD|PASSWD|PASSPHRASE|PASS|SECRET|TOKEN|CREDENTIAL|APIKEY|AUTHORIZATION|COOKIE|BEARER|SIGNATURE)/;
|
||||
|
||||
/**
|
||||
* Маркеры имён полей. Список покрывает известные секреты HY2XS/Hysteria и
|
||||
* типовые имена, под которыми секрет может появиться в будущем upstream-поле.
|
||||
@@ -36,11 +47,14 @@ export const SECRET_KEY_MARKERS: readonly string[] = [
|
||||
"authorization",
|
||||
"cookie",
|
||||
"bearer",
|
||||
"signature"
|
||||
"signature",
|
||||
];
|
||||
|
||||
/** Узлы, где секретом являются все значения карты, а не имя ключа. */
|
||||
const SECRET_MAP_PATHS: readonly string[] = ["auth.userpass", "acme.dns.config"];
|
||||
const SECRET_MAP_PATHS: readonly string[] = [
|
||||
"auth.userpass",
|
||||
"acme.dns.config",
|
||||
];
|
||||
|
||||
export function isSecretKey(key: string): boolean {
|
||||
const lowered = key.toLowerCase();
|
||||
@@ -92,7 +106,9 @@ function redactValueDeep(value: unknown): unknown {
|
||||
}
|
||||
if (value && typeof value === "object") {
|
||||
const out: Record<string, unknown> = {};
|
||||
for (const [key, child] of Object.entries(value as Record<string, unknown>)) {
|
||||
for (const [key, child] of Object.entries(
|
||||
value as Record<string, unknown>
|
||||
)) {
|
||||
out[key] = redactValueDeep(child);
|
||||
}
|
||||
return out;
|
||||
@@ -138,8 +154,15 @@ function redactNode(value: unknown, path: readonly string[]): unknown {
|
||||
|
||||
export function redactKeyValueSecrets(content: string): string {
|
||||
return content.replace(
|
||||
/^([A-Z0-9_]*(PASSWORD|PASSWD|PASSPHRASE|PASS|SECRET|TOKEN|CREDENTIAL|APIKEY|AUTHORIZATION|COOKIE|BEARER|SIGNATURE)[A-Z0-9_]*=).*$/gm,
|
||||
`$1${REDACTED}`
|
||||
/^([A-Z0-9_]+)=(.*?)(\r?)$/gm,
|
||||
(line, key: string, value: string, eol: string) => {
|
||||
const isKnownBoolean =
|
||||
NON_SECRET_ENV_KEYS.has(key) && /^(?:true|false)$/.test(value);
|
||||
if (isKnownBoolean || !ENV_SECRET_KEY_PATTERN.test(key)) {
|
||||
return line;
|
||||
}
|
||||
return `${key}=${REDACTED}${eol}`;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -207,7 +230,10 @@ export function redactLogText(content: string): string {
|
||||
LOG_SECRET_ASSIGNMENT_PATTERN,
|
||||
(match, key: string, separator: string, rawValue: string) => {
|
||||
const [value, trailing] = splitTrailingPunctuation(rawValue);
|
||||
if (value === REDACTED || value.toLowerCase() === ENCODED_REDACTED.toLowerCase()) {
|
||||
if (
|
||||
value === REDACTED ||
|
||||
value.toLowerCase() === ENCODED_REDACTED.toLowerCase()
|
||||
) {
|
||||
return match;
|
||||
}
|
||||
const rendered = separator === ":" ? ": " : "=";
|
||||
|
||||
Reference in New Issue
Block a user