Hamburger Cross Icon
CodeQL Collector - Lunar Collector

CodeQL Collector

Collector Beta Security

Detects GitHub CodeQL security scanning via GitHub Code Scanning check-runs or CLI integration in CI pipelines. Writes to normalized SAST Component JSON paths, enabling tool-agnostic SAST policies.

Add codeql to your lunar-config.yml:
uses: github://earthly/lunar-lib/collectors/codeql@v1.0.5

What This Integration Collects

This integration includes 4 collectors that gather metadata from your systems.

Collector code

github-app

Detects CodeQL scans on pull requests by querying GitHub check-runs API for the github-advanced-security app. Waits for scan completion and captures check status, conclusion, and URLs.

codeql github code scanning sast code analysis security
Book a demo
Collector code

running-in-prs

Proves CodeQL is running on PRs by querying Lunar Hub for CodeQL data from recent PRs. Used on the default branch to provide compliance proof that PR scanning is happening.

codeql pr scanning compliance proof default branch
Book a demo
Collector

cicd

Detects CodeQL CLI or legacy codeql-runner executions in CI pipelines. Captures command metadata and, when the SARIF output file is available on disk (from codeql database analyze/interpret-results), collects the raw SARIF and normalizes findings into .sast.findings and .sast.issues.

codeql cli codeql-runner ci integration sast code scanning
Book a demo
Collector after-json

monorepo-fanout

Redistributes a monorepo's repo-wide CodeQL findings to its subdirectory components, so each one gets the findings under its own paths instead of reading as unscanned.

ONLY for monorepos, and target it at the repository ROOT — the component whose name is the repo itself. Point it at anything else and it does useless work on every commit: a component with no subcomponents has nothing to fan out. Scope it with on: ["component:<repo>"], not a broad domain selector.

Enable alongside cicd on that root: cicd writes the findings, this distributes them. A finding reaches a subcomponent when its file matches that subcomponent's paths: (plus the implicit "/*"), the same rule the Hub uses for change detection. .sast.native stays on the root; subcomponents get the severity counts their policies read plus provenance under .sast.source.fanout. One that already has its own SAST scan is never overwritten.

Requires Hub v3.3.0+. The root also needs at least one policy — an after-json collector is dispatched by the doneness gate, which is only consulted while bundling a component's policies.

monorepo sast fan-out after-json subcomponent cross-component
Book a demo

How Collectors Fit into Lunar

Lunar watches your code and CI/CD systems to collect SDLC data from config files, test results, IaC, deployment configurations, security scans, and more.

Collectors are the automatic data-gathering layer. They extract structured metadata from your repositories and pipelines, feeding it into Lunar's centralized database where guardrails evaluate it to enforce your engineering standards.

Learn How Lunar Works
1
Collectors Gather Data This Integration
Triggered by code changes or CI pipelines, collectors extract metadata from config files, tool outputs, test results, and scans
2
{ } Centralized as JSON
All data merged into each component's unified metadata document
3
Guardrails Enforce Standards
Real-time feedback in PRs and AI workflows

Example Collected Data

This collector writes structured metadata to the Component JSON. Here's an example of the data it produces:

{ } component.json Component JSON
{
  "sast": {
    "running_in_prs": true,
    "source": {
      "tool": "codeql",
      "version": "2.16.0",
      "integration": "ci"
    },
    "findings": {
      "critical": 0,
      "high": 1,
      "medium": 3,
      "low": 0,
      "total": 4
    },
    "issues": [
      {
        "severity": "high",
        "rule": "go/sql-injection",
        "file": "db/query.go",
        "line": 42,
        "message": "Unsanitized input used in SQL query"
      }
    ],
    "summary": {
      "has_critical": false,
      "has_high": true
    },
    "native": {
      "codeql": {
        "github_app": {
          "id": 12345,
          "name": "CodeQL",
          "status": "completed",
          "conclusion": "success"
        },
        "cicd": {
          "cmds": [
            {"cmd": "codeql database interpret-results --format=sarif-latest --output=../results/go.sarif", "version": "2.16.0"}
          ]
        },
        "sarif": {}
      }
    }
  }
}

Configuration

Configure this collector in your lunar-config.yml.

Inputs

Input Required Default Description
monorepo_fanout_include_issues Optional false Also write the per-finding array (`.sast.issues`) to each subcomponent, sliced to the findings that component owns. Off by default: `policies/sast` reads `.sast.summary.has_<sev>` and `.sast.findings.<sev>`, and `max-total` reads `.sast.findings.total` — none of them read `.sast.issues`, so shipping it multiplies write size by findings x subcomponents for data no guardrail evaluates. Turn it on if you want the per-finding detail visible on each service in the UI.

Documentation

View on GitHub

CodeQL Collector

Detects GitHub CodeQL security scans and collects scan metadata from GitHub Code Scanning or CLI integrations.

Overview

This collector detects CodeQL static analysis via GitHub's Code Scanning integration or CLI usage in CI pipelines. CodeQL is GitHub's semantic code analysis engine — unlike pattern-matching tools, it compiles source code into a relational database and queries it for vulnerabilities using inter-procedural data flow analysis.

All data is written to the .sast category, enabling tool-agnostic SAST policies that work across CodeQL, Semgrep, Snyk Code, and other SAST tools.

Collected Data

This collector writes to the following Component JSON paths:

Path Type Description
.sast.source object Source metadata (tool, integration, optional version)
.sast.findings object Severity counts: critical, high, medium, low, total
.sast.issues[] array Individual findings with severity, rule, file, line, message
.sast.summary object has_critical, has_high booleans
.sast.native.codeql.github_app object Raw GitHub Code Scanning check-run data
.sast.native.codeql.cicd object CodeQL CLI invocations detected in CI
.sast.native.codeql.sarif object Raw SARIF output from CodeQL analysis (when available)
.sast.running_in_prs boolean Compliance proof that PRs are being scanned
.sast.source.fanout object (monorepo-fanout, on each subcomponent) Provenance — root component, root SHA, path patterns used, matched count, fingerprint
.sast.native.monorepo_fanout object (monorepo-fanout, on the ROOT only) Per-target written/skipped breadcrumb

Collectors

Collector Hook Type Description
github-app code (PRs only) Detects CodeQL via GitHub Code Scanning check-runs
running-in-prs code (default branch) Proves CodeQL is running on PRs (compliance proof for default branch)
cicd ci-after-command Detects codeql and legacy codeql-runner executions in CI, collects SARIF
monorepo-fanout after-json (.sast.issues) Redistributes a monorepo's repo-wide findings to its subdirectory components by file path. Enable on the repository ROOT alongside cicd. Writes each subcomponent's severity counts (.sast.findings/.sast.summary) plus provenance; .sast.native stays on the root. Subcomponents are discovered with a prefix-scoped SQL query, and a finding reaches a subcomponent when its file matches that component's paths: plus the implicit <subdir>/*. A subcomponent with zero matches is still written with total: 0, which turns its SAST policy from "no data" into a pass; one that already has its own SAST scan is never overwritten. Requires Hub v3.3.0+, and the root needs at least one policy or the after-json wave is never dispatched

Installation

Add to your lunar-config.yml:

collectors:
  - uses: github://earthly/lunar-lib/collectors/codeql@main
    on: ["domain:your-domain"]
    secrets:
      GH_TOKEN: ${GH_TOKEN}

The github-app collector requires a GH_TOKEN secret for GitHub API access. CodeQL posts check-runs via the github-advanced-security app. The collector queries the check-runs API, filters by this app slug, and waits for completion.

The cicd collector matches both codeql and codeql-runner (legacy) binary executions in CI. When the traced command is codeql database analyze or codeql database interpret-results with a --output= flag, the collector reads the SARIF file from disk and collects it as raw data plus normalized findings counts and issues.

The running-in-prs collector queries the Lunar Hub database to verify PR scanning. It uses lunar sql connection-string to obtain database credentials. If unavailable, the collector skips silently.

Monorepos

CodeQL analyses a whole repository, and the traced scan resolves to the repository — so in a monorepo the findings land on the repo-root component while the real components are the subdirectories, which all read as "No SAST scanning data found" despite having been scanned. monorepo-fanout closes that gap. Three things have to be true:

  1. the monorepo's root needs a component of its own — it is where the repo-wide scan lands and where the fan-out runs. Target the sub-collector at that root with on: ["component:<repo>"]; subcomponents are then found automatically. Pointing it at a broad domain selector just makes it do useless work on every non-monorepo component;
  2. cicd has to be enabled on that root, since it is what writes the .sast.issues the fan-out reads;
  3. at least one policy must also evaluate the root. An after-json collector is dispatched by the Hub's doneness gate, and that gate is only consulted while bundling a component's policies — so a component that no policy evaluates never fires its after-json collectors at all. Watch the interaction with domains: a policy declared with no on: resolves to domain:other, so putting the root in a dedicated domain (sensible on its own terms) can silently take it out of every fleet policy's scope.
components:
  # The repository root. A scan carrier, not a deployable service — its own
  # domain keeps your service guardrails off it.
  github.com/acme/repo:
    owner: platform@acme.com
    domain: engineering.monorepo-roots
    branch: "main"

  # Subcomponents are unchanged.
  github.com/acme/repo/services/api:
    domain: engineering.services
    paths: ["services/api/*"]

collectors:
  - uses: github://earthly/lunar-lib/collectors/codeql@main
    on: ["component:github.com/acme/repo"]
    include: [cicd, monorepo-fanout]
    secrets:
      GH_TOKEN: ${GH_TOKEN}
    # with:
    #   # Off by default: no SAST policy reads .sast.issues, so the per-finding
    #   # array is not shipped to every subcomponent unless you want the detail
    #   # visible per service.
    #   monorepo_fanout_include_issues: "false"

policies:
  # Point 3 above — without a policy on the root, the fan-out never dispatches.
  - uses: github://earthly/lunar-lib/policies/sast@main
    on: ["component:github.com/acme/repo"]
    include: [executed]

Because the wave is fire-once per (component, sha, pr), re-testing a change needs a fresh commit — re-running CI on the same commit will not re-fire it.

Open Source

This collector is open source and available on GitHub. Contribute improvements, report issues, or fork it for your own use.

View Repository

Ready to Automate Your Standards?

See how Lunar can turn your AGENTS.md, engineering wiki, compliance docs, or postmortem action items into automated guardrails with our 200+ built-in guardrails.

Works with any process
check AI agent rules & prompt files
check Post-mortem action items
check Security & compliance policies
check Testing & quality requirements
Automate Now
Paste your AGENTS.md or manual process doc and get guardrails in minutes
Book a Demo