← All posts
· 5 min read ·
SecurityGitHub ActionsCodeQLDevSecOpsCI/CDSAST

GitHub Advanced Security in 2026: Getting Real Value from CodeQL, Secret Scanning, and Dependabot

GitHub Advanced Security has matured significantly. Here's how to configure CodeQL, secret scanning, and Dependabot for signal over noise - and what the April 2026 updates change about the default setup.

GitHub Security tab showing CodeQL and Dependabot findings

GitHub Advanced Security (GHAS) bundles three capabilities: CodeQL for SAST, secret scanning for committed credentials, and Dependabot for vulnerable dependencies. All three are free for public repositories and included in GitHub Enterprise Cloud / Team plans for private ones. The defaults are reasonable, but the defaults are not the optimal configuration. Here’s how to tune each one.

CodeQL: Configuration That Matters

CodeQL’s default setup (the checkbox in repository settings) runs a baseline query suite on every push and pull request. It catches common vulnerabilities but misses many security-extended queries that are off by default because they have higher false positive rates.

The right approach is a custom workflow with the security-extended suite:

name: CodeQL Analysis

on:
    push:
        branches: [main, develop]
    pull_request:
        branches: [main, develop]
    schedule:
        - cron: '0 6 * * 1'  # Weekly full scan on Monday

permissions: {}

jobs:
    analyze:
        name: CodeQL
        runs-on: ubuntu-latest
        permissions:
            contents: read
            security-events: write
            actions: read

        strategy:
            fail-fast: false
            matrix:
                language: [javascript-typescript, python]
                # Add: java-kotlin, csharp, go, ruby, swift, cpp as needed

        steps:
            - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

            - name: Initialize CodeQL
              uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
              with:
                  languages: ${{ matrix.language }}
                  queries: security-extended
                  # For TypeScript projects with compiled output:
                  # config-file: .github/codeql/codeql-config.yml

            - name: Autobuild
              uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18

            - name: Perform CodeQL Analysis
              uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
              with:
                  category: /language:${{ matrix.language }}

For monorepos or projects where autobuild doesn’t work:

# .github/codeql/codeql-config.yml
name: CodeQL Config
paths:
    - src
    - lib
paths-ignore:
    - '**/__tests__/**'
    - '**/*.test.ts'
    - node_modules

Triaging CodeQL Findings

Not every CodeQL finding is a real vulnerability. Common false positives:

“Incomplete URL scheme check” - CodeQL flags any URL scheme comparison that doesn’t include all possible schemes. In practice, if you’re checking for http:// and https:// specifically, that’s often intentional.

“Potentially unsafe external link” - Flags target="_blank" without rel="noopener". This is a real issue in old browsers but mitigated by modern browsers automatically. Worth fixing, but not urgent.

“Log injection” - Flags logging of user-controlled values. Evaluate whether the log destination is accessible to end users. If logs go to CloudWatch, the practical risk is low.

To dismiss a false positive with context (preserves the audit trail):

  1. Click the finding in the Security tab
  2. “Dismiss alert” → select reason → add a note explaining why
  3. The dismissal is recorded in audit logs with your identity and timestamp

Do not dismiss without a note. Future reviewers need to understand why.

Secret Scanning: Push Protection

Secret scanning alerts you after a secret is committed. Push protection alerts you before the commit is accepted. Enable it:

Repository settings → Security → Secret scanning → Push protection

Or via API for an entire organisation:

gh api --method PATCH /orgs/your-org \
    -f secret_scanning_push_protection_enabled_for_new_repositories=true

Push protection checks the push against 200+ known secret patterns. If a match is found, the push is rejected with the specific secret identified.

Custom Patterns

Beyond the 200 built-in patterns, you can define custom patterns for your organisation’s secrets (internal API keys, service tokens, etc.):

# Custom pattern for internal API keys (format: PET-[32 hex chars])
PET-[0-9a-f]{32}

Add custom patterns in GitHub Settings → Security → Secret scanning → Custom patterns.

Handling False Positives

Push protection will occasionally flag test fixtures, example keys, or intentionally non-functional tokens. The correct response:

  1. If it’s a real secret: rotate it before bypassing. Then move it to Secrets Manager / environment variables.
  2. If it’s a test fixture: rename it to be obviously fake (EXAMPLE_API_KEY_NOT_REAL) or store it in .gitignored files.
  3. If it’s a known false positive pattern: add a # secret-scanning-ignore comment on the same line, then request a bypass with justification.

Dependabot: Configuration for Real Projects

The default Dependabot config checks npm, pip, maven, etc. for vulnerable versions. The important additional configuration is grouped updates:

# .github/dependabot.yml
version: 2
updates:
    - package-ecosystem: npm
      directory: /
      schedule:
          interval: weekly
          day: monday
          time: "08:00"
          timezone: Europe/London
      groups:
          minor-patch:
              update-types: [minor, patch]
          major:
              update-types: [major]
      ignore:
          - dependency-name: "react"
            versions: ["18.x"]  # Pin until ready to test React 19
      commit-message:
          prefix: "deps"

    - package-ecosystem: github-actions
      directory: /
      schedule:
          interval: weekly
      groups:
          actions:
              patterns: ["*"]
      commit-message:
          prefix: "ci"

    - package-ecosystem: terraform
      directory: /infra
      schedule:
          interval: weekly
      commit-message:
          prefix: "infra"

Grouped updates reduce PR noise significantly - instead of one PR per package, you get one PR for all minor/patch updates. Review the diff, run tests, merge.

Dependabot Security Updates vs Version Updates

Dependabot has two modes:

  • Security updates: Opens PRs specifically for packages with known CVEs. Enabled by default if vulnerability alerts are on. These should be reviewed urgently - they have a concrete CVE attached.
  • Version updates: Opens PRs to keep dependencies current. These are optional but reduce security debt over time (older versions have more CVEs when new ones are discovered).

Both are useful. Security updates are the priority.

The April 2026 Changes

GitHub updated CodeQL defaults this month:

  1. JavaScript/TypeScript now enables security-extended queries by default in the auto-configuration mode. If you were using default setup, you may see new findings appear - review them before dismissing.

  2. Secret scanning push protection is now enabled by default for new public repositories. Existing repositories need manual enablement.

  3. Dependabot grouped updates are now GA (previously beta). The groups key in dependabot.yml is stable and recommended.

  4. CodeQL query suite pinning: Actions now accept a packs: field for custom CodeQL packs from the GitHub registry, allowing organisation-specific query libraries. Useful for teams with proprietary frameworks.

Making It Useful, Not Noisy

GHAS generates real signal. The failure mode is treating every finding as low priority because there are too many. The antidote:

  1. Start with security-extended for new findings, security-and-quality only if you’re willing to triage the full set
  2. Dismiss false positives with notes, not silently
  3. Set branch protection to require CodeQL passing on PRs to main
  4. Treat Dependabot security update PRs as P1 (24-hour review SLA)
  5. Review custom secret patterns quarterly - your credential formats change

The security tab in GitHub is worth spending 30 minutes in every sprint. Most findings can be addressed in minutes. The ones that can’t deserve a tracked issue.

← All posts