Files
certctl/.github/codeql/qlpack.yml
T
shankar0123 1122f5a097 ci(codeql): teach analyzer about ValidateSafeURL SSRF barrier
Closes CodeQL alert #23 (go/request-forgery, Critical) at the
structural level — by telling CodeQL what the runtime code already
does — rather than via per-line `// codeql[...]` suppressions.

Background. internal/service/scep_probe.go:232 calls client.Do(req)
where the request URL is built from operator-supplied input. The
runtime defense is two-layer:

  1. validation.ValidateSafeURL(rawURL) at scep_probe.go:86 rejects
     non-http(s) schemes, empty hosts, literal-IP hosts in reserved
     ranges (loopback, link-local incl. cloud metadata
     169.254.169.254, multicast, broadcast, unspecified, IPv6
     link-local), and DNS names whose A/AAAA resolution returns any
     reserved IP. RFC 1918 is intentionally NOT blocked — see
     internal/validation/ssrf.go:17-21 for the design rationale.

  2. validation.SafeHTTPDialContext on the http.Transport (line 254)
     re-resolves at dial time, applies the same reserved-IP set, and
     pins the dial to a literal non-reserved IP — defeating DNS
     rebinding between validate and dial.

CodeQL's go/request-forgery query is a syntactic taint-tracking rule
with no built-in knowledge of either validator, so it reports the
finding even though the runtime is correctly defended.

The fix. Add a Models-as-Data (MaD) extension at .github/codeql/
declaring ValidateSafeURL as a request-forgery barrier. The barrier
applies to Argument[0] (the URL parameter), which means the analyzer
treats every URL flowing through ValidateSafeURL as sanitized for the
request-forgery taint set. After this lands:

  - Alert #23 dismisses at scep_probe.go:232.
  - The same model applies to the second site of this exact shape —
    webhook notifier's outbound client.Do (internal/connector/
    notifier/webhook/webhook.go) — without per-line annotations.
  - Future code that flows operator URLs through ValidateSafeURL
    inherits the barrier automatically.

This is the structural fix, not a band-aid:

  - Band-aid (rejected): `// codeql[go/request-forgery]` suppression
    on line 232. Suppresses one alert; doesn't teach the analyzer.
    Webhook notifier would need the same comment when its sibling
    rule landing fires.

  - Structural (this change): teach CodeQL via models-as-data, in
    config checked into the repo, that lives next to the workflow
    that uses it. The validators ARE sanitizers in the runtime —
    this PR makes the analyzer's model match reality.

Files:

  - .github/codeql/qlpack.yml — local model pack manifest, declares
    extensionTargets: codeql/go-all: '*'

  - .github/codeql/models/request-forgery-sanitizers.model.yml —
    barrierModel row for validation.ValidateSafeURL Argument[0] /
    request-forgery taint kind / manual provenance

  - .github/codeql/codeql-config.yml — references the local pack +
    keeps security-and-quality query suite scope

  - .github/workflows/codeql.yml — Initialize CodeQL step picks up
    config-file: ./.github/codeql/codeql-config.yml. The existing
    `queries: security-and-quality` line stays so even if the config
    file fails to load, the suite scope is preserved.

  - docs/architecture.md::Input Validation and SSRF Protection —
    extended to name the egress validators (ValidateSafeURL +
    SafeHTTPDialContext) and the call sites (SCEP probe + webhook
    notifier). Closes the docs gap surfaced during the audit; the
    egress threat-model previously lived only in source comments.

Requires CodeQL CLI ≥ 2.25.2 for the barrierModel extensible
predicate (Go MaD support added 2026-04-21). github/codeql-action@v3
ships a recent enough CLI by default; if a future analysis fails
with "unknown extensible predicate barrierModel", the action's CLI
has regressed below 2.25.2 — pin a newer action version rather than
reverting this pack. Documented inline in qlpack.yml.

References:
  - https://codeql.github.com/docs/codeql-language-guides/customizing-library-models-for-go/
  - https://github.blog/changelog/2026-04-21-codeql-now-supports-sanitizers-and-validators-in-models-as-data/
2026-05-01 00:28:26 +00:00

43 lines
2.2 KiB
YAML

# certctl CodeQL model pack — extends the standard Go queries with project-
# specific data-flow knowledge (sanitizers, sinks, summaries).
#
# Why this exists: CodeQL's standard `go/request-forgery` query is a syntactic
# taint-tracking rule. It traces operator-supplied URLs into HTTP egress sinks
# (`http.Client.Do`) and reports — but it has no built-in knowledge of
# certctl's `internal/validation.ValidateSafeURL` SSRF guard. The validator
# IS a sanitizer (rejects loopback, link-local incl. cloud metadata
# 169.254.169.254, multicast, broadcast, unspecified, IPv6 link-local;
# rejects DNS names whose A/AAAA records resolve into any of those ranges)
# but CodeQL doesn't know that, so the analyzer reports a finding the
# runtime defense already mitigates.
#
# This pack uses Models-as-Data (MaD) extensions to declare the validator as
# a barrier for the request-forgery query. After this pack is loaded:
# - The alert at internal/service/scep_probe.go:232 (CodeQL #23) is
# dismissed at source, not via per-line `// codeql[...]` suppression.
# - The same model applies to the second site of this shape — webhook
# notifier's outbound `client.Do` (internal/connector/notifier/webhook/
# webhook.go) — without per-line annotations.
# - Future code that flows operator URLs through ValidateSafeURL gets the
# same treatment automatically.
#
# Loading: codeql-config.yml's `packs:` field references this pack by its
# `name` below. The `extensionTargets:` map declares which upstream pack the
# extension data plugs into (codeql/go-all is the Go standard library pack).
# The `dataExtensions:` glob matches the .model.yml files in models/.
#
# MaD `barrierModel` extension was added for Go in CodeQL 2.25.2 (2026-04-21).
# `github/codeql-action@v3` (pinned in .github/workflows/codeql.yml) pulls a
# CLI version >= 2.25.2 by default. If a future analysis fails with
# "unknown extensible predicate barrierModel", the action's CLI version has
# regressed below 2.25.2 — pin a newer action version rather than reverting
# this pack.
name: shankar0123/certctl-codeql-models
version: 0.0.1
library: true
extensionTargets:
codeql/go-all: '*'
dataExtensions:
- models/*.model.yml