Files
claude-hub/src/utils/sanitize.ts
Cheffromspace 12e4589169 Fix: Merge entrypoint scripts and fix auto-tagging tool permissions (#146)
* fix: merge entrypoint scripts and fix auto-tagging tool permissions

- Merged duplicate claudecode-entrypoint.sh and claudecode-tagging-entrypoint.sh scripts
- Added dynamic tool selection based on OPERATION_TYPE environment variable
- Fixed auto-tagging permissions to include required Bash(gh:*) commands
- Removed 95% code duplication between entrypoint scripts
- Simplified claudeService.ts to use unified entrypoint
- Auto-tagging now uses: Read,GitHub,Bash(gh issue edit:*),Bash(gh issue view:*),Bash(gh label list:*)
- General operations continue to use full tool set

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: update Dockerfile to use unified entrypoint script

- Remove references to deleted claudecode-tagging-entrypoint.sh
- Update build process to use single unified entrypoint script

* fix: remove unnecessary async from promisify mock to fix lint error

* feat: add Husky pre-commit hooks with Prettier as primary formatter

- Added Husky for Git pre-commit hooks
- Configured eslint-config-prettier to avoid ESLint/Prettier conflicts
- Prettier handles all formatting, ESLint handles code quality only
- Pre-commit hooks: Prettier format, ESLint check, TypeScript check
- Updated documentation with pre-commit hook setup
- All code quality issues resolved

* feat: consolidate workflows and fix permission issues with clean Docker runners

- Replace 3 complex workflows with 2 lean ones (pull-request.yml, main.yml)
- Add Docker runner configuration for clean, isolated builds
- Remove file permission hacks - use ephemeral containers instead
- Split workload: GitHub-hosted for tests/security, self-hosted for Docker builds
- Add comprehensive pre-commit configuration for security
- Update documentation to be more pragmatic
- Fix credential file permissions and security audit

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: allow Husky prepare script to fail in production builds

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: update CI badge to reference new main.yml workflow

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-05-31 20:53:58 -05:00

104 lines
3.0 KiB
TypeScript

import { createLogger } from './logger';
const logger = createLogger('sanitize');
/**
* Sanitizes text to prevent infinite loops by removing bot username mentions
*/
export function sanitizeBotMentions(text: string): string {
if (!text) return text;
// Get bot username from environment variables - required
const BOT_USERNAME = process.env['BOT_USERNAME'];
if (!BOT_USERNAME) {
logger.warn('BOT_USERNAME environment variable is not set. Cannot sanitize properly.');
return text;
}
// Create a regex to find all bot username mentions
// First escape any special regex characters
const escapedUsername = BOT_USERNAME.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
// Look for the username with @ symbol anywhere in the text
const botMentionRegex = new RegExp(escapedUsername, 'gi');
// Replace mentions with a sanitized version (remove @ symbol if present)
const sanitizedName = BOT_USERNAME.startsWith('@') ? BOT_USERNAME.substring(1) : BOT_USERNAME;
const sanitized = text.replace(botMentionRegex, sanitizedName);
// If sanitization occurred, log it
if (sanitized !== text) {
logger.warn('Sanitized bot mentions from text to prevent infinite loops');
}
return sanitized;
}
/**
* Sanitizes an array of labels to remove potentially sensitive or invalid characters
*/
export function sanitizeLabels(labels: string[]): string[] {
return labels.map(label => label.replace(/[^a-zA-Z0-9:_-]/g, ''));
}
/**
* Sanitizes input for safe usage in commands and prevents injection attacks
*/
export function sanitizeCommandInput(input: string): string {
if (!input) return input;
// Remove or escape potentially dangerous characters
return input
.replace(/[`$\\]/g, '') // Remove backticks, dollar signs, and backslashes
.replace(/[;&|><]/g, '') // Remove command injection characters
.trim();
}
/**
* Validates that a string contains only safe repository name characters
*/
export function validateRepositoryName(name: string): boolean {
const repoPattern = /^[a-zA-Z0-9._-]+$/;
return repoPattern.test(name);
}
/**
* Validates that a string contains only safe GitHub reference characters
*/
export function validateGitHubRef(ref: string): boolean {
// GitHub refs cannot:
// - be empty
// - contain consecutive dots (..)
// - contain spaces or special characters like @ or #
if (!ref || ref.includes('..') || ref.includes(' ') || ref.includes('@') || ref.includes('#')) {
return false;
}
// Must contain only allowed characters
const refPattern = /^[a-zA-Z0-9._/-]+$/;
return refPattern.test(ref);
}
/**
* Sanitizes environment variable values for logging
*/
export function sanitizeEnvironmentValue(key: string, value: string): string {
const sensitiveKeys = [
'TOKEN',
'SECRET',
'KEY',
'PASSWORD',
'CREDENTIAL',
'GITHUB_TOKEN',
'ANTHROPIC_API_KEY',
'AWS_ACCESS_KEY_ID',
'AWS_SECRET_ACCESS_KEY',
'WEBHOOK_SECRET'
];
const isSensitive = sensitiveKeys.some(sensitiveKey => key.toUpperCase().includes(sensitiveKey));
return isSensitive ? '[REDACTED]' : value;
}