Repository navigation
136 lines (132 loc) · 5.67 KB
/
Copy pathcodeql.yml
File metadata and controls
136 lines (132 loc) · 5.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# agent-pmo:795a9c2
name: CodeQL
# CodeQL static security analysis ([GITHUB-CODE-SCANNING]).
#
# SEPARATE from ci.yml on purpose: CodeQL feeds GitHub code-scanning alerts and
# needs `security-events: write` + a weekly schedule, while ci.yml owns
# lint/test/build. It does NOT overlap with `make lint` (style/correctness) or
# dependency-review (vulnerable packages) — CodeQL finds vulnerable CODE.
#
# Matrix = (languages in this repo) ∩ (CodeQL-supported, checked at skill-run
# time): csharp, javascript-typescript (the LQL VS Code extension + Eleventy
# sites), rust (the LQL LSP), plus `actions` (always). F# is NOT CodeQL-supported
# and is therefore absent. Action SHAs are kept current by the github-actions
# Dependabot group ([GITHUB-DEPENDABOT]).
on:
pull_request:
branches: [main]
schedule:
# Weekly, so newly-published CodeQL queries re-scan even without a push.
- cron: "27 4 * * 1"
# release.yml calls this with gate=true on every v* tag to scan the exact
# released SHA with the current query set and BLOCK publishing on any
# High/Critical finding — a HARD gate, not advice. [GITHUB-CODE-SCANNING]
workflow_call:
inputs:
gate:
description: >-
When true (release calls), fail the job on any High/Critical finding so
the calling release workflow cannot publish. PR/weekly runs leave this
false and stay advisory (the PR check-failure threshold governs merges).
type: boolean
default: false
permissions:
contents: read
concurrency:
group: codeql-${{ github.ref }}
cancel-in-progress: true
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ubuntu-latest
timeout-minutes: 30
# Code scanning (SARIF upload) requires GitHub Advanced Security on PRIVATE
# repos. Gating on public visibility lets a private repo skip cleanly and
# self-enable the moment it is made public.
if: github.event.repository.visibility == 'public'
permissions:
security-events: write
actions: read
contents: read
strategy:
fail-fast: false
matrix:
# build-mode: none suits interpreted langs + rust + csharp (avoids
# recompiling what ci already builds).
include:
- language: actions # scans the workflow files themselves
build-mode: none
- language: csharp
build-mode: none
- language: javascript-typescript
build-mode: none
- language: rust
build-mode: none
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Initialize CodeQL
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
queries: security-extended
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
with:
category: "/language:${{ matrix.language }}"
# Drop SARIF on disk so the gate step can read it. `upload` stays on
# (default) so alerts still post to code scanning on every run.
output: sarif-results
# Release gate. `security-severity` is the 0-10 CVSS-style score CodeQL
# attaches to each security rule; >= 7.0 == High or Critical. Enforced ONLY
# on gated (release) calls. FAILS CLOSED: missing/malformed SARIF errors,
# never passes. [GITHUB-CODE-SCANNING]
- name: Enforce no high/critical findings (release gate)
if: inputs.gate
shell: bash
env:
SARIF_DIR: sarif-results
SEVERITY_THRESHOLD: '7.0'
run: |-
set -euo pipefail
shopt -s nullglob
# Fail closed: no SARIF means we cannot prove the code is clean.
sarifs=( "${SARIF_DIR}"/*.sarif )
if [ "${#sarifs[@]}" -eq 0 ]; then
echo "::error::CodeQL gate: no SARIF in ${SARIF_DIR}; cannot verify findings — failing closed."
exit 1
fi
offenders=0
for sarif in "${sarifs[@]}"; do
if ! jq -e '.runs' "${sarif}" >/dev/null 2>&1; then
echo "::error::CodeQL gate: ${sarif} is not valid SARIF (no .runs) — failing closed."
exit 1
fi
jq -r --arg f "${sarif##*/}" '
([ (.runs[].tool.driver.rules // [])[],
(.runs[].tool.extensions[]?.rules // [])[] ]) as $rules
| "CodeQL gate: \($f): results=\([.runs[].results[]?]|length) severity_rules=\([$rules[]|select(.properties["security-severity"])]|length)"
' "${sarif}"
hits="$(jq -r --argjson t "${SEVERITY_THRESHOLD}" '
.runs[]
| ( [ (.tool.driver.rules // [])[],
(.tool.extensions[]?.rules // [])[] ]
| map({ key: .id,
value: ((.properties["security-severity"] // "0") | tonumber) })
| from_entries
) as $severity
| .results[]
| select( ($severity[.ruleId] // 0) >= $t )
| .ruleId
' "${sarif}" | sort | uniq -c | sort -rn)"
if [ -n "${hits}" ]; then
echo "::error::High/critical CodeQL findings in ${sarif}:"
echo "${hits}"
offenders=$((offenders + 1))
fi
done
if [ "${offenders}" -gt 0 ]; then
echo "::error::CodeQL gate failed — release blocked. Fix or dismiss-and-exclude the findings, then re-tag."
exit 1
fi
echo "CodeQL gate passed: nothing at or above severity ${SEVERITY_THRESHOLD}."