feat(caretaker): implement Cloud Run webhook ingestion service#28015
feat(caretaker): implement Cloud Run webhook ingestion service#28015chadd28 wants to merge 6 commits into
Conversation
|
📊 PR Size: size/L
|
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a new ingestion service for the Caretaker Agent, designed to run on Cloud Run. The service acts as a secure gateway for GitHub webhooks, handling event validation, deduplication via Firestore transactions, and message queuing through Pub/Sub. This infrastructure enables automated downstream triage workflows for incoming GitHub issues. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a new Express-based ingestion service for a triage worker, featuring GitHub webhook signature verification, Firestore storage for tracking issues, and Pub/Sub integration. The review feedback highlights three critical security and reliability improvements: validating the signature length and payload type in the GitHub auth module to prevent crashes and DoS attacks, escaping the issue body to mitigate prompt injection vulnerabilities, and checking the return value of the issue creation transaction to prevent duplicate Pub/Sub messages.
| if (!signatureHeader) { | ||
| return false; | ||
| } | ||
|
|
||
| const hmac = crypto.createHmac('sha256', secret); | ||
| hmac.update(payloadBody); | ||
| const expectedSignature = 'sha256=' + hmac.digest('hex'); | ||
|
|
||
| try { | ||
| return crypto.timingSafeEqual( | ||
| Buffer.from(expectedSignature), | ||
| Buffer.from(signatureHeader), | ||
| ); | ||
| } catch { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
The signature verification is vulnerable to potential Denial of Service (DoS) and timing attacks if a client sends an extremely large or malformed signature header. Additionally, passing a non-Buffer/non-string payload to crypto.createHmac().update() will throw a TypeError and crash the server.
To prevent this, validate that the signature header is exactly 71 characters long (the length of a SHA-256 hex HMAC with the sha256= prefix) and that the payload is a valid Buffer or string before performing any HMAC computations or timing-safe comparisons.
if (!signatureHeader || signatureHeader.length !== 71) {
return false;
}
if (!Buffer.isBuffer(payloadBody) && typeof payloadBody !== 'string') {
return false;
}
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payloadBody);
const expectedSignature = 'sha256=' + hmac.digest('hex');
try {
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(signatureHeader),
);
} catch {
return false;
}| } | ||
|
|
||
| // Payload preprocessing | ||
| const sanitizedBody = `<untrusted_context>\n${payload.issue?.body || ''}\n</untrusted_context>`; |
There was a problem hiding this comment.
Wrapping untrusted user input directly in <untrusted_context> tags without escaping or sanitizing the input makes the system vulnerable to prompt injection. An attacker could include </untrusted_context> in their issue body to escape the context block and inject malicious instructions.
Sanitize or escape any occurrences of </untrusted_context> in the issue body before wrapping it.
const rawBody = payload.issue?.body || '';
const escapedBody = rawBody.replace(/<\/untrusted_context>/g, '[escaped_untrusted_context_tag]');
const sanitizedBody = `\<untrusted_context\>\n\${escapedBody}\n\</untrusted_context\>`;
Summary
Implements the Cloud Run Webhook Ingestion Service for the Caretaker Agent. The service acts as an entry point for GitHub webhooks, verifies incoming payload signatures, stores new issue entries using Firestore transactions, and publishes sanitized issue metadata to a GCP Pub/Sub topic for downstream processing.
Details
server.ts): The main Express server (to be hosted on Cloud Run) that receives GitHubissues.openedevents, validates their signature, adds the issue to Firestore, and publishes the issue details to Pub/Sub.auth/github.ts): HMAC SHA-256 signature verification helper usingnode:cryptoand a secure timing-safe equality check.db/issuesStore.ts): Initializes new issues in Firestore using a transaction.issuesStore.test.ts(Firestore mock tests) andgithub.test.ts(HMAC signature verification tests).How to Validate
Run unit tests in the service directory:
cd tools/caretaker-agent/cloudrun/ingestion-service npm install npx vitest runPre-Merge Checklist
@galz10