Repository navigation
238 lines (221 loc) · 12.4 KB
/
Copy pathsast-scan-codeql.yml
File metadata and controls
238 lines (221 loc) · 12.4 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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Security Workflows — SAST (CodeQL) reusable workflow.
#
# SCOPE: For most repositories, enable CodeQL via GitHub "Default setup" applied through an org/enterprise Security Configuration (zero YAML, GitHub-managed upgrades).
# Use THIS reusable workflow only when a repo needs path filtering, a non-default query suite, or a centrally-pinned CodeQL action.
# It cannot carry a repo-specific build (build-mode manual is rejected) — a repo needing one wants a standalone advanced workflow.
#
# SCOPE OF ENFORCEMENT: this workflow publishes findings to code scanning and always exits 0. It never fails a pull request.
# Gating is configured outside it, through code-scanning alert rules / branch protection on the consuming repository.
#
# PREREQUISITES for a consuming repository:
# * Public repos: code scanning is free — no GHAS license required.
# * Private/internal repos: GitHub Advanced Security (Code Security) MUST be enabled or CodeQL SARIF upload is rejected.
# * CodeQL "Default setup" MUST NOT cover a language this workflow analyzes — both publish the
# /language:<lang> SARIF category, so an overlap makes the two uploads collide. Default setup covering
# only other languages can coexist with this workflow.
# * A language analyzed with build-mode autobuild needs its toolchain on the runner; build-mode none needs none.
# GitHub-hosted runners ship the common toolchains; self-hosted images may not.
name: SAST (CodeQL)
on:
workflow_call:
inputs:
languages:
description: >-
JSON array of CodeQL languages to analyze; each entry becomes a matrix leg. Entries may be plain strings — '["actions","python"]'
or objects carrying per-language overrides: '[{"language":"go","build-mode":"autobuild"},{"language":"swift","runs-on":"macos-14"},"rust"]'.
Valid languages: actions, c-cpp, csharp, go, java-kotlin, javascript-typescript, python, ruby, rust, swift.
Each language's build mode is resolved automatically, so languages with incompatible requirements (go needs autobuild, rust supports only none) can be analyzed together in a single call.
CodeQL has no shell and CUDA analyzer.
type: string
required: true
runs-on:
description: >-
Default runner label for the analysis legs; override per language with the object form of languages.
CodeQL reaches no NVIDIA-internal service, so the GitHub-hosted default is fine;
swift is analyzed on macOS only and needs a macOS label.
type: string
required: false
default: ubuntu-latest
build-mode:
description: >-
Preferred CodeQL build mode, applied to every language that supports it.
none (default) — no toolchain, scans source directly. autobuild — best-effort compile.
A language that cannot honour this value is resolved to the mode CodeQL does support and the substitution is reported in the job log, so mixed-requirement language sets do not fail.
Set build-mode per language with the object form of languages when you need exact control.
manual is intentionally unsupported (a reusable workflow cannot carry a repo-specific build).
type: string
required: false
default: none
resolve-runs-on:
description: >-
Runner for the short job that resolves and validates the language matrix. Set this only if the repository cannot use GitHub-hosted runners.
type: string
required: false
default: ubuntu-latest
queries:
description: >-
CodeQL query suite(s), comma-separated. Prefix with '+' to append to the default suite (e.g. +security-and-quality).
Default: security-extended.
type: string
required: false
default: security-extended
packs:
description: >-
Optional CodeQL query packs (comma-separated). Packs hosted on GHCR are NOT usable today: this workflow does not declare
packages: read, and a called workflow's token cannot exceed what it declares, so granting the scope in the caller has no effect.
Adding the scope would force every existing caller to grant it too, which is a major version bump.
type: string
required: false
default: ''
config-file:
description: >-
Optional path (in the consuming repo) to a CodeQL config file for path filters, disabled rules, or query-pack selection.
type: string
required: false
default: ''
permissions:
actions: read
contents: read
security-events: write
jobs:
resolve:
name: Resolve language matrix
runs-on: ${{ inputs.resolve-runs-on }}
permissions: {}
outputs:
matrix: ${{ steps.resolve.outputs.matrix }}
steps:
- name: Resolve build mode and runner per language
id: resolve
env:
LANGUAGES: ${{ inputs.languages }}
DEFAULT_BUILD_MODE: ${{ inputs.build-mode }}
DEFAULT_RUNS_ON: ${{ inputs.runs-on }}
run: |
set -euo pipefail
if ! printf '%s' "$LANGUAGES" | jq empty 2>/dev/null; then
echo "::error::languages is not valid JSON. Received: ${LANGUAGES}"
exit 1
fi
# Authoritative per-language build-mode support, per
# https://docs.github.com/en/code-security/reference/code-scanning/codeql/build-options-for-compiled-languages
# "preferred" mirrors what GitHub Default setup selects for that language.
support='{
"actions": {"modes":["none"], "preferred":"none"},
"javascript-typescript": {"modes":["none"], "preferred":"none"},
"python": {"modes":["none"], "preferred":"none"},
"ruby": {"modes":["none"], "preferred":"none"},
"c-cpp": {"modes":["none","autobuild"], "preferred":"none"},
"csharp": {"modes":["none","autobuild"], "preferred":"none"},
"java-kotlin": {"modes":["none","autobuild"], "preferred":"none"},
"rust": {"modes":["none"], "preferred":"none"},
"go": {"modes":["autobuild"], "preferred":"autobuild"},
"swift": {"modes":["autobuild"], "preferred":"autobuild"}
}'
result=$(jq -c -n \
--argjson raw "$LANGUAGES" \
--argjson support "$support" \
--arg defaultMode "$DEFAULT_BUILD_MODE" \
--arg defaultRunsOn "$DEFAULT_RUNS_ON" '
def entries:
map(
if type == "string" then {language: ., mode: null, runner: null, unknown: []}
elif type == "object" then {language: .language, mode: .["build-mode"], runner: .["runs-on"],
unknown: (keys - ["language", "build-mode", "runs-on"])}
else {language: null, mode: null, runner: null, unknown: []}
end
);
def resolve($support; $defaultMode; $defaultRunsOn):
map(
. as $e
| ($support[$e.language // ""] // null) as $s
| if ($e.unknown | length) > 0 then
{ leg: null, note: null,
err: "\($e.language // "entry"): unrecognized key(s) \($e.unknown | join(", ")) — an object entry accepts only language, build-mode and runs-on. A misspelled key would otherwise be ignored silently." }
elif $s == null then
{ leg: null, note: null,
err: "unsupported language \($e.language // "null" | tojson). Valid values: \($support | keys | sort | join(", "))" }
elif $e.mode == "manual" then
{ leg: null, note: null,
err: "\($e.language): build-mode manual is not supported by this reusable workflow (it needs repo-specific build steps). Use a standalone advanced CodeQL workflow." }
elif ($e.mode != null and ($s.modes | index($e.mode)) == null) then
{ leg: null, note: null,
err: "\($e.language): CodeQL does not support build-mode \($e.mode | tojson) for this language. Supported: \($s.modes | join(", "))." }
else
( if $e.mode != null then $e.mode
elif ($s.modes | index($defaultMode)) != null then $defaultMode
else $s.preferred end ) as $mode
| ($e.runner // $defaultRunsOn) as $runner
| { leg: { language: $e.language, buildMode: $mode, runsOn: $runner },
note: ( if $e.mode == null and $mode != $defaultMode
then "\($e.language): build-mode \($defaultMode | tojson) is not available for this language; resolved to \($mode | tojson)."
else null end ),
err: null }
end
);
if ($raw | type) != "array" then
{ matrix: null, notices: [], errors: ["languages must be a JSON array, for example [\"actions\",\"python\"]"] }
elif ($raw | length) == 0 then
{ matrix: null, notices: [], errors: ["languages is empty — pass at least one CodeQL language"] }
else
($raw | entries) as $es
| ($es | resolve($support; $defaultMode; $defaultRunsOn)) as $r
| ( $es | map(.language) | group_by(.) | map(select(length > 1) | .[0])
| map("duplicate language \(. | tojson) — each language may appear once, or the legs race publishing the same SARIF category") ) as $dupes
| ( $r | map(select(.leg != null and .leg.language == "swift" and ((.leg.runsOn | ascii_downcase | test("mac")) | not)))
| map("swift is analyzed on macOS only; runs-on \(.leg.runsOn | tojson) does not look like a macOS runner") ) as $swift
| { matrix: { include: ($r | map(.leg) | map(select(. != null))) },
notices: (($r | map(.note) | map(select(. != null))) + $swift),
errors: (($r | map(.err) | map(select(. != null))) + $dupes) }
end
')
while IFS= read -r note; do
[ -n "$note" ] && echo "::notice::${note}"
done < <(printf '%s' "$result" | jq -r '.notices[]?')
errors=$(printf '%s' "$result" | jq -r '.errors[]?')
if [ -n "$errors" ]; then
while IFS= read -r err; do
echo "::error::${err}"
done <<< "$errors"
exit 1
fi
matrix=$(printf '%s' "$result" | jq -c '.matrix')
echo "matrix=${matrix}" >> "$GITHUB_OUTPUT"
{
echo "### CodeQL language matrix"
echo
echo "| Language | Build mode | Runner |"
echo "|---|---|---|"
printf '%s' "$matrix" | jq -r '.include[] | "| `\(.language)` | `\(.buildMode)` | `\(.runsOn)` |"'
} >> "$GITHUB_STEP_SUMMARY"
analyze:
name: CodeQL (${{ matrix.language }})
needs: resolve
runs-on: ${{ matrix.runsOn }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.resolve.outputs.matrix) }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.buildMode }}
queries: ${{ inputs.queries }}
packs: ${{ inputs.packs }}
config-file: ${{ inputs.config-file }}
# Runs only when this leg resolved to autobuild; a no-op for none.
- name: Autobuild
if: ${{ matrix.buildMode == 'autobuild' }}
uses: github/codeql-action/autobuild@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
with:
category: "/language:${{ matrix.language }}"