Automates security remediation across repositories by turning GitHub Dependabot alerts into tested pull requests with policy-based outcomes: simple fixes can auto-merge, while complex or breaking remediations stay PR-only for manual review.
The agent runs on a daily schedule (08:00 UTC) and sweeps all monitored repositories for open Dependabot alerts:
- The central workflow triggers automatically each morning via
cron: '0 8 * * *'. - It queries open Dependabot alerts across all monitored repositories, filtered by severity and ecosystem.
- For each new alert it creates a fix branch, runs lint/tests, and opens a PR.
- The agent applies merge policy: low-risk dependency-only fixes can auto-merge, while risky/breaking fixes are left as manual-review PRs.
- You receive a summary email with created, auto-merged, skipped, and failed outcomes plus recommended actions.
Note: Real-time per-repo listener workflows (using the
dependabot_alertevent) are not used. That event is not in GitHub's push-time YAML schema validator and causes failure emails on every push to every branch of every target repo. The daily 8am sweep covers all alert detection without the noise.
- GitHub emails (raw): Dependabot still sends its own alert emails directly — these are from GitHub's notification system and are not controlled by this agent.
- Agent emails (actionable): Sent to your configured
EMAIL_TOwhen there are new alerts or PR outcomes to act on. Contains status by repo (auto-merged, manual review, skipped, failed), PR links, and suggested actions. Suppressed on quiet days with no new actionable outcomes.
On a day with a new alert you may receive both — one from GitHub announcing the alert, and one from this agent with the PR already created.
- Node.js 20+
- npm
- GitHub CLI (
gh) installed and authenticated - Git configured locally
- SMTP account credentials for outbound notification email (
SMTP_USERandSMTP_PASS)
- Install dependencies:
npm install- Create local runtime env file:
cp .env.example .env- Fill
.envwith required values:
GITHUB_TOKENACCOUNT_LOGIN(recommended for auto-discovery mode)- SMTP fields when
EMAIL_ENABLED=true
- Choose repository selection mode:
- Explicit list mode: set
ALERT_REPOSITORIES=owner/repo1,owner/repo2 - Listener event mode (recommended for production): structured payload forwarded automatically by per-repo listener
- Auto-discovery mode: leave both empty and use
ACCOUNT_LOGINto scope all owned repos
- Validate configuration and quality:
npm run preflight
npm run check- Start the agent locally:
npm run dev.envis the single source of truth for local development and E2E simulation.- Incorrect values can stop PR creation or prevent notifications.
- Keep
.envoutside version control and treat it as sensitive.
- Reads open Dependabot alerts filtered by severity (critical, high, moderate) and ecosystem (npm only).
- Suppresses duplicate processing of unchanged alert sets per repository.
- Reuses an existing open Dependabot PR when one already covers the alert.
- Otherwise creates a fix branch, applies remediation, runs lint/tests, validates changed files, and opens a PR.
- One PR per repository (batched) — never multiple PRs for the same repo in one run.
- Runs install/test commands with per-repo runtime resolution (
REPO_COMMANDS.nodeVersion, thenengines.node, then default runtime). - Auto-merges low-risk/simple dependency-only outcomes after successful validation.
- Keeps difficult/breaking upgrades as PR-only and marks them for manual review.
- Sends a summary email only when there are actionable outcomes or newly seen alerts.
- Fix Agent: applies dependency update in a branch.
- Test Agent: runs lint/test commands.
- Validation Agent: verifies branch readiness.
- Orchestrator Agent: coordinates all steps and notifications.
| Trigger | When | Signal mode |
|---|---|---|
schedule (cron: '0 8 * * *') |
Daily at 08:00 UTC | Sweeps all monitored repos |
workflow_dispatch (manual) |
On demand | Signal-scoped when advisory payload is provided |
repository_dispatch vulnerability-alert-forwarded |
Integration / custom tooling | Signal-scoped |
repository_dispatch advisory-email-received |
Integration / email pipeline | Signal-scoped |
The
dependabot_alertevent trigger is not supported. It is not in GitHub's push-time JSON schema and causes a validation failure email on every push to every branch of every repo where the workflow file exists.
- Severity filter:
critical,high,moderate - Ecosystem filter: npm only
- Manifest filter:
package-lock.json,package.json,yarn.lock,pnpm-lock.yaml,npm-shrinkwrap.json - One PR per repo per run (batched)
- Duplicate unchanged alert sets are suppressed per repository
- Reuses open Dependabot PRs first; falls back to local fix branch
- Auto-merges low-risk/simple fixes after successful validation
- Classifies force/major compatibility paths as
breaking-upgradeand keeps them manual - Email suppressed if all alerts already notified and no new actionable outcomes
This JSON payload is built and forwarded automatically by the per-repo listener workflow. You do not need to construct it manually in production.
{
"source": "dependabot_alert",
"repository": "your_account/repo1",
"cve_ids": ["CVE-2026-31802"],
"ghsa_ids": ["GHSA-9HJG-PF89-8W2R"],
"dependency_names": ["tar"],
"alert_number": 42,
"advisory_url": "https://github.com/your_account/repo1/security/dependabot/42"
}- In production mode, run with advisory signal payloads only (
PROCESS_ONLY_EMAIL_SIGNAL=true). - To simulate a specific alert locally, provide
ADVISORY_SIGNAL_PAYLOADand setPROCESS_ONLY_EMAIL_SIGNAL=true.
Example local commands:
# Targeted listener payload simulation
PROCESS_ONLY_EMAIL_SIGNAL=true ADVISORY_SIGNAL_PAYLOAD='{"repository":"owner/repo","ghsa_ids":["GHSA-xxxx-yyyy-zzzz"],"dependency_names":["tar"]}' DRY_RUN=true npm run dev- Set
EMAIL_TOto the review inbox. - Set
EMAIL_FROMto the sender address. - Set SMTP values (
SMTP_HOST,SMTP_PORT,SMTP_SECURE,SMTP_USER,SMTP_PASS) for your email provider.
Use ./.github/workflows/security-pr-agent.yml. Use ./scripts/rollout-actions.sh to configure variables and secrets automatically.
Add repository variables:
ACCOUNT_LOGIN(recommended)ALERT_REPOSITORIES(optional)RAW_GITHUB_EMAIL(optional local/debug fallback)ADVISORY_SIGNAL_PAYLOAD(optional local/debug fallback)DRY_RUNVULN_SEVERITIESBRANCH_PREFIXMAX_ALERTS_PER_REPOREPO_COMMANDSINSTALL_RETRY_WITH_LEGACY_PEER_DEPSEMAIL_ENABLEDEMAIL_FAIL_OPENSMTP_HOSTSMTP_PORTSMTP_SECUREEMAIL_TO(required whenEMAIL_ENABLED=true)EMAIL_FROM(required whenEMAIL_ENABLED=true)
Add repository secrets:
SECURITY_AGENT_GITHUB_TOKENSMTP_USER(required whenEMAIL_ENABLED=true)SMTP_PASS(required whenEMAIL_ENABLED=true)
Production settings location:
- GitHub repository -> Settings -> Secrets and variables -> Actions
- Full rollout guide: ./.github/docs/GITHUB_ROLLOUT_CHECKLIST.md
Automated rollout command:
./scripts/rollout-actions.sh owner/repo .env --dispatchProduction mode command:
./scripts/set-production-mode.sh owner/repo --enable-email- Configure variables/secrets from
.env:
./scripts/rollout-actions.sh owner/repo .env- Switch to live mode:
./scripts/set-production-mode.sh owner/repo --enable-email- Trigger workflow with advisory payload:
gh workflow run security-pr-agent.yml \
--repo owner/repo \
-f advisory_email="$(cat advisory-email.txt)"Event-driven dispatch through repository_dispatch (recommended for integrations):
gh api repos/owner/repo/dispatches \
-f event_type='advisory-email-received' \
-f client_payload='{"advisory_email":"<raw advisory body>"}'Listener-based dispatch (optional — for custom integrations only):
- The per-repo listener template at ./.github/templates/repository-vulnerability-listener.yml can be used to forward custom signals via
workflow_dispatch. It does not usedependabot_alert(see note below). - Set target-repo variable
CENTRAL_SECURITY_AGENT_REPO=owner/github-vuln-pr-agent. - Set target-repo secret
CENTRAL_SECURITY_AGENT_DISPATCH_TOKENwith a token that can dispatch to the central repo.
Important: Do not add a listener workflow that uses
on: dependabot_alert. That event is not in GitHub's push-time YAML schema validator. The file will be rejected on every push and generate failure emails. The dailycronsweep is the recommended detection method.
Automate listener rollout to one target repo:
./scripts/install-repo-listener.sh owner/target-repo owner/github-vuln-pr-agent .env- Start with
DRY_RUN=trueuntil you validate behavior. - For repositories with custom command needs, set
REPO_COMMANDSas JSON. - Install retries automatically fall back to
--legacy-peer-depswhenINSTALL_RETRY_WITH_LEGACY_PEER_DEPS=true. - Auto-merged outcomes are reported directly in email; manual-review outcomes include PR links and suggested actions.
- Email reports include failure category for faster production triage.
breaking-upgradefailure category highlights force/major-risk remediation paths.EMAIL_FAIL_OPEN=truekeeps production remediation running even if the email provider is temporarily down.- Repeated non-actionable alerts are suppressed from email — no noise on quiet days.
- Duplicate unchanged alert sets are suppressed per repository to prevent repeat churn.
- Skipped alerts usually mean no patched version exists yet or the repo is already up to date.
- GitHub's own Dependabot emails are separate from agent emails — both may arrive on the same day for the same alert.
- To add a new repo to the monitored set: add
owner/new-repotoALERT_REPOSITORIESin the central agent's GitHub Actions variables (or useACCOUNT_LOGINto auto-discover all owned repos). No per-repo workflow file is needed. - Use ./.github/docs/GITHUB_ROLLOUT_CHECKLIST.md for the full GitHub setup and go-live sequence.
GITHUB_TOKEN="$(gh auth token)" npm run e2e:recommendationUnattended local run:
./scripts/run-e2e-recommendation.shUnattended GitHub Actions run:
-
Trigger with
workflow_dispatchafter settingE2E_*variables and sharedEMAIL_*/SMTP_*runtime values. -
Optional overrides:
-
E2E_TARGET_REPO(defaultyour_account/repo1) -
E2E_LIBRARY(defaultis-odd) -
E2E_LIBRARY_VERSION(default3.0.1) -
E2E_EMAIL_MODE(ethereal,smtp, oroff; defaultethereal) -
E2E_FLOW_MODE(add-only-closeoradd-remove-merge; defaultadd-only-close) -
E2E_AUTO_CLOSE_ON_ERROR(true/false; defaulttrue) -
EMAIL_TO,EMAIL_FROM -
SMTP_HOST,SMTP_PORT,SMTP_SECURE,SMTP_USER,SMTP_PASS