PHASE 7 OF 14
Security: Repository & Code Security
The full GitHub security stack — Dependabot alerts vs security updates vs version updates, dependabot.yml configuration, secret scanning with push protection, dependency review in PRs, security advisories, and minimising blast radius with scoped secrets
Dependabot
Secret Scanning
Push Protection
CVE
CVSS
Security Advisories
7.1
GitHub Security Features Overview
GitHub's security tooling covers four distinct threat categories. Understanding which feature addresses which threat is the starting point for building a coherent security posture.
Dependabot alerts
Threat: known vulnerabilities in your dependencies. GitHub compares your dependency graph against the GitHub Advisory Database (GHSA) and alerts when a match is found. Passive — alerts you, doesn't fix anything.
Dependabot security updates
Threat: same as above. GitHub automatically opens a PR that bumps the vulnerable dependency to a non-vulnerable version. Reactive — responds to newly published CVEs.
Dependabot version updates
Threat: dependency staleness and future vulnerability exposure. Proactively opens PRs to keep dependencies current on a schedule. Configured in dependabot.yml.
Secret scanning
Threat: accidentally committed credentials (API keys, tokens, passwords). Scans every push and every historical commit for known secret patterns. Push protection blocks commits containing secrets before they land.
Code scanning (CodeQL)
Threat: code-level vulnerabilities (SQL injection, XSS, path traversal, etc.). Static analysis that runs on every PR. Covered in detail in Phase 8.
Dependency review
Threat: introducing a new vulnerable dependency in a PR. Shows a diff of dependency changes and blocks merge if any new dependency has a known CVE above your threshold.
Security advisories
Threat: vulnerabilities in your own code that need coordinated disclosure. Private space to draft, collaborate on, and publish security advisories; can request a CVE ID from GitHub.
Private vulnerability reporting
Threat: security researchers reporting bugs publicly instead of privately. Gives researchers a private channel to submit reports directly into a draft advisory without your email address.
AVAILABILITY
Most features are free for public repos. For private repos: Dependabot alerts and secret scanning are free on all plans. Secret scanning push protection, code scanning, and private vulnerability reporting require GitHub Advanced Security (GHAS) — included in GitHub Enterprise and available as an add-on for Teams.
7.2
Dependabot: Alerts vs Security Updates vs Version Updates
| Dependabot Alerts | Dependabot Security Updates | Dependabot Version Updates |
| What triggers it |
A CVE is published that matches your dependency graph |
Same — plus GitHub can find a non-vulnerable version to upgrade to |
A newer version of a dependency is available (regardless of CVEs) |
| What it does |
Creates a security alert in the Security tab; optionally emails/Slacks |
Opens a PR bumping the vulnerable package to the minimum safe version |
Opens a PR bumping the package to the latest (or configured) version on a schedule |
| Where configured |
Settings → Security → Dependabot alerts (on/off toggle) |
Settings → Security → Dependabot security updates (on/off toggle) |
.github/dependabot.yml file |
| Requires dependabot.yml? |
No |
No |
Yes |
| Typical team policy |
Always on; review critical/high within 48 hours |
Always on; auto-merge patches, review minors manually |
On for all dependency ecosystems; group patch updates; require review for majors |
SECURITY UPDATES ≠ VERSION UPDATES
A Dependabot security update PR only bumps to the
minimum version that fixes the CVE — not the latest version. A version update PR bumps to the latest. Both types can coexist; they serve different purposes. Teams that only rely on security updates can still accumulate months of version lag, increasing future upgrade friction.
7.3
dependabot.yml: Ecosystems, Groups, Ignore Rules & Reviewers
A well-configured dependabot.yml keeps your dependencies fresh without drowning the team in PR noise. The key levers are scheduling, grouping, and ignore rules.
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Kolkata
open-pull-requests-limit: 10
reviewers:
- acme/platform-leads
labels:
- dependencies
- area/frontend
commit-message:
prefix: chore
include: scope
groups:
non-major:
update-types:
- minor
- patch
major:
update-types:
- major
ignore:
- dependency-name: webpack
update-types: [version-update:semver-major]
- dependency-name: typescript
versions: ['>=6.0.0']
- package-ecosystem: maven
directory: /
schedule:
interval: weekly
groups:
spring-boot:
patterns: ['org.springframework.boot:*']
other-deps:
patterns: ['*']
exclude-patterns: ['org.springframework.boot:*']
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
groups:
actions:
patterns: ['*']
- package-ecosystem: docker
directory: /
schedule:
interval: monthly
Supported ecosystems
npm
maven
gradle
pip
poetry
gomod
cargo
nuget
bundler
composer
docker
github-actions
terraform
hex
pub
GROUPING STRATEGY
Without groups, Dependabot opens one PR per package. A large project can generate 30+ PRs per week — impossible to review meaningfully. Group
patch +
minor updates into a single "maintenance" PR. Keep
major updates separate so they get deliberate attention. Pin framework families (Spring Boot, React, Angular) into their own groups since their packages must be updated together.
7.4
Secret Scanning: Default Patterns, Custom Patterns & Push Protection
Secret scanning watches for credentials in your code. It operates in two modes: retroactive scanning (after a secret is committed) and push protection (blocking the commit before it lands).
Default patterns
GitHub maintains a list of 200+ patterns for credentials from major providers — AWS access keys, GitHub tokens, Stripe keys, Slack webhooks, GCP service accounts, Twilio keys, and dozens more. These are detected automatically without any configuration.
When a match is found on a private repo, GitHub notifies the repo admins and the commit author. For public repos, GitHub also notifies the affected service provider (e.g., AWS, Stripe) who may revoke the credential automatically.
Push protection
Push protection intercepts a push before it reaches the remote. When a secret pattern is detected, the push is rejected with a message identifying the file and pattern type:
$ git push origin main
remote: error: GH013: Repository rule violations found for refs/heads/main.
remote:
remote: - Push cannot contain secrets
remote: —— AWS Access Key ———————————————————————————
remote: locations:
remote: - commit: a3f8d12
remote: path: src/config.js:14
remote:
remote: To push, remove the secret from your commit history
remote: and try again. Alternatively, to skip this check
remote: use the GitHub web UI to allow this secret.
Removing a committed secret
$ git rebase -i HEAD~3
$ git restore --staged src/config.js
$ git add src/config.js
$ git commit --amend
$ git rebase --continue
$ pip install git-filter-repo
$ git filter-repo --path src/config.js --invert-paths
REVOKE FIRST
Removing the secret from Git history does not make it safe. The secret was exposed during the push — bots scan GitHub in real-time and can capture secrets within seconds of exposure.
Revoke the credential immediately, then clean the history. Treat the secret as compromised regardless of how quickly you removed it.
Bypass policies
Sometimes push protection generates false positives (a test fixture that looks like a real token). The bypass options are:
- It's used in tests — the value is a test or placeholder, not a real credential
- It's a false positive — the pattern matched but it's not actually a secret
- I'll fix it later — acknowledge and push (creates an audit event; admin can see this)
All bypasses are logged in the audit log. Org admins can configure which bypass reasons are allowed and which require explicit org-level approval.
Custom secret scanning patterns
For internal credentials (internal API keys, custom token formats), add custom patterns:
Pattern name: Acme Internal API Key
Pattern: acme_[a-z0-9]{32}
Test string: acme_k7f2m9x1q3w8e5r4t6y0u2i1o9p3a5s7
7.5
Security Policies, Private Vulnerability Reporting & Coordinated Disclosure
SECURITY.md
A SECURITY.md file tells security researchers how to report vulnerabilities. GitHub surfaces it in the Security tab and adds a "Report a vulnerability" button when private vulnerability reporting is enabled.
## Security Policy
### Supported Versions
| Version | Supported |
|---------|--------------------|
| 3.x | ✅ Actively supported |
| 2.x | ✅ Security fixes only |
| < 2.x | ❌ End of life |
### Reporting a Vulnerability
**Please do not file a public GitHub issue for security vulnerabilities.**
#### Option 1 — GitHub Private Reporting (preferred)
Use the "Report a vulnerability" button in the Security tab.
We'll respond within 48 hours.
#### Option 2 — Email
security@acme.com (PGP key at https://acme.com/pgp.asc)
### Our Process
1. Acknowledge receipt within 48 hours
2. Confirm the vulnerability and assess severity within 5 business days
3. Develop and test a patch
4. Coordinate disclosure timing with the reporter
5. Release the patch and publish a security advisory
6. Credit the reporter (unless they prefer anonymity)
We follow a 90-day coordinated disclosure policy.
Enabling private vulnerability reporting
Settings → Security → Private vulnerability reporting → Enable. Once enabled:
- A "Report a vulnerability" button appears on the Security tab
- Reports land in a private draft security advisory, visible only to repo admins and collaborators you invite
- GitHub automatically suggests CVSS scores and CWE categories
- The reporter can collaborate on the draft advisory before it's published
WHY THIS MATTERS
Without private reporting, security researchers who discover a vulnerability in your project have two bad options: email an unknown address (often ignored) or open a public issue (exposes the vulnerability to everyone). Private reporting gives them a proper channel, which means more responsible disclosures and fewer public zero-days in your code.
7.6
Repository Security Settings Checklist
A hardened private repo should have all of these enabled. Walk through Settings → Security for each repo or automate via the REST API.
| Setting | Where | Recommended | Notes |
| Dependabot alerts |
Security → Dependabot |
✅ On |
Free on all plans; no reason to leave off |
| Dependabot security updates |
Security → Dependabot |
✅ On |
Automatically opens fix PRs for known CVEs |
| Secret scanning |
Security → Secret scanning |
✅ On |
Free on all plans for private repos |
| Secret scanning push protection |
Security → Secret scanning |
✅ On (GHAS) |
Blocks secrets before they land — much better than post-commit alerts |
| Private vulnerability reporting |
Security → Private vulnerability reporting |
✅ On |
Free; gives researchers a responsible disclosure channel |
| Dependency graph |
Security → Dependency graph |
✅ On |
Required for Dependabot alerts to work; enables dependency review in PRs |
| Private fork policy |
Settings → General |
Consider |
Allow forking privately within your org for internal contributors — disable public forking if the code is sensitive |
Auditing security settings across all repos in an org
$ gh api orgs/acme/repos --paginate \
--jq '.[] | select(.private == true) | {
name: .name,
secret_scanning: .security_and_analysis.secret_scanning.status,
push_protection: .security_and_analysis.secret_scanning_push_protection.status,
dependabot_alerts: .security_and_analysis.dependabot_security_updates.status
}'
7.7
Dependency Review: Blocking PRs on New Vulnerable Dependencies
The dependency review action compares the dependency manifest changes in a PR against the GitHub Advisory Database. It blocks the PR if any newly added dependency has a known CVE above your severity threshold.
on:
pull_request:
branches: [main, 'release/**']
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
allow-licenses: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC
deny-licenses: GPL-2.0, GPL-3.0, AGPL-3.0
comment-summary-in-pr: always
warn-only: false
When the action finds a problem it posts a comment on the PR showing:
- The vulnerable package name and version being added
- The CVE ID, CVSS score, and severity
- The vulnerable version range and the minimum safe version
- A direct link to the GitHub Advisory
License compliance gate
The allow-licenses / deny-licenses inputs add a licence compatibility check at the PR level — catching GPL contamination or AGPL obligations before they reach your codebase. This is particularly valuable for commercial software that can't adopt copyleft dependencies.
COMPLEMENTARY TO DEPENDABOT
Dependabot alerts tell you about vulnerabilities in
existing dependencies after they're committed. Dependency review catches vulnerabilities in
new dependencies being introduced via a PR — before they merge. Use both together.
7.8
Security Advisories: Drafting, CVSS, CVE IDs & Publishing
When a vulnerability is found in your code (by your team or a researcher), GitHub Security Advisories give you a private workspace to draft the advisory, coordinate with the reporter, develop a patch in a private fork, and publish when ready.
Advisory lifecycle
- Create a draft — Security tab → Advisories → New draft security advisory
- Fill in details — affected package, vulnerable version range, severity (CVSS), CWE category, description
- Invite collaborators — add the reporter and your security team; all communication is private
- Request a CVE — GitHub is a CVE Numbering Authority (CNA); request a CVE ID directly from the advisory
- Create a private fork — GitHub creates a temporary private fork where you can develop and review the fix without exposing it
- Publish — when the fix is released, publish the advisory. It enters the GitHub Advisory Database and Dependabot begins alerting anyone who uses your package.
CVSS scoring basics
CVSS (Common Vulnerability Scoring System) v3.1 produces a score from 0–10 based on base metrics:
| Score range | Severity | Typical response SLA |
| 9.0–10.0 |
Critical |
Patch within 24–48 hours; emergency release if needed |
| 7.0–8.9 |
High |
Patch within 7 days; include in next planned release |
| 4.0–6.9 |
Medium |
Patch within 30 days; schedule in next sprint |
| 0.1–3.9 |
Low |
Patch within 90 days; best-effort |
GitHub's advisory UI has a CVSS calculator — fill in the attack vector (network/local), privileges required, user interaction, and impact (confidentiality/integrity/availability) and it produces the score. Don't agonise over precision; a score accurate to ±1 point is sufficient for triage.
CWE categories (common ones)
7.9
Minimising Blast Radius with Environment-Scoped Secrets
The goal of blast radius minimisation is ensuring that if any single credential is compromised — through a leaked secret, a compromised runner, or a malicious dependency — the attacker can only reach a bounded subset of your systems.
The problem with repo-level secrets
Bad: one DEPLOY_KEY secret at repo level
→ Any workflow in the repo has access to DEPLOY_KEY
→ A compromised third-party action in your CI workflow accesses DEPLOY_KEY
→ Attacker can deploy to production
Better: environment-scoped secrets + branch protection
→ DEPLOY_KEY_STAGING in "staging" environment
→ DEPLOY_KEY_PROD in "production" environment (requires manual approval)
→ A compromised CI action can only reach STAGING credentials
→ PRODUCTION deployment is gated by human approval in the environment
Credential scoping strategy
| Credential type | Scope | Rationale |
| Docker registry read token (to pull base images) |
Org-level |
All repos need it; read-only, low blast radius |
| npm publish token |
Repo-level |
Scoped to one package; only the publish job needs it |
| Staging deploy key |
Environment: staging |
Only the deploy job targeting staging gets access |
| Production deploy key |
Environment: production (with required reviewers) |
Human approval gate + scoped credential = two-factor deploy |
| Database admin password |
Never in GitHub secrets |
Use OIDC + cloud IAM or Vault dynamic secrets instead |
OIDC as the ultimate blast radius minimiser
The best approach is to eliminate long-lived credentials from GitHub entirely using OIDC (Phase 6.9). When there are no stored secrets, there are no stored secrets to steal. The OIDC token is short-lived, scoped to the run, and can only be exchanged for credentials by a job that matches your IAM trust policy claims (specific repo + specific branch).
SECURITY POSTURE CHECKLIST
For each repo, ask:
1. Do we use OIDC for cloud auth where possible?
2. Are secrets scoped to environments rather than the repo?
3. Does the production environment require a human approval?
4. Does the CI workflow run with minimum necessary
permissions:?
5. Are all third-party actions pinned to a SHA?
If the answer to any of these is no, you have identified a concrete security improvement.
Up Next — Phase 8: Supply Chain Security
CodeQL and SAST integration, SARIF uploads, artifact attestations, SBOM generation, Sigstore/cosign container signing, SHA-pinned actions with Dependabot, and OpenSSF Scorecard.
Continue to Phase 8 →
Back to Hub