DevSecOps

GitHub Security Features: Code Scanning, Secret Scanning, and Dependabot

A comprehensive guide to GitHub's built-in security features including Advanced Security, CodeQL code scanning, Dependabot version updates and security alerts, and Secret Scanning with push protection to prevent credential exposure.

February 1, 20268 min readShipSafer Team

GitHub has invested heavily in security features that are either free for public repositories or included in GitHub Advanced Security (GHAS) for private repositories. Most teams that use GitHub have access to powerful security tooling they have not fully configured. This guide covers the four main security pillars: Dependabot, Code Scanning with CodeQL, Secret Scanning, and Push Protection — what they detect, how to configure them, and how to operationalize the findings.

Dependabot: Dependency Security and Version Management

Dependabot is the most immediately actionable security feature on GitHub. It continuously monitors your dependency manifest files and alerts you when a dependency has a known vulnerability (security alerts) or when newer versions are available (version updates).

Dependabot Security Alerts

Security alerts are automatic for all repositories once enabled. GitHub's Advisory Database (which aggregates from NVD, OSV, and GitHub's own research) is used to match your package.json, Cargo.toml, requirements.txt, Gemfile.lock, pom.xml, and other manifest files against known vulnerabilities.

Enable in Settings > Security > Dependabot alerts.

Configuration file for security updates (.github/dependabot.yml):

version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"
    open-pull-requests-limit: 10
    reviewers:
      - "your-security-team"
    labels:
      - "security"
      - "dependencies"
    ignore:
      # Ignore major version bumps for specific packages
      - dependency-name: "react"
        update-types: ["version-update:semver-major"]

  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
    labels:
      - "docker"
      - "dependencies"

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

Operationalizing Dependabot: Raw Dependabot alerts require triage. Not every vulnerable dependency is exploitable in your context — the vulnerable code path may not be reached by your application. Establish a workflow:

  1. Critical/High severity: Treat as P2 issues requiring a patch within a week. Auto-merge if your test suite passes.
  2. Medium severity: Patch within a sprint.
  3. Low severity: Batch with regular dependency updates.

Auto-merge for Dependabot PRs (requires branch protection + passing CI):

# .github/workflows/dependabot-auto-merge.yml
name: Dependabot auto-merge

on: pull_request

permissions:
  contents: write
  pull-requests: write

jobs:
  dependabot:
    runs-on: ubuntu-latest
    if: github.actor == 'dependabot[bot]'
    steps:
      - name: Fetch Dependabot metadata
        id: metadata
        uses: dependabot/fetch-metadata@v2
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"

      - name: Auto-merge patch and minor updates
        if: |
          steps.metadata.outputs.update-type == 'version-update:semver-patch' ||
          steps.metadata.outputs.update-type == 'version-update:semver-minor'
        run: gh pr merge --auto --squash "$PR_URL"
        env:
          PR_URL: ${{ github.event.pull_request.html_url }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

This auto-merges patch and minor updates after CI passes, reducing the operational overhead of dependency maintenance.

Code Scanning with CodeQL

GitHub Advanced Security includes CodeQL — GitHub's semantic code analysis engine. CodeQL treats code as data, building a queryable database of the program's structure and data flows. It can find complex vulnerabilities that pattern-matching tools miss, including multi-step injection flaws where attacker-controlled data flows through several functions before reaching a dangerous operation.

Enabling Code Scanning:

In Settings > Security > Code scanning, click "Set up code scanning" and choose the default setup (recommended) or advanced setup.

Default setup automatically configures CodeQL for the languages detected in your repository. For more control, use the advanced setup:

# .github/workflows/codeql.yml
name: "CodeQL Security Analysis"

on:
  push:
    branches: [ "main", "develop" ]
  pull_request:
    branches: [ "main" ]
  schedule:
    - cron: '0 2 * * 1'  # Weekly on Monday at 2 AM

jobs:
  analyze:
    name: Analyze (${{ matrix.language }})
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write  # Required to upload SARIF results

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

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: ${{ matrix.language }}
        queries: security-extended  # More thorough than default 'security-and-quality'

    - name: Autobuild
      uses: github/codeql-action/autobuild@v3

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

Query suites:

  • security-and-quality: The default — good balance of precision and recall
  • security-extended: Broader coverage, more false positives, recommended for security-focused scans
  • security-experimental: Cutting-edge queries still under development

What CodeQL finds in JavaScript/TypeScript:

  • SQL injection via string concatenation in database queries
  • NoSQL injection via unsanitized user input in MongoDB/Mongoose queries
  • XSS via unsanitized rendering in React (dangerouslySetInnerHTML)
  • Path traversal in file system operations
  • ReDoS (Regular Expression Denial of Service)
  • Server-Side Request Forgery (SSRF)
  • Prototype pollution
  • Insecure deserialization
  • Open redirects

Triaging CodeQL findings: CodeQL results appear in Security > Code scanning alerts. Each alert includes:

  • The vulnerable line of code
  • The complete data flow from source to sink (showing how attacker-controlled data reaches the dangerous operation)
  • Recommended remediation

For each finding:

  1. Verify the finding is not a false positive by tracing the data flow manually
  2. Check if the identified source is actually user-controllable in your application
  3. If confirmed real: create an issue, assign it, and remediate before merging
  4. If a false positive: dismiss it with a reason — this trains the system and reduces alert fatigue

Custom CodeQL queries: For application-specific security checks (e.g., ensuring certain functions are always called with a specific sanitizer), you can write custom CodeQL queries in QL:

// custom-queries/no-direct-db-queries.ql
/**
 * @name Direct database query without parameterization
 * @description Finds uses of string concatenation in MongoDB queries
 * @kind problem
 * @severity error
 */
import javascript

from MethodCallExpr call
where
  call.getMethodName() = "find" and
  call.getArgument(0).(StringConcatenation).getAnOperand() instanceof Expr
select call, "Potential NoSQL injection: use query parameters instead of string concatenation"

Secret Scanning

GitHub Secret Scanning automatically scans every repository for secrets — API keys, tokens, passwords, and other credentials that should not be in source code. It uses pattern matching against an extensive list of known credential formats from 200+ service providers including AWS, Google, GitHub, Stripe, Twilio, Slack, and many others.

Enabling Secret Scanning: Settings > Security > Secret scanning > Enable.

For public repositories, Secret Scanning has been active by default since 2022. For private repositories, it requires GitHub Advanced Security.

What it detects: Secret Scanning has partner patterns for specific providers (GitHub tokens, AWS access keys, Google API keys, Stripe secret keys, etc.) and also performs generic high-entropy string detection for potential passwords and tokens.

Each detection generates a secret scanning alert in Security > Secret scanning alerts. The alert shows:

  • The type of secret detected
  • The file and line number
  • Whether the secret has been verified as valid by the provider (partner providers perform live validation)

Responding to a secret scanning alert:

If the secret is valid (confirmed active):

  1. Immediately rotate/revoke the secret in the provider's dashboard — do not wait
  2. Investigate the exposure window: When was the secret first committed? Who had access to the repository?
  3. Review provider access logs for the exposed credential for unauthorized usage during the exposure window
  4. Remove the secret from git history using git filter-repo or BFG Repo Cleaner
  5. Assess impact: What could the exposed credential access? Was it accessed?
# Remove a secret from git history using git-filter-repo
pip install git-filter-repo
git filter-repo --path-regex '.*' --replace-text <(echo "ACTUAL_SECRET_VALUE==>REDACTED")

# Or remove a specific file that contained the secret
git filter-repo --invert-paths --path 'path/to/file/with/secret.env'

Push Protection

Push Protection is the most impactful GitHub security feature for preventing secrets in repositories. It blocks pushes that contain detected secrets before they reach the remote repository, providing a pre-commit enforcement layer.

Enable Push Protection: Settings > Security > Secret scanning > Push protection > Enable.

When a developer attempts to push a commit containing a detected secret, they receive this error:

remote: error: GH013: Repository rule violations found for refs/heads/main.
remote:
remote: - GITHUB PUSH PROTECTION
remote:   —————————————————————————————————————
remote:    Resolve the following secrets before pushing:
remote:
remote:   (?) To push, remove secret from commit(s) or bypass the protection.
remote:
remote:    —— Stripe Secret Key ——————————————————
remote:     locations:
remote:      - commit: abc123def456
remote:        path: src/config.ts:12
remote:
remote:     To skip push protection, use the `--push-option` git option:
remote:       git push --push-option=allow_secret_scanning_bypass

Configuring bypass permissions: You can configure who is allowed to bypass Push Protection when there is a legitimate reason (e.g., pushing a test fixture with an intentionally fake-looking but non-real secret):

In Settings > Security > Secret scanning > Push protection > Who can bypass push protection for repository secrets.

All bypasses are logged in the audit log, providing visibility into when and why the protection was overridden.

Custom patterns: If your organization uses internally generated tokens or secrets with specific formats (e.g., internal service API keys with a company-specific prefix), you can add custom secret scanning patterns:

Settings > Security > Secret scanning > Custom patterns > New pattern.

# Custom pattern for internal API keys with format: MYCO-[a-zA-Z0-9]{32}
Pattern: MYCO-[a-zA-Z0-9]{32}
Test string: MYCO-aBcDeFgHiJkLmNoPqRsTuVwXyZ123456

Integrating Security Findings into Your Workflow

The most important configuration is ensuring security findings block deployments when they are critical:

# .github/workflows/security-gate.yml
- name: Check for critical code scanning alerts
  run: |
    CRITICAL_ALERTS=$(gh api \
      /repos/${{ github.repository }}/code-scanning/alerts \
      --jq '[.[] | select(.state=="open" and .rule.severity=="critical")] | length')

    if [ "$CRITICAL_ALERTS" -gt "0" ]; then
      echo "::error::$CRITICAL_ALERTS critical code scanning alerts found"
      exit 1
    fi
  env:
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

With CodeQL scanning PRs, Dependabot patching dependencies, Secret Scanning preventing credential exposure, and Push Protection blocking secrets at commit time, you have a comprehensive security layer integrated directly into your development workflow — without requiring developers to change their tools or workflow significantly.

GitHub
code scanning
CodeQL
Dependabot
secret scanning
GitHub Advanced Security
DevSecOps
SAST

Check Your Security Score — Free

See exactly how your domain scores on DMARC, TLS, HTTP headers, and 25+ other automated security checks in under 60 seconds.