mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 15:50:46 +00:00
fix: sanitize diagnostics log edge cases
This commit is contained in:
@@ -148,6 +148,12 @@ describe("diagnostic support export", () => {
|
||||
},
|
||||
time: "2026-04-22T12:00:00.100Z",
|
||||
}),
|
||||
JSON.stringify({
|
||||
time: "2026-04-22T12:00:00.200Z",
|
||||
level: "info",
|
||||
component: "gateway/server",
|
||||
msg: "user said structured secret payload",
|
||||
}),
|
||||
JSON.stringify({
|
||||
"0": JSON.stringify({ subsystem: "gateway/channels/matrix" }),
|
||||
"1": privateChat,
|
||||
@@ -156,7 +162,7 @@ describe("diagnostic support export", () => {
|
||||
name: "gateway-runtime",
|
||||
hostname: "support-host",
|
||||
},
|
||||
time: "2026-04-22T12:00:00.200Z",
|
||||
time: "2026-04-22T12:00:00.300Z",
|
||||
}),
|
||||
`plain fallback ${privateChat} ${fakeToken}`,
|
||||
],
|
||||
@@ -246,6 +252,7 @@ describe("diagnostic support export", () => {
|
||||
expect(combined).not.toContain(os.hostname());
|
||||
expect(combined).not.toContain("QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
|
||||
expect(combined).not.toContain("sid=secret");
|
||||
expect(combined).not.toContain("structured secret payload");
|
||||
expect(combined).not.toContain(fakeAwsKey);
|
||||
expect(combined).not.toContain(fakeJwt);
|
||||
expect(combined).toContain("payload.large");
|
||||
@@ -377,6 +384,7 @@ describe("diagnostic support export", () => {
|
||||
"connect wss://support-user:support-password@gateway.example/ws?token=short-token&ok=1",
|
||||
"connect wss://<redacted>:<redacted>@gateway.example/ws?token=<redacted>",
|
||||
],
|
||||
["connect https://token@gateway.example/ws", "connect https://<redacted>@gateway.example/ws"],
|
||||
["auth Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", "auth Basic <redacted>"],
|
||||
["Cookie: sid=secret; theme=light", "Cookie: <redacted>"],
|
||||
[`aws ${fakeAwsKey}`, "aws <redacted-aws-key>"],
|
||||
|
||||
@@ -272,6 +272,7 @@ function sanitizeConfigDetails(parsed: unknown, redaction: SupportRedactionConte
|
||||
|
||||
function configShapeReadFailure(params: {
|
||||
configPath: string;
|
||||
redaction: SupportRedactionContext;
|
||||
stat?: fs.Stats;
|
||||
error?: string;
|
||||
}): ConfigShape {
|
||||
@@ -286,7 +287,7 @@ function configShapeReadFailure(params: {
|
||||
shape.mtime = params.stat.mtime.toISOString();
|
||||
}
|
||||
if (params.error) {
|
||||
shape.error = redactTextForSupport(params.error);
|
||||
shape.error = redactSupportString(params.error, params.redaction);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
@@ -300,7 +301,7 @@ function readConfigExport(options: {
|
||||
const stat = fs.existsSync(options.configPath) ? fs.statSync(options.configPath) : null;
|
||||
if (!stat) {
|
||||
return {
|
||||
shape: configShapeReadFailure({ configPath: redactedConfigPath }),
|
||||
shape: configShapeReadFailure({ configPath: redactedConfigPath, redaction: options }),
|
||||
};
|
||||
}
|
||||
try {
|
||||
@@ -309,6 +310,7 @@ function readConfigExport(options: {
|
||||
return {
|
||||
shape: configShapeReadFailure({
|
||||
configPath: redactedConfigPath,
|
||||
redaction: options,
|
||||
stat,
|
||||
error: parsed.error,
|
||||
}),
|
||||
@@ -322,6 +324,7 @@ function readConfigExport(options: {
|
||||
return {
|
||||
shape: configShapeReadFailure({
|
||||
configPath: redactedConfigPath,
|
||||
redaction: options,
|
||||
stat,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
}),
|
||||
@@ -541,7 +544,12 @@ function addSafeLogField(
|
||||
return;
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
sanitized[key] = sanitizeLogString(value, redaction);
|
||||
const message = sanitizeLogString(value, redaction);
|
||||
if (key === "msg" && (!message || UNSAFE_LOG_MESSAGE_RE.test(message))) {
|
||||
addOmittedLogMessageMetadata(sanitized, value);
|
||||
return;
|
||||
}
|
||||
sanitized[key] = message;
|
||||
} else if (typeof value === "number" || typeof value === "boolean" || value === null) {
|
||||
sanitized[key] = value;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const BASIC_AUTH_RE = /\bBasic\s+[A-Za-z0-9+/]+={0,2}/giu;
|
||||
const COOKIE_HEADER_RE = /\b(Cookie|Set-Cookie)\s*:\s*[^\r\n]+/giu;
|
||||
const AWS_ACCESS_KEY_ID_RE = /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/gu;
|
||||
const JWT_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/gu;
|
||||
const URL_USERINFO_RE = /\b([a-z][a-z0-9+.-]*:\/\/)([^/@\s:?#]+):([^/@\s?#]+)@/giu;
|
||||
const URL_USERINFO_RE = /\b([a-z][a-z0-9+.-]*:\/\/)([^/@\s:?#]+)(?::([^/@\s?#]+))?@/giu;
|
||||
const SENSITIVE_URL_PARAM_RE =
|
||||
/([?&](?:api[-_]?key|access[-_]?token|auth[-_]?token|hook[-_]?token|password|passwd|refresh[-_]?token|secret|token)=)[^&#\s]+/giu;
|
||||
const EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/giu;
|
||||
@@ -140,7 +140,9 @@ function redactCommonCredentialTextForSupport(value: string): string {
|
||||
|
||||
function redactUrlSecretsForSupport(value: string): string {
|
||||
return value
|
||||
.replace(URL_USERINFO_RE, "$1<redacted>:<redacted>@")
|
||||
.replace(URL_USERINFO_RE, (_match, scheme: string, _username: string, password?: string) =>
|
||||
password ? `${scheme}<redacted>:<redacted>@` : `${scheme}<redacted>@`,
|
||||
)
|
||||
.replace(SENSITIVE_URL_PARAM_RE, "$1<redacted>");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user