forked from claude-did-this/claude-hub
fix: address security vulnerabilities and linting issues
- Fix log injection vulnerability by sanitizing user input in webhook logging - Fix regex injection vulnerability by escaping profile names in AWS credential provider - Remove unnecessary optional chaining operators based on TypeScript interface definitions - Improve type safety and defensive programming practices - Maintain backward compatibility while enhancing security 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -114,13 +114,13 @@ export const handleWebhook: WebhookHandler = async (req, res) => {
|
||||
const event = req.headers['x-github-event'] as string;
|
||||
const delivery = req.headers['x-github-delivery'] as string;
|
||||
|
||||
// Log webhook receipt with key details
|
||||
// Log webhook receipt with key details (sanitize user input to prevent log injection)
|
||||
logger.info(
|
||||
{
|
||||
event,
|
||||
delivery,
|
||||
sender: req.body.sender?.login,
|
||||
repo: req.body.repository?.full_name
|
||||
sender: req.body.sender.login.replace(/[\r\n\t]/g, '_'),
|
||||
repo: req.body.repository.full_name.replace(/[\r\n\t]/g, '_')
|
||||
},
|
||||
`Received GitHub ${event} webhook`
|
||||
);
|
||||
@@ -619,8 +619,8 @@ async function handleCheckSuiteCompleted(
|
||||
pullRequestCount: checkSuite.pull_requests ? checkSuite.pull_requests.length : 0,
|
||||
pullRequests: checkSuite.pull_requests?.map(pr => ({
|
||||
number: pr.number,
|
||||
headRef: pr.head?.ref,
|
||||
headSha: pr.head?.sha
|
||||
headRef: pr.head.ref,
|
||||
headSha: pr.head.sha
|
||||
}))
|
||||
},
|
||||
'Processing check_suite completed event'
|
||||
@@ -689,7 +689,7 @@ async function handleCheckSuiteCompleted(
|
||||
repo: repo.full_name,
|
||||
checkSuite: checkSuite.id,
|
||||
conclusion: checkSuite.conclusion,
|
||||
pullRequestCount: checkSuite.pull_requests?.length ?? 0,
|
||||
pullRequestCount: (checkSuite.pull_requests ?? []).length,
|
||||
shouldTriggerReview,
|
||||
triggerReason,
|
||||
waitForAllChecks,
|
||||
@@ -726,7 +726,7 @@ async function processAutomatedPRReviews(
|
||||
|
||||
try {
|
||||
// Extract SHA from PR data first
|
||||
const commitSha = pr.head?.sha;
|
||||
const commitSha = pr.head.sha;
|
||||
|
||||
if (!commitSha) {
|
||||
logger.error(
|
||||
@@ -1432,7 +1432,10 @@ function getWorkflowNameFromCheckSuite(
|
||||
/**
|
||||
* Handle general webhook errors
|
||||
*/
|
||||
function handleWebhookError(error: unknown, res: Response<WebhookResponse | ErrorResponse>): Response<WebhookResponse | ErrorResponse> {
|
||||
function handleWebhookError(
|
||||
error: unknown,
|
||||
res: Response<WebhookResponse | ErrorResponse>
|
||||
): Response<WebhookResponse | ErrorResponse> {
|
||||
const err = error as Error;
|
||||
|
||||
// Generate a unique error reference
|
||||
|
||||
@@ -119,7 +119,7 @@ app.get('/api/test-tunnel', (req, res: express.Response<TestTunnelResponse>) =>
|
||||
message: 'CF tunnel is working!',
|
||||
timestamp: new Date().toISOString(),
|
||||
headers: req.headers,
|
||||
ip: req.ip ?? (req.connection as { remoteAddress?: string })?.remoteAddress
|
||||
ip: req.ip ?? (req.connection as { remoteAddress?: string }).remoteAddress
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -594,11 +594,13 @@ export async function getCheckSuitesForRef({
|
||||
head_sha: suite.head_sha,
|
||||
status: suite.status,
|
||||
conclusion: suite.conclusion,
|
||||
app: suite.app ? {
|
||||
id: suite.app.id,
|
||||
slug: suite.app.slug,
|
||||
name: suite.app.name
|
||||
} : null,
|
||||
app: suite.app
|
||||
? {
|
||||
id: suite.app.id,
|
||||
slug: suite.app.slug,
|
||||
name: suite.app.name
|
||||
}
|
||||
: null,
|
||||
pull_requests: null, // Simplified for our use case
|
||||
created_at: suite.created_at,
|
||||
updated_at: suite.updated_at,
|
||||
|
||||
@@ -213,10 +213,11 @@ class AWSCredentialProvider {
|
||||
const credentialsContent = await fs.readFile(credentialsPath, 'utf8');
|
||||
const configContent = await fs.readFile(configPath, 'utf8');
|
||||
|
||||
// Parse credentials for the specific profile
|
||||
const profileRegex = new RegExp(`\\[${profileName}\\]([^\\[]*)`);
|
||||
// Parse credentials for the specific profile (escape profile name to prevent regex injection)
|
||||
const escapedProfileName = profileName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const profileRegex = new RegExp(`\\[${escapedProfileName}\\]([^\\[]*)`);
|
||||
const credentialsMatch = credentialsContent.match(profileRegex);
|
||||
const configMatch = configContent.match(new RegExp(`\\[profile ${profileName}\\]([^\\[]*)`));
|
||||
const configMatch = configContent.match(new RegExp(`\\[profile ${escapedProfileName}\\]([^\\[]*)`));
|
||||
|
||||
if (!credentialsMatch && !configMatch) {
|
||||
const error = new Error(`Profile '${profileName}' not found`) as AWSCredentialError;
|
||||
|
||||
Reference in New Issue
Block a user