Skip to content

Commit 94d71e1

Browse files
LeadGoEngineerPaperclip-Paperclip
andcommitted
chore(ci): add supply-chain security workflow + harden ci.yml
- New .github/workflows/security.yml gates every push and PR on three scans: govulncheck (Go vuln DB), osv-scanner (transitive OSV vulns), and a license-policy check that enforces a permissive-only allowlist (Apache-2.0, MIT, BSD-2/3-Clause, MPL-2.0, ISC, Unlicense). - Pin every action by full commit SHA. Both workflows now declare workflow-level permissions: {} (default-deny) and re-grant only contents:read per job. - Reference the supply-chain bar from CONTRIBUTING.md so the rule is visible to anyone opening a PR. Refs QUA-7. Co-Authored-By: Paperclip <[email protected]>
1 parent 9dd5520 commit 94d71e1

3 files changed

Lines changed: 165 additions & 6 deletions

File tree

.github/workflows/ci.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,28 @@ on:
66
pull_request:
77
branches: [main]
88

9-
permissions:
10-
contents: read
9+
# Default-deny at workflow level; jobs grant only what they need.
10+
permissions: {}
1111

1212
jobs:
1313
markdown-lint:
1414
name: markdown lint
1515
runs-on: ubuntu-latest
16+
permissions:
17+
contents: read
1618
steps:
17-
- uses: actions/checkout@v4
18-
- uses: DavidAnson/markdownlint-cli2-action@v16
19+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
20+
- uses: DavidAnson/markdownlint-cli2-action@b4c9feab76d8025d1e83c653fa3990936df0e6c8 # v16.0.0
1921
with:
2022
globs: '**/*.md'
2123

2224
go:
2325
name: go vet + test
2426
runs-on: ubuntu-latest
27+
permissions:
28+
contents: read
2529
steps:
26-
- uses: actions/checkout@v4
30+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
2731
- id: detect
2832
name: detect go module
2933
run: |
@@ -33,7 +37,7 @@ jobs:
3337
echo "has_go=false" >> "$GITHUB_OUTPUT"
3438
echo "::notice::No go.mod present; skipping go vet and go test."
3539
fi
36-
- uses: actions/setup-go@v5
40+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
3741
if: steps.detect.outputs.has_go == 'true'
3842
with:
3943
go-version-file: go.mod

.github/workflows/security.yml

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
name: security
2+
3+
# Supply-chain and license-policy gate for quantcli repos.
4+
# Source of truth lives in quantcli/common; export-clis carry a copy of this file.
5+
# When updating, propagate the change to every *-export-cli repo (see CONTRIBUTING.md).
6+
7+
on:
8+
push:
9+
branches: [main]
10+
pull_request:
11+
branches: [main]
12+
workflow_dispatch:
13+
14+
# Default-deny at workflow level; each job re-grants only what it needs.
15+
permissions: {}
16+
17+
jobs:
18+
govulncheck:
19+
name: govulncheck (Go vuln DB)
20+
runs-on: ubuntu-latest
21+
permissions:
22+
contents: read
23+
steps:
24+
- name: Checkout
25+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
26+
27+
- name: Detect Go module
28+
id: detect
29+
run: |
30+
if [ -f go.mod ]; then
31+
echo "has_go=true" >> "$GITHUB_OUTPUT"
32+
else
33+
echo "has_go=false" >> "$GITHUB_OUTPUT"
34+
echo "::notice::No go.mod present; skipping govulncheck."
35+
fi
36+
37+
- name: Set up Go
38+
if: steps.detect.outputs.has_go == 'true'
39+
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
40+
with:
41+
go-version-file: go.mod
42+
cache: true
43+
44+
- name: Install govulncheck
45+
if: steps.detect.outputs.has_go == 'true'
46+
run: go install golang.org/x/vuln/cmd/govulncheck@latest
47+
48+
- name: Run govulncheck
49+
if: steps.detect.outputs.has_go == 'true'
50+
run: govulncheck ./...
51+
52+
osv-scanner:
53+
name: osv-scanner (transitive vulns)
54+
runs-on: ubuntu-latest
55+
permissions:
56+
contents: read
57+
steps:
58+
- name: Checkout
59+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
60+
61+
- name: Set up Go
62+
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
63+
with:
64+
go-version: stable
65+
cache: false
66+
67+
- name: Install osv-scanner
68+
run: go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latest
69+
70+
- name: Run osv-scanner
71+
run: osv-scanner scan source --recursive .
72+
73+
license-policy:
74+
name: license policy (allowlist)
75+
runs-on: ubuntu-latest
76+
permissions:
77+
contents: read
78+
env:
79+
# Policy: every direct + transitive Go dep must resolve to one of these SPDX ids.
80+
# See SECURITY.md "Supply-chain policy" for the rationale.
81+
ALLOWED_LICENSES: "Apache-2.0,MIT,BSD-2-Clause,BSD-3-Clause,MPL-2.0,ISC,Unlicense"
82+
steps:
83+
- name: Checkout
84+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
85+
86+
- name: Detect Go module
87+
id: detect
88+
run: |
89+
if [ -f go.mod ]; then
90+
echo "has_go=true" >> "$GITHUB_OUTPUT"
91+
else
92+
echo "has_go=false" >> "$GITHUB_OUTPUT"
93+
echo "::notice::No go.mod present; skipping license-policy check."
94+
fi
95+
96+
- name: Set up Go
97+
if: steps.detect.outputs.has_go == 'true'
98+
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
99+
with:
100+
go-version-file: go.mod
101+
cache: true
102+
103+
- name: Install go-licenses
104+
if: steps.detect.outputs.has_go == 'true'
105+
run: go install github.com/google/go-licenses@latest
106+
107+
- name: Check licenses against allowlist
108+
if: steps.detect.outputs.has_go == 'true'
109+
shell: bash
110+
run: |
111+
set -euo pipefail
112+
report=$(mktemp)
113+
# go-licenses csv emits: module,license-url,license-id (one per line).
114+
# Warnings (e.g. license-url not found) go to stderr and are non-fatal here.
115+
go-licenses csv ./... > "$report"
116+
117+
IFS=',' read -ra ALLOWED <<< "$ALLOWED_LICENSES"
118+
is_allowed() {
119+
local needle="$1"
120+
for a in "${ALLOWED[@]}"; do
121+
if [ "$needle" = "$a" ]; then return 0; fi
122+
done
123+
return 1
124+
}
125+
126+
bad=0
127+
while IFS=',' read -r module url license; do
128+
[ -z "$module" ] && continue
129+
if ! is_allowed "$license"; then
130+
echo "::error::Disallowed license: module=$module license=$license"
131+
bad=1
132+
fi
133+
done < "$report"
134+
135+
if [ "$bad" -ne 0 ]; then
136+
echo "::error::License policy violated. Allowlist: ${ALLOWED_LICENSES}"
137+
echo "::error::See SECURITY.md for the policy and how to request an exception."
138+
exit 1
139+
fi
140+
141+
echo "All dependency licenses are within policy."

CONTRIBUTING.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,20 @@ The contract is only as honest as the test that proves three CLIs behave the sam
7878

7979
The `compat/` harness is being scaffolded — until it lands, document the test you *would* write in the PR body so the expectation stays visible.
8080

81+
## Supply-chain and security
82+
83+
Every PR — here and in each `*-export-cli` — is gated on the `security` workflow defined in [`.github/workflows/security.yml`](.github/workflows/security.yml). It runs three checks:
84+
85+
- **`govulncheck`** — Go vulnerability database scan against the module's full dependency closure (skipped when there is no `go.mod`).
86+
- **`osv-scanner`** — OSV transitive vulnerability scan over the working tree.
87+
- **License policy** — every direct + transitive Go dependency must resolve to one of the permissive SPDX ids listed in [`SECURITY.md`](SECURITY.md#supply-chain-policy). GPL-family, SSPL, BSL, and other non-permissive licenses are blocking.
88+
89+
If a PR fails the license-policy check, do not unblock it by adding a `replace` directive or vendoring a fork to relabel the license. Either drop the dependency, switch to a permissively licensed equivalent, or open an issue requesting a policy exception with the rationale.
90+
91+
To propose a security policy change (allowlist, severity bar, or workflow tooling), open an issue here first — these decisions ripple to every export-cli.
92+
93+
For reporting a vulnerability in a shipped CLI or in `common`, see [SECURITY.md](SECURITY.md). Do **not** open a public issue with exploit details.
94+
8195
## License and sign-off
8296

8397
This repo is MIT-licensed (see [LICENSE](LICENSE)). By contributing you agree your changes are under the same license. We don't require a CLA. Sign-off (`Signed-off-by:` in commit messages) is encouraged but not required.

0 commit comments

Comments
 (0)