Skip to content

Commit b21d014

Browse files
committed
feat(security): add reusable security workflow runtime
1 parent a058208 commit b21d014

1 file changed

Lines changed: 291 additions & 0 deletions

File tree

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
name: Code Foundry Security
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
runtime-repository:
7+
description: Repository containing the Code Foundry runtime.
8+
required: false
9+
type: string
10+
default: 0xPlayerOne/code-foundry
11+
runtime-ref:
12+
description: Code Foundry runtime tag or ref.
13+
required: false
14+
type: string
15+
default: v0.9.0
16+
17+
permissions:
18+
contents: read
19+
20+
env:
21+
REPO_FOUNDRY_PROFILE: ${{ vars.REPO_FOUNDRY_PROFILE }}
22+
REPO_FOUNDRY_LANGUAGES: ${{ vars.REPO_FOUNDRY_LANGUAGES }}
23+
REPO_FOUNDRY_FEATURES: ${{ vars.REPO_FOUNDRY_FEATURES }}
24+
REPO_FOUNDRY_PACKAGE_MANAGER: ${{ vars.REPO_FOUNDRY_PACKAGE_MANAGER }}
25+
REPO_FOUNDRY_CACHE_PACKAGES: ${{ vars.REPO_FOUNDRY_CACHE_PACKAGES || 'auto' }}
26+
REPO_FOUNDRY_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before || '' }}
27+
28+
concurrency:
29+
group: ${{ github.workflow }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
30+
cancel-in-progress: true
31+
32+
jobs:
33+
profile:
34+
name: Profile
35+
runs-on: ubuntu-slim
36+
timeout-minutes: 10
37+
outputs:
38+
javascript: ${{ steps.profile.outputs.javascript }}
39+
rust: ${{ steps.profile.outputs.rust }}
40+
python: ${{ steps.profile.outputs.python }}
41+
python_requirements: ${{ steps.profile.outputs.python_requirements }}
42+
steps:
43+
- name: Checkout
44+
uses: actions/checkout@v7
45+
with:
46+
fetch-depth: 2
47+
filter: blob:none
48+
sparse-checkout: |
49+
.github
50+
.mise.toml
51+
mise.lock
52+
.npmrc
53+
.pnpmfile.cjs
54+
.yarnrc*
55+
**/package.json
56+
**/bun.lock
57+
**/bun.lockb
58+
**/pnpm-lock.yaml
59+
**/yarn.lock
60+
**/package-lock.json
61+
**/Cargo.toml
62+
**/Cargo.lock
63+
**/.cargo/**
64+
**/pyproject.toml
65+
**/requirements*.txt
66+
**/setup.py
67+
**/setup.cfg
68+
**/Pipfile
69+
**/Pipfile.lock
70+
**/poetry.lock
71+
**/uv.lock
72+
sparse-checkout-cone-mode: false
73+
- name: Runtime
74+
uses: actions/checkout@v7
75+
with:
76+
repository: ${{ inputs.runtime-repository }}
77+
ref: ${{ inputs.runtime-ref }}
78+
path: .code-foundry
79+
sparse-checkout: |
80+
.github/actions
81+
.github/scripts
82+
- name: Install runtime
83+
run: |
84+
mkdir -p .github/actions .github/scripts
85+
cp -R .code-foundry/.github/actions/. .github/actions/
86+
cp -R .code-foundry/.github/scripts/. .github/scripts/
87+
- name: Detect
88+
id: profile
89+
run: bash .github/scripts/security.sh profile >> "$GITHUB_OUTPUT"
90+
91+
dependency-audit-javascript:
92+
name: Dependency Audit (JavaScript)
93+
runs-on: ubuntu-slim
94+
timeout-minutes: 20
95+
steps:
96+
- name: Checkout
97+
uses: actions/checkout@v7
98+
with:
99+
filter: blob:none
100+
sparse-checkout: |
101+
.github
102+
.mise.toml
103+
mise.lock
104+
.npmrc
105+
.pnpmfile.cjs
106+
.yarnrc*
107+
**/package.json
108+
**/bun.lock
109+
**/bun.lockb
110+
**/pnpm-lock.yaml
111+
**/yarn.lock
112+
**/package-lock.json
113+
sparse-checkout-cone-mode: false
114+
- name: Runtime
115+
uses: actions/checkout@v7
116+
with:
117+
repository: ${{ inputs.runtime-repository }}
118+
ref: ${{ inputs.runtime-ref }}
119+
path: .code-foundry
120+
sparse-checkout: |
121+
.github/actions
122+
.github/scripts
123+
- name: Install runtime
124+
run: |
125+
mkdir -p .github/actions .github/scripts
126+
cp -R .code-foundry/.github/actions/. .github/actions/
127+
cp -R .code-foundry/.github/scripts/. .github/scripts/
128+
- name: Detect
129+
id: applicability
130+
run: bash .github/scripts/security.sh should_run javascript >> "$GITHUB_OUTPUT"
131+
- name: Setup
132+
if: steps.applicability.outputs.applicable == 'true'
133+
uses: ./.github/actions/setup
134+
with:
135+
mise-scope: javascript
136+
cache-save: ${{ github.event_name != 'pull_request' }}
137+
- name: Audit dependencies
138+
if: steps.applicability.outputs.applicable == 'true'
139+
run: bash .github/scripts/security.sh audit javascript
140+
141+
dependency-audit-rust:
142+
name: Dependency Audit (Rust)
143+
runs-on: ${{ vars.REPO_FOUNDRY_RUST_AUDIT_RUNNER || 'ubuntu-latest' }}
144+
timeout-minutes: 20
145+
steps:
146+
- name: Checkout
147+
uses: actions/checkout@v7
148+
with:
149+
filter: blob:none
150+
sparse-checkout: |
151+
.github
152+
.mise.toml
153+
mise.lock
154+
**/Cargo.toml
155+
**/Cargo.lock
156+
**/.cargo/**
157+
sparse-checkout-cone-mode: false
158+
- name: Runtime
159+
uses: actions/checkout@v7
160+
with:
161+
repository: ${{ inputs.runtime-repository }}
162+
ref: ${{ inputs.runtime-ref }}
163+
path: .code-foundry
164+
sparse-checkout: |
165+
.github/actions
166+
.github/scripts
167+
- name: Install runtime
168+
run: |
169+
mkdir -p .github/actions .github/scripts
170+
cp -R .code-foundry/.github/actions/. .github/actions/
171+
cp -R .code-foundry/.github/scripts/. .github/scripts/
172+
- name: Detect
173+
id: applicability
174+
run: bash .github/scripts/security.sh should_run rust >> "$GITHUB_OUTPUT"
175+
- name: Check Rust
176+
id: system-rust
177+
if: steps.applicability.outputs.applicable == 'true'
178+
shell: bash
179+
run: |
180+
if command -v cargo >/dev/null 2>&1 && command -v rustc >/dev/null 2>&1; then
181+
cargo --version
182+
rustc --version
183+
echo 'available=true' >> "$GITHUB_OUTPUT"
184+
else
185+
echo 'available=false' >> "$GITHUB_OUTPUT"
186+
fi
187+
- name: Setup
188+
if: >-
189+
steps.applicability.outputs.applicable == 'true' &&
190+
steps.system-rust.outputs.available != 'true'
191+
uses: ./.github/actions/setup
192+
with:
193+
mise-scope: rust
194+
cache-save: ${{ github.event_name != 'pull_request' }}
195+
- name: Install cargo-audit
196+
if: steps.applicability.outputs.applicable == 'true'
197+
uses: taiki-e/install-action@v2
198+
with:
199+
tool: cargo-audit
200+
- name: Audit dependencies
201+
if: steps.applicability.outputs.applicable == 'true'
202+
run: bash .github/scripts/security.sh audit rust
203+
204+
dependency-audit-python:
205+
name: Dependency Audit (Python) / ${{ matrix.requirement }}
206+
needs: profile
207+
if: >-
208+
${{ needs.profile.result == 'success' &&
209+
needs.profile.outputs.python == 'true' &&
210+
needs.profile.outputs.python_requirements != '["none"]' }}
211+
strategy:
212+
fail-fast: false
213+
max-parallel: 8
214+
matrix:
215+
requirement: ${{ fromJSON(needs.profile.outputs.python_requirements) }}
216+
runs-on: ubuntu-slim
217+
timeout-minutes: 20
218+
env:
219+
REPO_FOUNDRY_PYTHON_REQUIREMENT: ${{ matrix.requirement }}
220+
steps:
221+
- name: Checkout
222+
uses: actions/checkout@v7
223+
with:
224+
filter: blob:none
225+
sparse-checkout: |
226+
.github
227+
.mise.toml
228+
mise.lock
229+
**/pyproject.toml
230+
**/requirements*.txt
231+
**/setup.py
232+
**/setup.cfg
233+
**/Pipfile
234+
**/Pipfile.lock
235+
**/poetry.lock
236+
**/uv.lock
237+
sparse-checkout-cone-mode: false
238+
- name: Runtime
239+
uses: actions/checkout@v7
240+
with:
241+
repository: ${{ inputs.runtime-repository }}
242+
ref: ${{ inputs.runtime-ref }}
243+
path: .code-foundry
244+
sparse-checkout: |
245+
.github/actions
246+
.github/scripts
247+
- name: Install runtime
248+
run: |
249+
mkdir -p .github/actions .github/scripts
250+
cp -R .code-foundry/.github/actions/. .github/actions/
251+
cp -R .code-foundry/.github/scripts/. .github/scripts/
252+
- name: Setup
253+
uses: ./.github/actions/setup
254+
with:
255+
mise-scope: python
256+
cache-save: ${{ github.event_name != 'pull_request' }}
257+
- name: Audit dependencies
258+
run: bash .github/scripts/security.sh audit python
259+
260+
dependency-audit-python-gate:
261+
name: Dependency Audit (Python)
262+
needs: [profile, dependency-audit-python]
263+
if: always()
264+
runs-on: ubuntu-slim
265+
timeout-minutes: 5
266+
steps:
267+
- name: Gate
268+
env:
269+
PROFILE_RESULT: ${{ needs.profile.result }}
270+
AUDIT_RESULT: ${{ needs.dependency-audit-python.result }}
271+
run: |
272+
if [ "$PROFILE_RESULT" != success ]; then
273+
echo "Python audit profile failed: $PROFILE_RESULT" >&2
274+
exit 1
275+
fi
276+
case "$AUDIT_RESULT" in
277+
success|skipped) exit 0 ;;
278+
*) echo "Python dependency audit failed: $AUDIT_RESULT" >&2; exit 1 ;;
279+
esac
280+
281+
dependency-review:
282+
name: Dependency Review
283+
if: ${{ github.event_name == 'pull_request' && !github.event.repository.private }}
284+
runs-on: ubuntu-slim
285+
timeout-minutes: 10
286+
steps:
287+
- name: Review
288+
uses: actions/dependency-review-action@v5
289+
with:
290+
fail-on-severity: high
291+
license-check: true

0 commit comments

Comments
 (0)