GitHub Advanced Security: Complete Enterprise Setup and Optimization Guide
Most GitHub security deployments fail within 90 days due to alert backlog, not lack of features. The rollout sequence matters more than configuration: secret scanning first, code scanning with the default query suite, then dependency review. This guide covers enterprise-scale deployment across GitHub Code Security, GitHub Secret Protection, Defender for DevOps integration, and alert triage that actually works.

Video transcript
Here's the thing: most GitHub Advanced Security deployments crash and burn within ninety days. Not because the features don't work, but because teams drown in alerts they never planned to handle. When you flip on G A S without a rollout strategy, you're like opening every fire alarm in a building simultaneously. Teams panic, alerts get ignored, and security becomes noise instead of signal. That's when people give up and disable the whole thing. Start with secret scanning first. Think of it as the security guard checking IDs at the door before anyone enters the building. You catch leaked credentials, API keys, and tokens before attackers find them in your commit history. That's your fastest win and lowest false positive rate. Then layer in code scanning with the default CodeQL query suite. This is your internal code auditor, looking for logic flaws and unsafe patterns in the actual application logic. You're not guessing at what to scan. You're using O W A S P aligned detection that teams have already tuned in thousands of enterprises. Finally add dependency review to catch vulnerable libraries before they land in main. It's like vetting your supply chain before signing contracts. You see exactly which packages introduced risk and can make informed decisions about updates or alternatives. Start small with secret scanning this week. Pick one team, run it clean, then expand. The guide walks you through license accounting, Defender for DevOps integration, and triage workflows that actually work. Read the complete guide at protego dot me.
The Alert Backlog That Kills Every GHAS Deployment
Three months after enabling GitHub Advanced Security across a 500-developer GitHub Enterprise Cloud org, the AppSec team stares at 47,000 open code scanning alerts. 12,000 fired in the first week because someone toggled the security-extended query suite on every repository without testing alert volume on representative codebases. The security program has regressed: the team now spends its time managing alert queues instead of doing security work.
This is not a capability problem. It is a rollout sequencing problem. GitHub's paid security packaging is now commonly described as GitHub Code Security and GitHub Secret Protection, with many teams still using the broader GHAS shorthand. In practical terms, you are deploying code scanning with CodeQL, secret scanning with push protection, and dependency controls with Dependabot and dependency review. Each has a different signal-to-noise profile, different remediation latency, and different blast radius if you enable it wrong. The sequence matters more than any individual configuration setting.
Terminology note, June 2026: when you read GitHub docs, expect the current packaging names GitHub Code Security and GitHub Secret Protection. "GitHub Advanced Security" is still widely used in teams and older docs, but buying and entitlement conversations increasingly happen through those product names.
GHAS License Accounting Before You Enable Anything
Paid GitHub security features are licensed by the product package and account plan. Historically, GHAS was priced around active committers for private/internal repositories, and many enterprise contracts still use that language. The operational trap remains the same: if you enable paid security features org-wide on day one, every repository and contributor in scope can affect your entitlement, budget, and alert volume. Confirm your current GitHub Code Security and GitHub Secret Protection licensing model before rollout.
Audit your active committer count before enabling:
# Check org-level security billing or entitlement data where available
gh api \
-H "Accept: application/vnd.github+json" \
/orgs/{org}/settings/billing/advanced-security \
--jq '{
total_seats: .total_advanced_security_committers,
purchased_seats: .purchased_advanced_security_committers,
repos: [.repositories[] | {
name: .name,
committers: .advanced_security_committers
}] | sort_by(-.committers) | .[0:20]
}'Use this data to prioritize: enable Code Security and Secret Protection first on your highest-risk repositories (customer-facing APIs, repositories with PII processing, financial systems) and expand to lower-risk repositories as your remediation capacity scales.
For GitHub Enterprise Server 3.10+, use the license API endpoint at /api/v1/enterprise/settings/license and the management console committer report. GHES charges are reconciled quarterly, not in real time, so the overage risk is a budget issue rather than an immediate access block.
Phase 1: Secret Scanning with Push Protection
Secret scanning delivers the highest ROI per hour of configuration effort. The detection coverage spans 200+ partner patterns covering major cloud providers, payment processors, source control tokens, and communication APIs. The false positive rate for high-confidence patterns is under 5% and detection requires zero developer workflow changes.
Push protection upgrades the posture from reactive (find secrets after commit) to preventive (block secrets at push). A developer pushing a commit containing a covered secret gets:
remote: error: GH013: Repository rule violations found for refs/heads/main.
remote:: GITHUB PUSH PROTECTION
remote: Detected secrets for the following ids:
remote: 0-github_token -> .env (line 3)
remote: To push, please remove the detected secrets or use a bypass.Enable both at org level:
gh api \
--method PATCH \
-H "Accept: application/vnd.github+json" \
/orgs/{org} \
-f "security_and_analysis[secret_scanning][status]=enabled" \
-f "security_and_analysis[secret_scanning_push_protection][status]=enabled"The bypass workflow allows developers to push through a detected secret with a justification: test credential, false positive, or will fix later. Every bypass creates an auditable event. Review bypasses weekly for any bypass of production-format credentials:
# Find all recent push protection bypasses
gh api \
-H "Accept: application/vnd.github+json" \
"/orgs/{org}/secret-scanning/alerts?state=open&per_page=100" \
--paginate \
--jq '[.[] | select(.push_protection_bypassed == true) | {
number: .number,
repo: .repository.full_name,
type: .secret_type_display_name,
bypassed_at: .push_protection_bypassed_at,
bypasser: .push_protection_bypassed_by.login,
url: .html_url
}]'Any bypass of an AWS secret access key, Azure service principal credential, or GitHub personal access token that is marked as "false positive" but has a format consistent with a production service warrants immediate investigation.
Custom Secret Patterns for Internal Tokens
GitHub's built-in patterns detect third-party service credentials. They will not detect your organization's proprietary tokens: internal API keys with custom formats, internal service account credentials, HMAC signing secrets with org-specific prefix patterns.
Define custom patterns at org level using regex. Navigate to Org Settings > Code security and analysis > Custom patterns > New pattern. The pattern editor validates your regex against sample strings before publishing.
Example: internal API key format CORP-[A-Z]{8}-[0-9A-F]{32}:
- Pattern: CORP-[A-Z]{8}-[0-9A-F]{32}
- Test string: CORP-ABCDEFGH-0123456789ABCDEF0123456789AB
Limitation as of GHEC May 2026: custom patterns do not support push protection. They scan committed content retroactively. Track the GitHub public roadmap for this capability. For the highest-risk internal token formats, consider implementing pre-receive hooks on GitHub Enterprise Server or pre-commit hooks deployed via your developer tooling repository.
Phase 2: Code Scanning with CodeQL
Default Setup vs. Advanced Setup
Default setup auto-detects repository languages, selects an appropriate query suite, and runs CodeQL on every push and pull request with no workflow YAML file required. Advanced setup deploys a codeql.yml workflow that you control, enabling custom query packs, build configuration flags, and third-party scanner integration.
For most enterprise repositories: use default setup. Reserve advanced setup for repositories with complex build requirements (native code, monorepos with non-standard build tools) or where you need to run custom CodeQL queries from an internal query pack.
Enable default setup across a repository via API:
gh api \
--method PATCH \
-H "Accept: application/vnd.github+json" \
/repos/{owner}/{repo}/code-scanning/default-setup \
-f state="configured" \
-f query_suite="default" \
-f languages='["javascript","typescript","python","java","csharp"]'Script this against your prioritized repository list for bulk enablement. The API returns the new configuration state and the workflow run that was triggered.
Query Suite Selection
| Suite | Alert Volume (per 100K LOC) | False Positive Rate | Recommended For |
|---|---|---|---|
| `default` | 50 to 200 | Under 5% | All repositories at initial rollout |
| `security-extended` | 200 to 800 | 15 to 25% | Repositories with dedicated AppSec review bandwidth |
| `security-and-quality` | 500 to 2000 | 20 to 35% | Security champion programs; targeted quality initiatives |
The default suite covers OWASP Top 10 categories with high-confidence queries: SQL injection, XSS, command injection, path traversal, unsafe deserialization. The security-extended suite adds medium-confidence queries for CWEs including CWE-338 (weak PRNG), CWE-730 (ReDoS), and broader injection variants.
Start every repository on default. Upgrade to security-extended only after you have demonstrated consistent remediation velocity on default findings. Enabling security-extended org-wide before remediation capacity is established generates a backlog that immediately exceeds what your team can triage, and those alerts then become noise that desensitizes developers to real findings.
Suppression Discipline
Inline CodeQL suppressions (language-specific comment annotations) can silence alerts without fixing the underlying issue. Without enforcement, suppressions accumulate silently. A suppressed critical finding in a production repository is a compliance liability.
Enforce suppression documentation with a PR workflow check:
# .github/workflows/codeql-suppression-audit.yml
name: CodeQL Suppression Audit
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
check-suppressions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Reject undocumented CodeQL suppressions
run: |
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -E '.(java|js|ts|py|cs|go|rb)$' || true)
if [ -z "$CHANGED_FILES" ]; then exit 0; fi
BARE=$(echo "$CHANGED_FILES" | xargs grep -l "codeql[" 2>/dev/null | xargs grep -E "codeql[" | grep -v -E "(reason|justif|false.pos|intentional|approved|ticket)" || true)
if [ -n "$BARE" ]; then
echo "Found CodeQL suppressions without documented justification:"
echo "$BARE"
echo "Add a reason comment: # codeql[rule-id] reason: <explanation>"
exit 1
fi
echo "All suppressions are documented."This does not prevent suppressions, but it requires that the suppression comment includes one of the justification keywords. Adjust to match your team's convention.
Phase 3: Dependency Review and Supply Chain Security
Dependency Review Action in PRs
Dependabot alerts fire when a dependency already in your repository has a known CVE. The dependency review action operates at PR time and blocks merges that introduce new vulnerable dependencies. Both are needed; the PR-time check is operationally more valuable because it prevents vulnerabilities from entering the default branch rather than tracking them after the fact.
# .github/workflows/dependency-review.yml
name: Dependency Review
on:
pull_request:
branches: [main, master, 'release/**']
permissions:
contents: read
pull-requests: write
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
deny-licenses: GPL-3.0, AGPL-3.0, SSPL-1.0
comment-summary-in-pr: always
base-ref: ${{ github.event.pull_request.base.sha }}
head-ref: ${{ github.event.pull_request.head.sha }}The deny-licenses parameter is a legal compliance control, not just a security control. AGPL-3.0 and SSPL-1.0 have aggressive copyleft requirements that affect commercial software. Catching these at PR time prevents the situation where a library is deep in your dependency tree for six months before legal review flags it.
Dependabot Auto-Merge for Patch Security Updates
Without grouping and auto-merge, Dependabot generates one PR per vulnerable dependency. In a large JavaScript project with 300 direct dependencies, a batch advisory publication generates 40 simultaneous PRs. Auto-merge patch-level security updates to reduce remediation latency from "whenever someone reviews it" to under 24 hours:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
time: "06:00"
timezone: "UTC"
open-pull-requests-limit: 5
groups:
patch-security:
applies-to: security-updates
patterns: ["*"]
update-types: ["patch"]
minor-security:
applies-to: security-updates
patterns: ["*"]
update-types: ["minor"]
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
groups:
security-all:
applies-to: security-updates
patterns: ["*"]# .github/workflows/dependabot-auto-merge.yml
name: Dependabot Auto-Merge
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
permissions:
contents: write
pull-requests: write
jobs:
auto-merge:
if: github.actor == 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get Dependabot metadata
id: meta
uses: dependabot/fetch-metadata@v2
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Auto-merge patch security updates
if: |
steps.meta.outputs.update-type == 'version-update:semver-patch' &&
steps.meta.outputs.dependency-type == 'direct:production'
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}Scope auto-merge to direct:production dependencies only. Transitive dependency updates have a higher regression risk. Minor-level updates should go through standard PR review.
Enterprise Scale: Organization Security Configurations and Rulesets
Organization Security Configurations
GitHub Enterprise Cloud lets you define Security Configurations: preset bundles of GHAS settings that apply to repositories and override repository-level settings. Once applied, repository administrators cannot disable code scanning or push protection.
Create and apply a security configuration:
# Create org security configuration
CONFIG_ID=$(gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/orgs/{org}/code-security/configurations \
-f name="Enterprise Security Baseline" \
-f description="Non-overridable GHAS baseline for all repositories" \
-f advanced_security="enabled" \
-f secret_scanning="enabled" \
-f secret_scanning_push_protection="enabled" \
-f dependabot_alerts="enabled" \
-f dependabot_security_updates="enabled" \
-f code_scanning_default_setup="enabled" \
-f private_vulnerability_reporting="enabled" \
--jq '.id')
# Apply to all existing repositories
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/orgs/{org}/code-security/configurations/${CONFIG_ID}/attach \
-f scope="all" \
-f default_for_new_repos=trueBranch Rulesets with Required Security Checks
Repository rulesets enforce required status checks at the org level. CodeQL, dependency review, and secret scanning results become blocking conditions before merging to main:
{
"name": "security-baseline",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
"include": ["refs/heads/main", "refs/heads/master", "refs/heads/release/*"],
"exclude": []
}
},
"rules": [
{
"type": "required_status_checks",
"parameters": {
"strict_required_status_checks_policy": true,
"required_status_checks": [
{"context": "CodeQL"},
{"context": "Dependency Review"},
{"context": "CodeQL Suppression Audit"}
]
}
},
{
"type": "required_pull_request_reviews",
"parameters": {
"required_approving_review_count": 1,
"dismiss_stale_reviews_on_push": true,
"require_code_owner_review": true
}
},
{"type": "non_fast_forward"},
{"type": "deletion"}
]
}Deploy via API for consistent application across repositories:
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/orgs/{org}/rulesets \
--input ruleset.jsonThe strict_required_status_checks_policy flag is important: it prevents merging if the required check has not run at all, not just if it has failed. A developer cannot bypass CodeQL by only modifying files that do not trigger the scan workflow.
Integrating GHAS with Azure: Defender for DevOps
Defender for DevOps (part of Defender for Cloud) pulls GHAS findings into Azure security posture alongside your cloud resource misconfigurations. The integration uses a GitHub App installed on your org, configured through the Defender for Cloud connectors page.
Once connected, GHAS findings appear as Defender for Cloud recommendations:
- "Repositories should have code scanning enabled"
- "Code scanning findings should be resolved"
- "Repositories should have secret scanning enabled"
The operational value: your Azure SOC team sees AppSec posture in the same dashboard as cloud resource misconfigurations without needing direct GitHub access. Security findings from code scanning in a production service repository appear alongside the Azure Defender alerts for that service's cloud resources.
Defender for DevOps also writes findings to the Log Analytics workspace connected to Defender for Cloud. Query GHAS-sourced alerts directly in Sentinel:
SecurityAlert
| where ProviderName == "Microsoft Defender for DevOps"
| extend Properties = parse_json(ExtendedProperties)
| project
TimeGenerated,
AlertName,
AlertSeverity,
RepositoryName = Properties.repositoryName,
Branch = Properties.branch,
RuleId = Properties.ruleId,
FilePath = Properties.filePath,
RemediationSteps
| where AlertSeverity in ("Critical", "High")
| order by TimeGenerated descCreate a Sentinel analytics rule that fires on new critical GHAS findings in production repositories. The rule condition: AlertSeverity == "Critical" and RepositoryName contains "prod". Detection latency with this setup is under 15 minutes from the time CodeQL posts the finding to GitHub.
Alert Triage at Scale
Without a defined SLA, GHAS alerts accumulate indefinitely. Teams with no triage process see 80% of critical alerts age past 30 days within 90 days of GHAS deployment. The alerts become background noise.
Define remediation SLAs:
| Severity | Target Remediation | Escalation |
|---|---|---|
| Critical | 7 calendar days | Engineering VP + CISO notification |
| High | 30 calendar days | Engineering Director |
| Medium | 90 calendar days | Team lead |
| Low | Next sprint or dismiss with written justification | None |
Track SLA compliance with a weekly API script:
# Find critical alerts breaching the 7-day SLA
CUTOFF=$(date -d '7 days ago' --iso-8601=seconds 2>/dev/null || date -v-7d +%Y-%m-%dT%H:%M:%SZ)
gh api \
-H "Accept: application/vnd.github+json" \
"/orgs/{org}/code-scanning/alerts?severity=critical&state=open&per_page=100" \
--paginate \
--jq --arg cutoff "$CUTOFF" \
'[.[] | select(.created_at < $cutoff) | {
repo: .repository.full_name,
rule: .rule.id,
description: .rule.description,
created: .created_at,
age_days: (now - (.created_at | fromdateiso8601) / 86400 | floor),
url: .html_url
}]' | jq 'sort_by(-.age_days)'The pre-GHAS backlog deserves a separate treatment. Alerts older than 180 days from repositories with no active commits or no active deployments can be bulk-dismissed with an organizational justification: "Pre-GHAS baseline; triaged against current attack surface; repository inactive." This is not security theater. It reflects that an unmaintained repository with no running deployment has a different risk profile from an actively deployed service. Do not let historical backlog crowd out actionable new findings in production systems.
Hardening Checklist
- [ ] Active committer count audited before enabling GHAS broadly: billing API checked for license headroom vs. org-wide committer count
- [ ] Secret scanning enabled at org level for all private and internal repositories
- [ ] Push protection enabled at org level: no developer can push a covered secret without creating an audit event
- [ ] Custom secret patterns defined for internal token formats: proprietary API keys, service account credentials, signing secrets
- [ ] Push protection bypass events reviewed weekly: any bypass of a production-format credential triggers an investigation
- [ ] Code scanning default setup deployed org-wide: query_suite=default at rollout, not security-extended
- [ ] security-extended query suite applied only to repositories with consistent AppSec review bandwidth demonstrated on default findings first
- [ ] Suppression hygiene enforced: PR workflow rejects bare suppression annotations without documented justification keywords
- [ ] Dependency review action deployed on all PR workflows: fail-on-severity: high, copyleft license deny list (GPL-3.0, AGPL-3.0, SSPL-1.0)
- [ ] Dependabot alerts enabled on all repositories
- [ ] Dependabot security updates grouped: one PR per severity level per ecosystem, not one PR per package
- [ ] Dependabot auto-merge configured for patch-level security updates on direct production dependencies that pass all CI checks
- [ ] Org Security Configuration created and applied: teams cannot disable scanning controls at the repository level
- [ ] Branch rulesets deployed at org scope: CodeQL and dependency review as required status checks for main and master
- [ ] Defender for DevOps connector configured: GHAS findings visible in Defender for Cloud posture and queryable in Sentinel
- [ ] Sentinel analytics rule deployed: alert on new critical GHAS findings in production repositories within 15 minutes
- [ ] Remediation SLA defined and tracked: critical (7 days), high (30 days), medium (90 days) with named escalation paths
- [ ] Pre-GHAS alert backlog age-gated: alerts older than 180 days from inactive repositories reviewed and bulk-dismissed with documented justification
Frequently Asked Questions
What does GitHub Advanced Security include?
GitHub Advanced Security (GHAS) bundles three capabilities: code scanning (powered by CodeQL), secret scanning with push protection, and dependency review. Code scanning identifies security vulnerabilities and code quality issues in pull requests. Secret scanning detects committed credentials and, with push protection enabled, prevents secrets from ever entering the repository in the first place.
How much does GitHub Advanced Security cost?
GHAS is included free for all public repositories on GitHub. For private and internal repositories, GHAS is available on GitHub Enterprise Cloud and GitHub Enterprise Server. Pricing is per active committer per month, where an active committer is any person who authored a commit to a private repository in the billing month. Enterprise licensing is typically negotiated annually.
What is the difference between CodeQL default setup and advanced setup?
Default setup is a no-configuration option that automatically selects the right query suite and language settings for each repository. Advanced setup gives you a CodeQL workflow file you can customize: adjusting query suites, adding custom queries, and controlling scan triggers. Start with default setup for broad deployment and use advanced setup only for repositories where you need fine-grained control or custom detection rules.
What is push protection in GitHub secret scanning?
Push protection is a feature that blocks a git push if the commit contains a secret pattern covered by secret scanning. When a developer tries to push a credential, GitHub rejects the push and presents an in-terminal error identifying the secret type. The developer must either remove the secret or create an explicit bypass with a documented reason. Bypass events are auditable and reviewable by security teams.
How do I integrate GHAS findings with Microsoft Sentinel?
Enable the Defender for DevOps connector in Microsoft Defender for Cloud, then connect your GitHub organization. Defender for DevOps pulls GHAS code scanning and secret scanning alerts into Defender for Cloud's recommendations view and forwards them to Microsoft Sentinel as SecurityAlert events. From there, you can build KQL analytics rules to alert on critical findings in production repositories within minutes of detection.
Get weekly security insights
Cloud security, zero trust, and identity guides — straight to your inbox.
Microsoft Cloud Solution Architect
Cloud Solution Architect with deep expertise in Microsoft Azure and a strong background in systems and IT infrastructure. Passionate about cloud technologies, security best practices, and helping organizations modernize their infrastructure.
Share this article
Questions & Answers
Related Articles
Need Help with Your Security?
Our team of security experts can help you implement the strategies discussed in this article.
Contact Us