GitHub Advanced Security (GHAS)
Pulls code-scanning alerts, secret-scanning findings, Dependabot alerts, branch-protection posture, transitive-dependency risk, and Actions supply-chain indicators from GitHub to derive application / code security KRIs across the software supply chain.
At a glance
| Vendor | GitHub Advanced Security — org-scoped, covers GitHub.com and GitHub Enterprise Server (GHES). |
|---|---|
| Source type | appsec |
| Vendor ID (slug) | github-advanced-security |
| Base URL | https://api.github.com (default, GitHub.com) or https://<ghes-host>/api/v3 (Enterprise Server). GHES private-IP hosts are blocked by Draxis's SSRF guard — see Quirks. |
| Auth method | api-key — fine-grained PAT (recommended) or classic PAT with security_events+repo scopes. Dispatcher emits Authorization: Bearer <token> natively. |
| Schedule default | daily. GitHub's rate limits are generous (5000 req/hr authenticated) but code-search has its own 30-req/min limit; daily is well under both. |
| Licensing | GHAS is a paid add-on on GitHub.com (per-active-committer pricing) or an Enterprise SKU feature on GHES. Without GHAS, secret scanning and code scanning endpoints return 403; Dependabot is free on public repos and included on paid private repos. The connector tolerates 403/404 with warns — tenants without a GHAS license should uncheck the code-scanning, secret-scanning, and Actions KRIs to avoid 0-as-clean misreads. |
| Availability | New in 2026.04. |
Required scopes & roles
Use a fine-grained personal access token (preferred) or a GitHub App installation token. Classic PATs work but grant much broader access than needed.
For a fine-grained PAT scoped to your GitHub organization:
- Resource owner: your GitHub organization (not personal account).
- Repository access: All repositories (the org-level alerts endpoints scan across all repos).
- Organization permissions:
- Administration: Read-only — for branch-protection queries and repo metadata.
- Repository permissions:
- Code scanning alerts: Read-only
- Secret scanning alerts: Read-only
- Dependabot alerts: Read-only
- Contents: Read-only (for workflow-file inspection on supply-chain KRIs)
- Actions: Read-only (for workflow enumeration)
- Metadata: Read-only (auto-granted)
Classic PAT equivalent (if you must): security_events (covers code/secret/Dependabot alerts) + repo (covers branch protection and content reads). Do not grant admin:org or any write scope — the connector never modifies org state.
Setup steps
- (Org admin) Enable Advanced Security features in Org Settings → Code security → Global settings:
- Code scanning: enabled for all eligible repos
- Secret scanning: enabled (with push protection recommended but not required for this KRI)
- Dependabot alerts: enabled
- Dependabot security updates: enabled (not required for KRIs; makes the dependabot-known-exploits number actionable)
- Generate a fine-grained PAT. GitHub web UI: Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token. Name:
draxis-connector. Resource owner: your organization. Expiration: align with your rotation policy (max 1 year; 90 days is a common compromise). - Request org approval if your GitHub organization requires fine-grained PAT approval (Settings → Personal access tokens → Require approval for fine-grained personal access tokens that access resources owned by your organization). An org admin approves the PAT in the org's PAT approval queue before it gains access.
- Verify the token with a quick API check before wiring Draxis:
Should print your org's slug and name. 404 = token doesn't have access to the org (likely pending org approval); 401 = token is wrong or revoked.curl -s -H 'Authorization: Bearer <pat>' \ -H 'X-GitHub-Api-Version: 2022-11-28' \ 'https://api.github.com/orgs/<your-org>' \ | jq '.login, .name' - (GHES only) Verify public-TLS reachability. Draxis runs from public egress IPs; a GHES instance on a private IP / internal network is blocked by Draxis's SSRF guard. Front GHES with a reverse proxy that terminates public TLS, or expose it directly with IP allowlisting.
Wire it into Draxis
- Open Settings → Integrations in your tenant.
- Click Add integration and pick Application / Code Security as the source type.
- Pick GitHub Advanced Security (GHAS) from the vendor dropdown. Draxis pre-fills the GitHub.com base URL, Bearer auth, daily schedule, and seeds
extra_config_jsonwith{"organization":""}. - If you're on GHES, override the API Base URL to
https://<ghes-host>/api/v3. - In API Key / Token, paste the fine-grained PAT. Draxis encrypts it server-side with
encryption.keybefore storage. - In the Extra Config JSON field, replace the empty
organizationwith your GitHub org slug:{"organization":"acme-corp"}. - Click Test. Green means Draxis called
GET /orgs/<org>successfully — the message includes your org's login slug and display name so you can verify. - Under KRIs to import, tick the KRIs you want Draxis to manage. All six
ghas_*KRIs are checked by default; uncheck the code-scanning, secret-scanning, and Actions-supply-chain KRIs if your org doesn't have GHAS enabled (otherwise they report 0, which looks like a clean posture). Selected rows are created on save. Unchecking a previously-imported KRI deletes it on save. - Save. The connector runs
dailyby default; use Run now from run history to trigger the first sync immediately.
KRIs produced
| Slug | Meaning | Derivation |
|---|---|---|
ghas_critical_code_scan_unresolved |
Open CodeQL / code-scanning alerts at severity=critical | GET /orgs/<org>/code-scanning/alerts?state=open&severity=critical&per_page=1, extract total from Link header's rel="last" page number. |
ghas_secrets_unrotated |
Open secret-scanning alerts | GET /orgs/<org>/secret-scanning/alerts?state=open&per_page=1, same last-page technique. "Open" is a proxy for "detected but not yet rotated" — GitHub doesn't expose a direct "rotated" field. |
ghas_dependabot_known_exploits |
Open Dependabot alerts at severity=critical or high | GET /orgs/<org>/dependabot/alerts?state=open paginated; count alerts where security_advisory.severity ∈ {critical, high}. Proxy for "known exploitable" — GitHub flags CVEs with published exploit activity in these tiers. |
ghas_repos_no_branch_protection |
Repos whose default branch lacks a protection rule | GET /orgs/<org>/repos paginated (capped at 500 repos); for each repo, GET /repos/<owner>/<repo>/branches/<default-branch>/protection — count 404s (404 = "no rule configured", not an error). |
ghas_sca_transitive_risk |
Open Dependabot alerts on transitive (indirect) dependencies | Same Dependabot list as #3; count alerts where dependency.relationship == 'indirect'. One paginated API call backs KRIs #3 and #5. |
ghas_unsigned_actions |
Workflow files referencing third-party actions (non-official) | Two code-search calls: GET /search/code?q=org:<org>+path:.github/workflows+"uses:" minus GET /search/code?q=org:<org>+path:.github/workflows+"uses: actions/". The difference ≈ workflow files referencing third-party actions. Approximation — see quirks. |
Each row is a slug the connector writes to. Draxis creates the matching kri rows automatically when you check them in the KRIs to import section of the integration form — no manual API call or seed script needed. Thresholds shown in the table are the seeded defaults; you can edit them freely in the KRIs tab afterwards.
Vendor quirks
- GHAS is a paid add-on on GitHub.com. Code scanning and secret scanning endpoints return 403 on orgs without GHAS licensing. The connector tolerates those and records 0 + warn log. Dependabot alerts are free on public repos and included on private repos on paid plans, so the Dependabot KRIs usually work even without GHAS specifically.
- Fine-grained PATs need org approval if required. Many orgs enable "Require approval for fine-grained personal access tokens" — your token sits in a pending queue until an org admin approves it. 404 on the probe typically means pending approval, not a wrong org name.
- Classic PATs grant too much. Using
reposcope to cover branch-protection reads also grants write access to every repo the user can see. Fine-grained with per-permission read-only is materially tighter; the docs recommend it even though classic works. - "Secrets unrotated" is a proxy, not a direct signal. GitHub doesn't track whether a detected secret was rotated — it just tracks whether the alert was closed. Closing an alert without rotating is possible; the KRI counts "open" as "probably unrotated", which is the operationally useful read.
- "Dependabot known exploits" uses severity as the proxy. GitHub doesn't expose a "has-known-exploit" boolean per alert. Severity critical/high is the strongest practically-available filter. For truly exploit-specific filtering, you'd need to cross-reference CISA KEV catalog against each alert's CVE — roadmap work.
- The Actions supply-chain KRI is a double-count. The search API returns workflow FILES matching, not individual
uses:references. A workflow file with 5 third-party actions counts as 1. Directionally useful for trending, not precise. - Branch-protection check caps at 500 repos. Orgs with more repos get a partial sample; the run log warns with the actual repo count seen. Full enumeration requires multi-page pagination across repos + per-repo protection probes — expensive at GitHub API limits (each repo is 1 call). Capped to keep runs bounded.
- GitHub Enterprise Server on private IPs is blocked. Same story as Splunk / on-prem Palo Alto: Draxis's SSRF guard rejects RFC1918 addresses. Front GHES with a public-TLS reverse proxy and allowlist Draxis's egress.
- Code search API uses its own rate limit. 30 req/min authenticated. The connector makes 2 code-search calls per run — well under the cap even at hourly scheduling.
- Classic PAT expiration does not invalidate existing tokens. An expired classic PAT continues to work until explicitly revoked (this is a GitHub quirk). Fine-grained PATs actually expire. If you want guaranteed rotation, fine-grained is the way.
- The connector is org-scoped, not enterprise-scoped. For multi-org GitHub enterprises (EMU or enterprise accounts with many orgs), add one Draxis integration per org — each with its own PAT.
Troubleshooting
- Test returns "org not found or token lacks access" — most commonly: (1) the fine-grained PAT is pending org approval, (2) the PAT's resource owner isn't the org you're querying, or (3) the org slug in Extra Config is wrong. Check the org's PAT approval queue first.
- HTTP 401 on Test — PAT is wrong or revoked. Generate a new token.
- HTTP 403 with
Resource not accessible by personal access token— the fine-grained PAT is missing a specific permission. The error message usually names the endpoint; match it to the Required Scopes list and add the missing permission. - All GHAS KRIs are 0 but other KRIs work — GHAS isn't enabled on the org (it's a paid feature). Either enable it in Org Settings or uncheck those KRIs.
ghas_repos_no_branch_protectionis high in a small org — the connector counts repos where the default branch has no protection rule, including empty / archived / experimental repos. Archive or exclude those repos, or treat the value as a repo-hygiene metric rather than a security finding.ghas_unsigned_actionsis 0 but you know you use third-party actions — code search excludes private repos unless the authenticated user has access, and it's eventually consistent (newly-added workflows may not be indexed yet). Allow 24h after workflow changes for code search to catch up.rowsSkipped > 0androwsWritten = 0— your tenant hasn't imported any KRIs for this integration yet. Open the integration in Settings → Integrations, tick the KRIs under KRIs to import, and save.- Still stuck? Open a support ticket with the run ID (from Run history) and we'll dig in.