name: CodeQL # Public-facing SAST baseline that complements the existing security-deep-scan # workflow (gosec, osv-scanner, trivy, ZAP, semgrep, schemathesis, nuclei, # testssl) with cross-file Go and JavaScript dataflow analysis. Results land # in the repository's Security → Code scanning tab as a public signal — any # operator/security team auditing certctl can see the scan history and # triage state without asking. # # Why CodeQL in addition to gosec: # - gosec is single-file pattern matching (catches obvious issues like # `os/exec.Command(userInput)`); CodeQL does interprocedural taint # tracking (catches the same issue when the userInput is laundered # through several function calls or struct fields). # - GitHub-native; no third-party SaaS license gate (works for BSL 1.1 # and other source-available licenses, unlike Aikido / Snyk / SonarCloud # free tiers which require OSI-approved licenses). # - SARIF results auto-deduplicate and persist on PRs, so reviewers see # "this PR introduces N new findings" rather than re-running ad hoc. # # Findings that are intentional (e.g., the SSH connector's # InsecureIgnoreHostKey, ACME DNS solver's intentional shell-out to operator- # supplied scripts) get suppressed via inline `// codeql[]` # comments OR via a `.github/codeql/codeql-config.yml` query-pack tweak — # document the rationale in the same commit that adds the suppression so # the public scan-tab readers see the threat-model justification. on: push: branches: [master] pull_request: branches: [master] schedule: # Weekly Sunday 06:00 UTC, in addition to push/PR coverage. Catches # rule-pack updates from CodeQL upstream (their Go/JS rulesets ship # new queries on a roughly-monthly cadence). - cron: '0 6 * * 0' permissions: contents: read security-events: write # SARIF upload to GitHub code scanning actions: read jobs: analyze: name: Analyze (${{ matrix.language }}) runs-on: ubuntu-latest timeout-minutes: 30 strategy: fail-fast: false matrix: language: [go, javascript-typescript] steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Go if: matrix.language == 'go' uses: actions/setup-go@v5 with: # Match ci.yml + release.yml + security-deep-scan.yml. go-version: '1.25.9' - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # Use the security-and-quality query suite — security finds plus # maintainability/correctness issues that the smaller security-extended # suite skips. Comparable scope to what Aikido / SonarCloud run. queries: security-and-quality # Custom config that pulls in the local model pack at # .github/codeql/certctl-models/. The pack declares # internal/validation.ValidateSafeURL as a request-forgery barrier # via Models-as-Data, dismissing the alert that fires at every # call site using the validator (scep_probe.go, webhook.go) # without per-line `// codeql[...]` suppressions. See # .github/codeql/certctl-models/qlpack.yml for the full motivation. # Requires CodeQL CLI ≥ 2.25.2 for the barrierModel extension; # codeql-action@v3 ships a recent enough CLI by default. config-file: ./.github/codeql/codeql-config.yml # Tells the CodeQL CLI's pack resolver where to find unpublished # local packs. The pack referenced by name in codeql-config.yml's # `packs:` directive (shankar0123/certctl-models) is resolved # against this path. Without it, the name would fail to resolve # and the pack would silently not load — that's exactly what # happened to commits d8026d5 + 4bb7a74 (alert #23 stayed open # across two CodeQL runs because the pack never made it into the # analysis). Verified pattern: github/vscode-codeql. additional-packs: .github/codeql - name: Autobuild uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: category: "/language:${{ matrix.language }}" # SARIF upload is implicit (and is what populates the Security tab).