Skip to content

Commit 664f3ad

Browse files
feat: add SLSA Level 3 build provenance + unified badge header (#6)
Extract reusable build workflow with attest-build-provenance for SLSA L3. Refactor publish.yml to call it. Standardize README badges across the Project-Navi org. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d10f0dc commit 664f3ad

3 files changed

Lines changed: 123 additions & 57 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: Reusable Build (SLSA L3)
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
tag_name:
7+
required: true
8+
type: string
9+
10+
permissions: {}
11+
12+
jobs:
13+
build:
14+
name: Build distribution
15+
runs-on: ubuntu-latest
16+
timeout-minutes: 10
17+
permissions:
18+
contents: write
19+
id-token: write
20+
attestations: write
21+
steps:
22+
- name: Harden runner
23+
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
24+
with:
25+
egress-policy: audit
26+
27+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
28+
29+
- name: Set up uv
30+
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v5.4.2
31+
with:
32+
enable-cache: true
33+
34+
- name: Build wheel and sdist
35+
run: uv build
36+
37+
- name: Smoke test
38+
run: |
39+
uv venv .smoke-venv
40+
uv pip install dist/*.whl --python .smoke-venv/bin/python
41+
.smoke-venv/bin/python -c "
42+
from navi_sanitize import clean, walk, jinja2_escaper, path_escaper
43+
assert clean('hello') == 'hello'
44+
assert walk({'k': 'v'}) == {'k': 'v'}
45+
print('Smoke test passed')
46+
"
47+
48+
- name: Generate CycloneDX SBOM
49+
uses: anchore/sbom-action@17ae1740179002c89186b61233e0f892c3118b11 # v0.23.0
50+
with:
51+
path: .
52+
format: cyclonedx-json
53+
output-file: navi-sanitize.cdx.json
54+
upload-artifact: false
55+
upload-release-assets: false
56+
57+
- name: Generate SPDX SBOM
58+
uses: anchore/sbom-action@17ae1740179002c89186b61233e0f892c3118b11 # v0.23.0
59+
with:
60+
path: .
61+
format: spdx-json
62+
output-file: navi-sanitize.spdx.json
63+
upload-artifact: false
64+
upload-release-assets: false
65+
66+
- name: Attest build provenance
67+
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
68+
with:
69+
subject-path: |
70+
dist/*
71+
navi-sanitize.cdx.json
72+
navi-sanitize.spdx.json
73+
74+
- name: Upload dist artifacts
75+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
76+
with:
77+
name: dist
78+
path: dist/
79+
80+
- name: Upload SBOM artifacts
81+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
82+
with:
83+
name: sbom
84+
path: |
85+
navi-sanitize.cdx.json
86+
navi-sanitize.spdx.json

.github/workflows/publish.yml

Lines changed: 30 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ on:
44
release:
55
types: [published]
66

7-
permissions:
8-
contents: read
7+
permissions: {}
98

109
concurrency:
1110
group: ${{ github.workflow }}-${{ github.ref }}
@@ -14,80 +13,59 @@ concurrency:
1413
jobs:
1514
ci:
1615
uses: ./.github/workflows/ci.yml
16+
permissions:
17+
contents: read
1718

18-
publish:
19+
build:
1920
needs: [ci]
21+
uses: ./.github/workflows/_build-reusable.yml
22+
with:
23+
tag_name: ${{ github.event.release.tag_name }}
24+
permissions:
25+
contents: write
26+
id-token: write
27+
attestations: write
28+
29+
publish:
30+
needs: [build]
2031
runs-on: ubuntu-latest
32+
timeout-minutes: 10
2133
environment: pypi
2234
permissions:
2335
id-token: write
2436
steps:
25-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
26-
- uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
27-
with:
28-
enable-cache: true
29-
- name: Build wheel and sdist
30-
run: uv build
31-
- name: Smoke test
32-
run: |
33-
uv venv .smoke-venv
34-
uv pip install dist/*.whl --python .smoke-venv/bin/python
35-
.smoke-venv/bin/python -c "
36-
from navi_sanitize import clean, walk, jinja2_escaper, path_escaper
37-
assert clean('hello') == 'hello'
38-
assert walk({'k': 'v'}) == {'k': 'v'}
39-
print('Smoke test passed')
40-
"
41-
- name: Upload dist
42-
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
37+
- name: Download dist artifacts
38+
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
4339
with:
4440
name: dist
4541
path: dist/
42+
4643
- name: Publish to PyPI
47-
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
44+
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
4845
with:
4946
print-hash: true
5047

5148
release-assets:
5249
needs: [publish]
5350
runs-on: ubuntu-latest
51+
timeout-minutes: 10
5452
permissions:
5553
contents: write
56-
id-token: write
57-
attestations: write
5854
steps:
59-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
60-
- uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
55+
- name: Download dist artifacts
56+
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
6157
with:
6258
name: dist
6359
path: dist/
64-
- name: Generate CycloneDX SBOM
65-
uses: anchore/sbom-action@17ae1740179002c89186b61233e0f892c3118b11 # v0.23.0
66-
with:
67-
path: dist/
68-
artifact-name: navi-sanitize.cdx.json
69-
output-file: navi-sanitize.cdx.json
70-
format: cyclonedx-json
71-
upload-artifact: false
72-
upload-release-assets: true
73-
- name: Generate SPDX SBOM
74-
uses: anchore/sbom-action@17ae1740179002c89186b61233e0f892c3118b11 # v0.23.0
75-
with:
76-
path: dist/
77-
artifact-name: navi-sanitize.spdx.json
78-
output-file: navi-sanitize.spdx.json
79-
format: spdx-json
80-
upload-artifact: false
81-
upload-release-assets: true
82-
- name: Attest build provenance
83-
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
60+
61+
- name: Download SBOM artifacts
62+
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
8463
with:
85-
subject-path: |
86-
dist/*
87-
navi-sanitize.cdx.json
88-
navi-sanitize.spdx.json
89-
- name: Upload dist to release
64+
name: sbom
65+
path: sbom/
66+
67+
- name: Upload to GitHub Release
9068
env:
9169
GH_TOKEN: ${{ github.token }}
9270
TAG_NAME: ${{ github.ref_name }}
93-
run: gh release upload "$TAG_NAME" dist/* --clobber
71+
run: gh release upload "$TAG_NAME" dist/* sbom/* --clobber

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# navi-sanitize
22

3-
[![CI](https://github.com/Project-Navi/navi-sanitize/actions/workflows/ci.yml/badge.svg)](https://github.com/Project-Navi/navi-sanitize/actions/workflows/ci.yml)
4-
[![Fuzz](https://github.com/Project-Navi/navi-sanitize/actions/workflows/fuzz.yml/badge.svg)](https://github.com/Project-Navi/navi-sanitize/actions/workflows/fuzz.yml)
5-
[![codecov](https://codecov.io/github/Project-Navi/navi-sanitize/graph/badge.svg?token=9Vr26NV2Fn)](https://codecov.io/github/Project-Navi/navi-sanitize)
6-
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7-
[![Python 3.12+](https://img.shields.io/badge/Python-3.12%2B-green.svg)](https://www.python.org/)
3+
[![Tests](https://github.com/Project-Navi/navi-sanitize/actions/workflows/ci.yml/badge.svg)](https://github.com/Project-Navi/navi-sanitize/actions/workflows/ci.yml)
4+
[![CodeQL](https://github.com/Project-Navi/navi-sanitize/actions/workflows/codeql.yml/badge.svg)](https://github.com/Project-Navi/navi-sanitize/actions/workflows/codeql.yml)
5+
[![codecov](https://codecov.io/gh/Project-Navi/navi-sanitize/graph/badge.svg?token=9Vr26NV2Fn)](https://codecov.io/gh/Project-Navi/navi-sanitize)
86
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Project-Navi/navi-sanitize/badge)](https://scorecard.dev/viewer/?uri=github.com/Project-Navi/navi-sanitize)
7+
[![SLSA 3](https://slsa.dev/images/gh-badge-level3.svg)](https://slsa.dev)
8+
[![PyPI](https://img.shields.io/pypi/v/navi-sanitize)](https://pypi.org/project/navi-sanitize/)
9+
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
10+
[![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/downloads/)
911
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
1012

1113
Deterministic input sanitization for untrusted text. Zero dependencies, zero false positives.

0 commit comments

Comments
 (0)