Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions actions/setup/js/redact_secrets.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,9 @@ function redactBuiltInPatterns(content) {
for (const { name, pattern } of BUILT_IN_PATTERNS) {
const matches = redacted.match(pattern);
if (matches && matches.length > 0) {
// Redact each match
// Redact each match with fixed-length string
const replacement = "***REDACTED***";
for (const match of matches) {
const prefix = match.substring(0, 3);
const asterisks = "*".repeat(Math.max(0, match.length - 3));
const replacement = prefix + asterisks;
redacted = redacted.split(match).join(replacement);
}
redactionCount += matches.length;
Expand Down Expand Up @@ -120,10 +118,8 @@ function redactSecrets(content, secretValues) {
// Count occurrences before replacement
// Use split and join for exact string matching (not regex)
// This is safer than regex as it doesn't interpret special characters
// Show first 3 letters followed by asterisks for the remaining length
const prefix = secretValue.substring(0, 3);
const asterisks = "*".repeat(Math.max(0, secretValue.length - 3));
const replacement = prefix + asterisks;
// Use fixed-length redaction string without prefix preservation
const replacement = "***REDACTED***";
const parts = redacted.split(secretValue);
const occurrences = parts.length - 1;
if (occurrences > 0) {
Expand Down
70 changes: 35 additions & 35 deletions actions/setup/js/redact_secrets.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redactedContent = fs.readFileSync(testFile, "utf8");
(expect(redactedContent).toBe("Secret: ghp************************************* and another ghp*************************************"),
(expect(redactedContent).toBe("Secret: ***REDACTED*** and another ***REDACTED***"),
expect(mockCore.info).toHaveBeenCalledWith("Starting secret redaction in /tmp/gh-aw and /opt/gh-aw directories"),
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Secret redaction complete")));
}),
Expand All @@ -70,9 +70,9 @@ describe("redact_secrets.cjs", () => {
(process.env.SECRET_API_KEY3 = "api-key-789"));
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
(await eval(`(async () => { ${modifiedScript}; await main(); })()`),
expect(fs.readFileSync(path.join(tempDir, "test1.txt"), "utf8")).toBe("Secret: api********"),
expect(fs.readFileSync(path.join(tempDir, "test2.json"), "utf8")).toBe('{"key": "api********"}'),
expect(fs.readFileSync(path.join(tempDir, "test3.log"), "utf8")).toBe("Log: api********"));
expect(fs.readFileSync(path.join(tempDir, "test1.txt"), "utf8")).toBe("Secret: ***REDACTED***"),
expect(fs.readFileSync(path.join(tempDir, "test2.json"), "utf8")).toBe('{"key": "***REDACTED***"}'),
expect(fs.readFileSync(path.join(tempDir, "test3.log"), "utf8")).toBe("Log: ***REDACTED***"));
}),
it("should use core.info for logging hits", async () => {
const testFile = path.join(tempDir, "test.txt"),
Expand Down Expand Up @@ -109,7 +109,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("Token1: ghp*************************************\nToken2: sk-*********************\nToken1 again: ghp*************************************");
expect(redacted).toBe("Token1: ***REDACTED***\nToken2: ***REDACTED***\nToken1 again: ***REDACTED***");
}),
it("should handle empty secret values gracefully", async () => {
const testFile = path.join(tempDir, "test.txt");
Expand All @@ -131,10 +131,10 @@ describe("redact_secrets.cjs", () => {
(process.env.SECRET_API_JSONL = "api-key-jsonl123"));
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
(await eval(`(async () => { ${modifiedScript}; await main(); })()`),
expect(fs.readFileSync(path.join(tempDir, "test.md"), "utf8")).toBe("# Markdown\nSecret: api**********"),
expect(fs.readFileSync(path.join(tempDir, "test.mdx"), "utf8")).toBe("# MDX\nSecret: api***********"),
expect(fs.readFileSync(path.join(tempDir, "test.yml"), "utf8")).toBe("# YAML\nkey: api***********"),
expect(fs.readFileSync(path.join(tempDir, "test.jsonl"), "utf8")).toBe('{"key": "api*************"}'));
expect(fs.readFileSync(path.join(tempDir, "test.md"), "utf8")).toBe("# Markdown\nSecret: ***REDACTED***"),
expect(fs.readFileSync(path.join(tempDir, "test.mdx"), "utf8")).toBe("# MDX\nSecret: ***REDACTED***"),
expect(fs.readFileSync(path.join(tempDir, "test.yml"), "utf8")).toBe("# YAML\nkey: ***REDACTED***"),
expect(fs.readFileSync(path.join(tempDir, "test.jsonl"), "utf8")).toBe('{"key": "***REDACTED***"}'));
}));
}),
describe("built-in pattern detection", () => {
Expand All @@ -147,7 +147,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("Using token: ghp************************************* in this file");
expect(redacted).toBe("Using token: ***REDACTED*** in this file");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("GitHub Personal Access Token"));
});

Expand All @@ -159,7 +159,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("Server token: ghs*************************************");
expect(redacted).toBe("Server token: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("GitHub Server-to-Server Token"));
});

Expand All @@ -171,7 +171,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("OAuth: gho*************************************");
expect(redacted).toBe("OAuth: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("GitHub OAuth Access Token"));
});

Expand All @@ -183,7 +183,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("User token: ghu*************************************");
expect(redacted).toBe("User token: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("GitHub User Access Token"));
});

Expand All @@ -195,7 +195,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("Fine-grained PAT: git******************************************************************************************");
expect(redacted).toBe("Fine-grained PAT: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("GitHub Fine-grained PAT"));
});

Expand All @@ -207,7 +207,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("Refresh: ghr*************************************");
expect(redacted).toBe("Refresh: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("GitHub Refresh Token"));
});

Expand All @@ -221,7 +221,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("PAT: ghp*************************************\nServer: ghs*************************************\nOAuth: gho*************************************");
expect(redacted).toBe("PAT: ***REDACTED***\nServer: ***REDACTED***\nOAuth: ***REDACTED***");
});
});

Expand All @@ -234,7 +234,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("Azure Key: ABC***************************************************************************************");
expect(redacted).toBe("Azure Key: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Azure Storage Account Key"));
});

Expand All @@ -260,7 +260,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("Google API Key: AIz************************************");
expect(redacted).toBe("Google API Key: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Google API Key"));
});

Expand All @@ -272,7 +272,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("OAuth Token: ya2********************************************");
expect(redacted).toBe("OAuth Token: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Google OAuth Access Token"));
});
});
Expand All @@ -286,7 +286,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("AWS Key: AKI*****************");
expect(redacted).toBe("AWS Key: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("AWS Access Key ID"));
});
});
Expand All @@ -300,7 +300,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("OpenAI Key: sk-************************************************");
expect(redacted).toBe("OpenAI Key: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("OpenAI API Key"));
});

Expand All @@ -312,7 +312,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("OpenAI Project Key: sk-************************************************************");
expect(redacted).toBe("OpenAI Project Key: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("OpenAI Project API Key"));
});
});
Expand All @@ -326,7 +326,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("Anthropic Key: sk-*********************************************************************************************************");
expect(redacted).toBe("Anthropic Key: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Anthropic API Key"));
});
});
Expand All @@ -342,7 +342,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("GitHub: ghp*************************************\nCustom: my-**************************");
expect(redacted).toBe("GitHub: ***REDACTED***\nCustom: ***REDACTED***");
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("GitHub Personal Access Token"));
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("occurrence(s) of a secret"));
});
Expand All @@ -357,7 +357,7 @@ describe("redact_secrets.cjs", () => {
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
// Built-in pattern should redact it first
expect(redacted).toBe("Token: ghp************************************* repeated: ghp*************************************");
expect(redacted).toBe("Token: ***REDACTED*** repeated: ***REDACTED***");
});
});

Expand All @@ -380,7 +380,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("First: ghp*************************************\nSecond: ghp*************************************\nThird: ghp*************************************");
expect(redacted).toBe("First: ***REDACTED***\nSecond: ***REDACTED***\nThird: ***REDACTED***");
});

it("should handle secrets in JSON content", async () => {
Expand All @@ -392,8 +392,8 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toContain("ghp*************************************");
expect(redacted).toContain("AIz************************************");
expect(redacted).toContain("***REDACTED***");
expect(redacted).toContain("***REDACTED***");
});

it("should handle secrets in log files with timestamps", async () => {
Expand All @@ -404,7 +404,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("[2024-01-01 12:00:00] INFO: Using token ghp************************************* for authentication");
expect(redacted).toBe("[2024-01-01 12:00:00] INFO: Using token ***REDACTED*** for authentication");
});

it("should not redact partial matches", async () => {
Expand All @@ -427,7 +427,7 @@ describe("redact_secrets.cjs", () => {
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toBe("https://api.github.com?token=ghp*************************************");
expect(redacted).toBe("https://api.github.com?token=***REDACTED***");
});

it("should handle multiline content with various token types", async () => {
Expand All @@ -446,11 +446,11 @@ Custom secret: my-secret-123456789012`;
const modifiedScript = redactScript.replace('findFiles("/tmp/gh-aw", targetExtensions)', `findFiles("${tempDir.replace(/\\/g, "\\\\")}", targetExtensions)`);
await eval(`(async () => { ${modifiedScript}; await main(); })()`);
const redacted = fs.readFileSync(testFile, "utf8");
expect(redacted).toContain("ghp*************************************");
expect(redacted).toContain("ABC***************************************************************************************");
expect(redacted).toContain("AIz************************************");
expect(redacted).toContain("AKI*****************");
expect(redacted).toContain("my-*******************");
expect(redacted).toContain("***REDACTED***");
expect(redacted).toContain("***REDACTED***");
expect(redacted).toContain("***REDACTED***");
expect(redacted).toContain("***REDACTED***");
expect(redacted).toContain("***REDACTED***");
});
});
}));
Expand Down
Loading