-
Notifications
You must be signed in to change notification settings - Fork 0
191 lines (184 loc) · 7.45 KB
/
security.yml
File metadata and controls
191 lines (184 loc) · 7.45 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
name: Security (OSS-CLI)
# OSS-CLI security stack per RAN-54 AC §3 — replicates codeiq RAN-46 path B
# (Sonar / CodeQL / OWASP-DC excluded by board ruling), language-adapted for
# this single-file PowerShell project.
#
# Six independent jobs — fail-fast off so all signals surface on a single run.
# All actions SHA-pinned per Scorecard `Pinned-Dependencies`. Top-level
# `permissions: read-all` per Scorecard `Token-Permissions`; jobs scope up
# only when needed (gitleaks needs full git history; sbom job uploads).
#
# PowerShell-variant deltas vs the codeiq Java stack:
# - OSV-Scanner job is omitted: snipIT is a single .ps1 with zero external
# dependencies (no npm / Maven / pip lockfile). Trivy filesystem scan
# remains the SCA channel for any future deps; Dependabot covers the
# GitHub Actions ecosystem.
# - Semgrep packs: `p/security-audit` + `p/owasp-top-ten` only. Semgrep
# has no first-party PowerShell pack today; the language-specific gate
# is PSScriptAnalyzer (added below) — codeiq-equivalent of `p/java`.
# - jscpd format set to `powershell`, scoped to the three .ps1 files.
# - Added `psscriptanalyzer` job — language lint per
# `shared/runbooks/engineering-standards.md` (PowerShell variant).
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '21 4 * * 1' # Mondays 04:21 UTC — catch newly-disclosed CVEs
permissions: read-all
jobs:
trivy:
name: Trivy (filesystem + container scan)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: fs
scan-ref: .
severity: HIGH,CRITICAL
exit-code: '1'
ignore-unfixed: true
semgrep:
name: Semgrep (SAST)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.12'
- name: Install semgrep
run: python -m pip install --quiet --upgrade pip semgrep
- name: Run semgrep (security-audit + owasp-top-ten)
# No `p/powershell` pack ships in Semgrep registry — language-level
# findings come from PSScriptAnalyzer below. The two packs run here
# are language-agnostic (path traversal, dangerous deserialization,
# OWASP top-ten patterns) and still flag issues in .ps1 source.
run: |
semgrep scan \
--error \
--config p/security-audit \
--config p/owasp-top-ten \
--severity ERROR \
--metrics off
psscriptanalyzer:
name: PSScriptAnalyzer (Error severity gate)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Install PSScriptAnalyzer
shell: pwsh
run: Install-Module PSScriptAnalyzer -Scope CurrentUser -Force -SkipPublisherCheck
- name: Surface warnings (non-blocking)
shell: pwsh
run: |
Import-Module PSScriptAnalyzer
$w = Invoke-ScriptAnalyzer -Path ./SnipIT.ps1 -Severity Warning
Write-Host "Warning count: $((@($w)).Count)"
$w | Group-Object RuleName | Sort-Object Count -Descending |
Select-Object Count, Name | Format-Table -AutoSize | Out-String | Write-Host
- name: Fail on Error-severity findings
shell: pwsh
run: |
Import-Module PSScriptAnalyzer
$errs = Invoke-ScriptAnalyzer -Path ./SnipIT.ps1 -Severity Error
$count = (@($errs)).Count
Write-Host "Error count: $count"
if ($count -gt 0) {
$errs | Format-Table -AutoSize Severity, RuleName, Line, Message |
Out-String | Write-Host
exit 1
}
gitleaks:
name: Gitleaks (secret scan)
runs-on: ubuntu-latest
permissions:
contents: read
env:
GITLEAKS_VERSION: 8.30.1
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
with:
fetch-depth: 0
# The official `gitleaks/gitleaks-action` requires a paid license for
# GitHub organisations. The underlying gitleaks CLI is MIT-licensed and
# free; install it directly from the upstream release. Using the
# preinstalled `gh` CLI avoids any external `curl`/`wget`.
- name: Install gitleaks
run: |
gh release download "v${GITLEAKS_VERSION}" \
--repo gitleaks/gitleaks \
--pattern "gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
--output gitleaks.tar.gz
tar -xzf gitleaks.tar.gz gitleaks
chmod +x gitleaks
- name: Run gitleaks (full git history)
run: ./gitleaks detect --source . --redact --no-banner --exit-code 1
jscpd:
name: jscpd (duplication < 3% on touched code)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
- run: |
# snipIT is three PowerShell files at repo root:
# - SnipIT.ps1 (production)
# - Test-SnipIT.ps1 (headless tests)
# - Test-SnipIT-Interactive.ps1 (interactive tests)
#
# Production-only scope per AC interpretation (matches codeiq
# convention where tests are excluded from the dup gate). Tests
# share fixture / Assert-* shape by design.
#
# `--min-tokens 100` is calibrated to PowerShell's medium verbosity
# floor (Add-Type/[CmdletBinding] blocks, `param()` headers, P/Invoke
# signatures). The Java floor of 200 over-suppresses; the jscpd
# default of 50 surfaces param-block boilerplate as false-positive
# clones. 100 captures real duplicate logic without flagging the
# 8–15 line `[CmdletBinding()]` + `param(...)` openers many
# functions share.
npx --yes jscpd@4 \
--threshold 3 \
--min-tokens 100 \
--reporters consoleFull \
--format "powershell" \
--ignore "**/Test-SnipIT*.ps1,**/docs/**" \
./SnipIT.ps1
sbom:
name: SBOM (SPDX + CycloneDX)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Generate SPDX SBOM
uses: anchore/sbom-action@fc46e51fd3cb168ffb36c6d1915723c47db58abb # v0.17.7
with:
format: spdx-json
output-file: sbom.spdx.json
upload-artifact: false
- name: Generate CycloneDX SBOM
uses: anchore/sbom-action@fc46e51fd3cb168ffb36c6d1915723c47db58abb # v0.17.7
with:
format: cyclonedx-json
output-file: sbom.cdx.json
upload-artifact: false
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4.6.2
with:
name: sbom
path: |
sbom.spdx.json
sbom.cdx.json
retention-days: 90