Skip to content

Commit b9d3174

Browse files
Add GitHub issue security analysis feature (#10214)
- Import IssuesOpenedEvent from @octokit/webhooks-types - Add ALERTS_WEBHOOK environment variable - Create isIssueOpenedEvent type guard function - Implement analyzeIssueSecurity AI function to detect security issues - Add sendSecurityAlert function for GChat notifications - Extend /github webhook handler to process issue events - Send alerts to ANT: alerts channel for potential vulnerability reports Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: [email protected] <[email protected]>
1 parent 298c0fc commit b9d3174

File tree

3 files changed

+126
-0
lines changed

3 files changed

+126
-0
lines changed

.changeset/spotty-poets-jump.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"devprod-status-bot": minor
3+
---
4+
5+
Add GitHub issue security analysis feature to detect and alert on potential vulnerability reports

packages/devprod-status-bot/src/index.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Endpoints } from "@octokit/types";
22
import type {
3+
IssuesOpenedEvent,
34
PullRequestOpenedEvent,
45
PullRequestReadyForReviewEvent,
56
WebhookEvent,
@@ -26,6 +27,48 @@ async function getBotMessage(ai: Ai, prompt: string) {
2627
return message.response;
2728
}
2829

30+
async function analyzeIssueSecurity(
31+
ai: Ai,
32+
issueTitle: string,
33+
issueBody: string
34+
): Promise<boolean> {
35+
const prompt = `Analyze this GitHub issue to determine if it's likely reporting a security vulnerability or security concern.
36+
37+
Issue Title: ${issueTitle}
38+
39+
Issue Body: ${issueBody}
40+
41+
Look for keywords and patterns that suggest this is a security report, such as:
42+
- Vulnerability, exploit, security flaw, CVE
43+
- Authentication bypass, privilege escalation
44+
- XSS, SQL injection, CSRF, RCE
45+
- Unauthorized access, data exposure
46+
- Security disclosure, responsible disclosure
47+
48+
Respond with only "YES" if this appears to be a security-related issue, or "NO" if it appears to be a regular bug report or feature request.`;
49+
50+
const chat = {
51+
messages: [
52+
{
53+
role: "system",
54+
content:
55+
"You are a security analyst assistant that helps identify potential security vulnerability reports in GitHub issues. Respond only with YES or NO.",
56+
},
57+
{
58+
role: "user",
59+
content: prompt,
60+
},
61+
] as RoleScopedChatInput[],
62+
};
63+
64+
const message = await ai.run("@cf/meta/llama-2-7b-chat-int8", chat);
65+
if (!("response" in message) || !message.response) {
66+
return false;
67+
}
68+
69+
return message.response.trim().toUpperCase() === "YES";
70+
}
71+
2972
type PRList = Endpoints["GET /repos/{owner}/{repo}/pulls"]["response"]["data"];
3073
async function getPrs(pat: string) {
3174
const workersSdk = await fetch(
@@ -312,6 +355,12 @@ function isPullRequestReadyForReviewEvent(
312355
return "action" in message && message.action === "ready_for_review";
313356
}
314357

358+
function isIssueOpenedEvent(
359+
message: WebhookEvent
360+
): message is IssuesOpenedEvent {
361+
return "issue" in message && message.action === "opened";
362+
}
363+
315364
function sendReviewMessage(webhookUrl: string, message: WebhookEvent) {
316365
if (
317366
(isPullRequestOpenedEvent(message) ||
@@ -364,6 +413,66 @@ function sendReviewMessage(webhookUrl: string, message: WebhookEvent) {
364413
}
365414
}
366415

416+
async function sendSecurityAlert(webhookUrl: string, issue: IssuesOpenedEvent) {
417+
return sendMessage(
418+
webhookUrl,
419+
{
420+
cardsV2: [
421+
{
422+
cardId: "unique-card-id",
423+
card: {
424+
header: {
425+
title: "🚨 Potential Security Issue Detected",
426+
subtitle: `Issue #${issue.issue.number} in ${issue.repository.full_name}`,
427+
imageUrl: issue.sender.avatar_url,
428+
imageType: "CIRCLE",
429+
imageAltText: "Reporter Avatar",
430+
},
431+
sections: [
432+
{
433+
collapsible: false,
434+
widgets: [
435+
{
436+
textParagraph: {
437+
text: `<b>Title:</b> ${issue.issue.title}\n\n<b>Reporter:</b> ${issue.sender.login}`,
438+
},
439+
},
440+
{
441+
buttonList: {
442+
buttons: [
443+
{
444+
text: "View Issue",
445+
onClick: {
446+
openLink: {
447+
url: issue.issue.html_url,
448+
},
449+
},
450+
},
451+
],
452+
},
453+
},
454+
],
455+
},
456+
{
457+
collapsible: true,
458+
uncollapsibleWidgetsCount: 0,
459+
widgets: [
460+
{
461+
textParagraph: {
462+
text: issue.issue.body || "No description provided",
463+
},
464+
},
465+
],
466+
},
467+
],
468+
},
469+
},
470+
],
471+
},
472+
"security-alerts"
473+
);
474+
}
475+
367476
async function sendUpcomingReleaseMessage(pat: string, webhookUrl: string) {
368477
const releasePr = await getVersionPackagesPR(pat);
369478

@@ -620,6 +729,17 @@ export default {
620729
if (url.pathname === "/github") {
621730
const body = await request.json<WebhookEvent>();
622731
await sendReviewMessage(env.PROD_WEBHOOK, body);
732+
733+
if (isIssueOpenedEvent(body)) {
734+
const isSecurityIssue = await analyzeIssueSecurity(
735+
env.AI,
736+
body.issue.title,
737+
body.issue.body || ""
738+
);
739+
if (isSecurityIssue) {
740+
await sendSecurityAlert(env.ALERTS_WEBHOOK, body);
741+
}
742+
}
623743
}
624744

625745
if (url.pathname.startsWith("/pr-project") && request.method === "POST") {

packages/devprod-status-bot/worker-configuration.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// by running `wrangler types`
33

44
interface Env {
5+
ALERTS_WEBHOOK: string;
56
GITHUB_PAT: string;
67
PROD_TEAM_ONLY_WEBHOOK: string;
78
PROD_WEBHOOK: string;

0 commit comments

Comments
 (0)