mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-22 14:24:13 +08:00
* Add GitHub token redaction to update_claude_comment tool - Add redactGitHubTokens() function to sanitizer.ts that detects and redacts all GitHub token formats (ghp_, gho_, ghs_, ghr_, github_pat_) - Update sanitizeContent() to include token redaction in the sanitization pipeline - Apply sanitization to comment body in github-comment-server.ts before updating comments - Add comprehensive tests covering all token formats, edge cases, and integration scenarios - Prevents accidental exposure of GitHub tokens in PR/issue comments while preserving existing functionality 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Add GitHub token redaction to inline comment server - Apply sanitizeContent() to comment body in github-inline-comment-server.ts before creating inline PR comments - Ensures consistency in token redaction across all comment creation tools - Prevents GitHub tokens from being exposed in inline PR review comments 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
101 lines
3.4 KiB
TypeScript
101 lines
3.4 KiB
TypeScript
export function stripInvisibleCharacters(content: string): string {
|
|
content = content.replace(/[\u200B\u200C\u200D\uFEFF]/g, "");
|
|
content = content.replace(
|
|
/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g,
|
|
"",
|
|
);
|
|
content = content.replace(/\u00AD/g, "");
|
|
content = content.replace(/[\u202A-\u202E\u2066-\u2069]/g, "");
|
|
return content;
|
|
}
|
|
|
|
export function stripMarkdownImageAltText(content: string): string {
|
|
return content.replace(/!\[[^\]]*\]\(/g, ";
|
|
}
|
|
|
|
export function stripMarkdownLinkTitles(content: string): string {
|
|
content = content.replace(/(\[[^\]]*\]\([^)]+)\s+"[^"]*"/g, "$1");
|
|
content = content.replace(/(\[[^\]]*\]\([^)]+)\s+'[^']*'/g, "$1");
|
|
return content;
|
|
}
|
|
|
|
export function stripHiddenAttributes(content: string): string {
|
|
content = content.replace(/\salt\s*=\s*["'][^"']*["']/gi, "");
|
|
content = content.replace(/\salt\s*=\s*[^\s>]+/gi, "");
|
|
content = content.replace(/\stitle\s*=\s*["'][^"']*["']/gi, "");
|
|
content = content.replace(/\stitle\s*=\s*[^\s>]+/gi, "");
|
|
content = content.replace(/\saria-label\s*=\s*["'][^"']*["']/gi, "");
|
|
content = content.replace(/\saria-label\s*=\s*[^\s>]+/gi, "");
|
|
content = content.replace(/\sdata-[a-zA-Z0-9-]+\s*=\s*["'][^"']*["']/gi, "");
|
|
content = content.replace(/\sdata-[a-zA-Z0-9-]+\s*=\s*[^\s>]+/gi, "");
|
|
content = content.replace(/\splaceholder\s*=\s*["'][^"']*["']/gi, "");
|
|
content = content.replace(/\splaceholder\s*=\s*[^\s>]+/gi, "");
|
|
return content;
|
|
}
|
|
|
|
export function normalizeHtmlEntities(content: string): string {
|
|
content = content.replace(/&#(\d+);/g, (_, dec) => {
|
|
const num = parseInt(dec, 10);
|
|
if (num >= 32 && num <= 126) {
|
|
return String.fromCharCode(num);
|
|
}
|
|
return "";
|
|
});
|
|
content = content.replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => {
|
|
const num = parseInt(hex, 16);
|
|
if (num >= 32 && num <= 126) {
|
|
return String.fromCharCode(num);
|
|
}
|
|
return "";
|
|
});
|
|
return content;
|
|
}
|
|
|
|
export function sanitizeContent(content: string): string {
|
|
content = stripHtmlComments(content);
|
|
content = stripInvisibleCharacters(content);
|
|
content = stripMarkdownImageAltText(content);
|
|
content = stripMarkdownLinkTitles(content);
|
|
content = stripHiddenAttributes(content);
|
|
content = normalizeHtmlEntities(content);
|
|
content = redactGitHubTokens(content);
|
|
return content;
|
|
}
|
|
|
|
export function redactGitHubTokens(content: string): string {
|
|
// GitHub Personal Access Tokens (classic): ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (40 chars)
|
|
content = content.replace(
|
|
/\bghp_[A-Za-z0-9]{36}\b/g,
|
|
"[REDACTED_GITHUB_TOKEN]",
|
|
);
|
|
|
|
// GitHub OAuth tokens: gho_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (40 chars)
|
|
content = content.replace(
|
|
/\bgho_[A-Za-z0-9]{36}\b/g,
|
|
"[REDACTED_GITHUB_TOKEN]",
|
|
);
|
|
|
|
// GitHub installation tokens: ghs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (40 chars)
|
|
content = content.replace(
|
|
/\bghs_[A-Za-z0-9]{36}\b/g,
|
|
"[REDACTED_GITHUB_TOKEN]",
|
|
);
|
|
|
|
// GitHub refresh tokens: ghr_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (40 chars)
|
|
content = content.replace(
|
|
/\bghr_[A-Za-z0-9]{36}\b/g,
|
|
"[REDACTED_GITHUB_TOKEN]",
|
|
);
|
|
|
|
// GitHub fine-grained personal access tokens: github_pat_XXXXXXXXXX (up to 255 chars)
|
|
content = content.replace(
|
|
/\bgithub_pat_[A-Za-z0-9_]{11,221}\b/g,
|
|
"[REDACTED_GITHUB_TOKEN]",
|
|
);
|
|
|
|
return content;
|
|
}
|
|
|
|
export const stripHtmlComments = (content: string) =>
|
|
content.replace(/<!--[\s\S]*?-->/g, "");
|