Add GitHub security advisory alerts workflow#3087
Conversation
Adds a nightly workflow that checks for open security advisories in triage/draft state and sends email alerts via Mailgun. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
@BugBot run |
There was a problem hiding this comment.
Pull request overview
Adds automated monitoring for GitHub Security Advisories so the team is alerted when advisories remain in triage or draft states.
Changes:
- Introduces a nightly + manual GitHub Actions workflow to check advisory states and trigger alerts.
- Adds a Node.js script that queries the GitHub API for advisory counts (triage/draft) and sends Mailgun email notifications when non-zero.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
scripts/github-security-advisory-alert.mjs |
Implements GitHub API pagination/counting and Mailgun email delivery + step summary output. |
.github/workflows/github-security-advisory-alerts.yml |
Schedules and runs the script nightly (and via workflow_dispatch) using a GitHub App token and Mailgun env configuration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - name: Setup Node | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22 |
There was a problem hiding this comment.
The workflow pins Node.js to v22, but the repo declares engines.node as >=24 and CI initializes Node using node-version-file: package.json. This workflow should follow the same source of truth (or at least use a Node version compatible with the engine constraint) to avoid subtle runtime differences and future breakage when scripts start relying on Node 24+ features.
| node-version: 22 | |
| node-version-file: package.json |
There was a problem hiding this comment.
Code Review
This pull request introduces a script to monitor and alert on GitHub security advisories via Mailgun. The review suggests refactoring the parseRecipients function for better conciseness using modern JavaScript features and recommends dynamically generating report sections based on the ADVISORY_STATES constant to improve maintainability and avoid hardcoded values.
| const parseRecipients = (value) => { | ||
| const seen = new Set(); | ||
| const recipients = []; | ||
|
|
||
| for (const entry of value.split(",")) { | ||
| const email = entry.trim(); | ||
| if (!email || seen.has(email)) { | ||
| continue; | ||
| } | ||
| seen.add(email); | ||
| recipients.push(email); | ||
| } | ||
|
|
||
| if (recipients.length === 0) { | ||
| throw new Error( | ||
| "SECURITY_ADVISORY_ALERT_EMAILS must contain at least one email address", | ||
| ); | ||
| } | ||
|
|
||
| return recipients; | ||
| }; |
There was a problem hiding this comment.
This function can be written more concisely using modern JavaScript features. Using a Set directly from a mapped array simplifies the logic for finding unique, non-empty email addresses, improving readability and maintainability.
const parseRecipients = (value) => {
const recipients = [
...new Set(
value
.split(",")
.map((email) => email.trim())
.filter(Boolean),
),
];
if (recipients.length === 0) {
throw new Error(
"SECURITY_ADVISORY_ALERT_EMAILS must contain at least one email address",
);
}
return recipients;
};| await appendStepSummary(`Triage advisories: ${advisoryCounts.triage}`); | ||
| await appendStepSummary(`Draft advisories: ${advisoryCounts.draft}`); |
There was a problem hiding this comment.
The script hardcodes the 'triage' and 'draft' states when generating the summary and email content. This is inconsistent with the data fetching logic, which dynamically uses the ADVISORY_STATES constant. If ADVISORY_STATES is ever updated, the reporting parts of the script will become outdated and require manual changes.
To improve maintainability and robustness, you should generate these report sections dynamically by iterating over ADVISORY_STATES.
For example, for the step summary:
for (const state of ADVISORY_STATES) {
const capitalizedState = state.charAt(0).toUpperCase() + state.slice(1);
await appendStepSummary(`${capitalizedState} advisories: ${advisoryCounts[state]}`);
}A similar approach should be applied to generate the lists and links in the text and HTML email bodies.
🔍 Dyadbot Code Review SummaryVerdict: ✅ YES - Ready to merge Reviewed by 3 independent agents: Correctness Expert, Code Health Expert, UX Wizard. Issues Summary
🚫 Dropped False Positives (9 items)
Generated by Dyadbot multi-agent code review |
| const fromEmail = requireEnv("MAILGUN_FROM_EMAIL"); | ||
| const recipients = parseRecipients( | ||
| requireEnv("SECURITY_ADVISORY_ALERT_EMAILS"), | ||
| ); |
There was a problem hiding this comment.
🟡 MEDIUM | code-health
Mailgun env vars eagerly validated before advisory count check
All environment variables including MAILGUN_API_KEY, MAILGUN_DOMAIN, MAILGUN_FROM_EMAIL, and SECURITY_ADVISORY_ALERT_EMAILS are validated via requireEnv at the top of main(), before checking whether there are any advisories to report. If totalCount === 0 (the common case for a healthy repo), the script exits without sending email — meaning the Mailgun variables were never needed.
This means a misconfigured or expired Mailgun secret will cause the nightly workflow to fail even when there are zero advisories to report.
💡 Suggestion: Move the Mailgun and recipient env var validation to after the totalCount === 0 early-return, so the workflow only fails on missing email config when it actually needs to send an email.
There was a problem hiding this comment.
1 issue found across 2 files
Confidence score: 3/5
- There is a concrete medium-risk mismatch in
.github/workflows/github-security-advisory-alerts.yml: the workflow uses Node 22 while the repo requires Node >=24, which can cause the advisory alert script (or dependencies) to fail. - The issue has relatively high confidence (8/10) and is environment-related, so it is likely reproducible in CI even if application runtime code is unaffected.
- Pay close attention to
.github/workflows/github-security-advisory-alerts.yml- align the workflow runtime to Node 24+ to avoid alert workflow breakage.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/github-security-advisory-alerts.yml">
<violation number="1" location=".github/workflows/github-security-advisory-alerts.yml:42">
P2: The workflow installs Node 22, but the repo’s engines requirement is Node >=24. This can break the alert script or future dependencies that assume 24+. Use Node 24 in the workflow to align with the project’s supported runtime.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| - name: Setup Node | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22 |
There was a problem hiding this comment.
P2: The workflow installs Node 22, but the repo’s engines requirement is Node >=24. This can break the alert script or future dependencies that assume 24+. Use Node 24 in the workflow to align with the project’s supported runtime.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/github-security-advisory-alerts.yml, line 42:
<comment>The workflow installs Node 22, but the repo’s engines requirement is Node >=24. This can break the alert script or future dependencies that assume 24+. Use Node 24 in the workflow to align with the project’s supported runtime.</comment>
<file context>
@@ -0,0 +1,55 @@
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+
+ - name: Send advisory alert email
</file context>
Summary
scripts/github-security-advisory-alert.mjsTest plan
workflow_dispatchrunKEPPO_GITHUB_APP_ID,KEPPO_GITHUB_APP_PRIVATE_KEY,MAILGUN_API_KEY,MAILGUN_DOMAIN,MAILGUN_FROM_EMAIL,SECURITY_ADVISORY_ALERT_EMAILS) are configured in theai-botsenvironment🤖 Generated with Claude Code