-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add centralized org SBOM pipeline #361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
f29a0b0
feat: add centralized org SBOM pipeline
seonghobae 3cf5de1
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 5f77963
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 93bda57
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 6128014
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] d36703e
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] effa491
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 08cdf5b
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 5deec65
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 9dea4cf
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 074fcb0
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 6a1fb60
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 8dc57d8
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 28abbfa
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] d41a7ee
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] e6d4f01
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 9f57f04
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] dc94bea
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 16ff32e
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 86360e9
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 6d1b8a8
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 570d252
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 670210e
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 7e7f4fe
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 7274f72
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] dbfbbe3
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] b68a4f3
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] ccd5daf
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 06608a0
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] c5bee3b
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 1fcf92d
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] fdc62a3
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 0201d8a
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 1b56a51
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 4af580a
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 19daab3
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 0d551b0
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] bf04bfa
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] aaa06e9
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] a6c18b1
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 648ea4a
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 80422ba
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] 18e55e7
Merge branch 'main' into feat/central-sbom
opencode-agent[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| # Central SBOM generation for every ContextualWisdomLab repo. | ||
| # | ||
| # This is a REQUIRED-style org workflow (mirrors security-scan.yml): same | ||
| # pull_request trigger conventions, least-privilege permissions, SHA-pinned | ||
| # actions. It complements the Security Scan by producing a Software Bill of | ||
| # Materials for every repo's dependencies on each PR and release. | ||
| # | ||
| # What it does per repo: | ||
| # - Generates BOTH a CycloneDX and an SPDX SBOM with anchore/syft (via the | ||
| # anchore/sbom-action wrapper; Apache-2.0, permissive tooling only), scanning | ||
| # the whole filesystem so every present ecosystem is covered | ||
| # (npm / pyproject / uv / cargo / go / maven). | ||
| # - Uploads each SBOM as a build artifact. | ||
| # - Attaches both SBOMs to GitHub releases (on release: published). | ||
| # - Submits the SPDX snapshot to the GitHub dependency submission API so the | ||
| # components show up in the repo's dependency graph. That graph is the source | ||
| # the central SBOM inventory aggregator reads back out org-wide. | ||
| # | ||
| # NOTE: contents: write is required for release-asset upload and for the | ||
| # dependency submission API. Fork PR heads run without write and simply skip | ||
| # those side effects; the artifact is still produced. | ||
| name: SBOM Generation | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened, ready_for_review, closed] | ||
| branches: [main, master, develop] | ||
| release: | ||
| types: [published] | ||
|
|
||
| concurrency: | ||
| group: sbom-generation-${{ github.event.pull_request.base.repo.full_name || github.repository }}-${{ github.event.pull_request.number || github.event.release.tag_name || github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| cancel-closed-pr-runs: | ||
| if: github.event_name == 'pull_request' && github.event.action == 'closed' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - run: echo "PR closed; this run only cancels older runs through workflow concurrency." | ||
|
|
||
| generate-sbom: | ||
| if: github.event_name != 'pull_request' || github.event.action != 'closed' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| # write is needed for release-asset upload and dependency submission. | ||
| contents: write | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Generate SPDX SBOM and submit dependency snapshot | ||
| uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 | ||
| with: | ||
| path: . | ||
| format: spdx-json | ||
| output-file: sbom.spdx.json | ||
| artifact-name: sbom-spdx-json | ||
| upload-artifact: true | ||
| upload-release-assets: true | ||
| # Feeds the repo dependency graph -> read back by the org aggregator. | ||
| dependency-snapshot: true | ||
|
|
||
| - name: Generate CycloneDX SBOM | ||
| uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 | ||
| with: | ||
| path: . | ||
| format: cyclonedx-json | ||
| output-file: sbom.cyclonedx.json | ||
| artifact-name: sbom-cyclonedx-json | ||
| upload-artifact: true | ||
| upload-release-assets: true | ||
| dependency-snapshot: false | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| # Central SBOM inventory aggregator. | ||
| # | ||
| # Scheduled companion to sbom-generation.yml. It reads every managed repo's | ||
| # latest SBOM back out of the GitHub dependency graph (populated by the | ||
| # per-repo SBOM Generation dependency snapshot) and writes ONE consolidated org | ||
| # inventory into this .github repo: | ||
| # | ||
| # docs/sbom/inventory.json machine-readable component roll-up | ||
| # docs/sbom/inventory.md component + license roll-up (flags copyleft / | ||
| # NOASSERTION against the commercial-license-only policy) | ||
| # | ||
| # Cross-repo reads reuse the OpenCode app OIDC token exchange the other | ||
| # schedulers use, falling back to github.token. Results land through a PR so the | ||
| # central inventory update follows the same review path as everything else. | ||
| name: SBOM Inventory Scheduler | ||
|
|
||
| on: | ||
| schedule: | ||
| - cron: "0 6 * * 1" | ||
| workflow_dispatch: | ||
| inputs: | ||
| org: | ||
| description: Organization login to inventory | ||
| required: false | ||
| default: ContextualWisdomLab | ||
| type: string | ||
|
|
||
| concurrency: | ||
| group: sbom-inventory-scheduler-${{ github.repository }} | ||
| cancel-in-progress: false | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| aggregate-sbom-inventory: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||
| id-token: write | ||
| pull-requests: write | ||
| env: | ||
| ORG_LOGIN: ${{ inputs.org || vars.SBOM_INVENTORY_ORG || 'ContextualWisdomLab' }} | ||
| steps: | ||
| - name: Exchange OpenCode app token for cross-repo reads | ||
| id: aggregator_app_token | ||
| env: | ||
| OIDC_AUDIENCE: opencode-github-action | ||
| OPENCODE_API_BASE_URL: https://api.opencode.ai | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| mark_unavailable() { | ||
| echo "available=false" >>"$GITHUB_OUTPUT" | ||
| } | ||
|
|
||
| if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then | ||
| echo "OpenCode app token exchange unavailable: OIDC request environment is missing." | ||
| mark_unavailable | ||
| exit 0 | ||
| fi | ||
|
|
||
| request_url="${ACTIONS_ID_TOKEN_REQUEST_URL}" | ||
| separator="&" | ||
| case "$request_url" in | ||
| *\?*) ;; | ||
| *) separator="?" ;; | ||
| esac | ||
|
|
||
| if ! oidc_response="$( | ||
| curl -fsS \ | ||
| -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \ | ||
| "${request_url}${separator}audience=${OIDC_AUDIENCE}" | ||
| )"; then | ||
| echo "OpenCode app token exchange unavailable: OIDC token request did not complete." | ||
| mark_unavailable | ||
| exit 0 | ||
| fi | ||
|
|
||
| oidc_token="$(jq -r '.value // empty' <<<"$oidc_response")" | ||
| if [ -z "$oidc_token" ]; then | ||
| echo "OpenCode app token exchange unavailable: OIDC token response was empty." | ||
| mark_unavailable | ||
| exit 0 | ||
| fi | ||
|
|
||
| if ! token_response="$( | ||
| curl -fsS \ | ||
| -X POST \ | ||
| -H "Authorization: Bearer ${oidc_token}" \ | ||
| "${OPENCODE_API_BASE_URL}/exchange_github_app_token" | ||
| )"; then | ||
| echo "OpenCode app token exchange unavailable: app token request did not complete." | ||
| mark_unavailable | ||
| exit 0 | ||
| fi | ||
|
|
||
| app_token="$(jq -r '.token // empty' <<<"$token_response")" | ||
| if [ -z "$app_token" ]; then | ||
| echo "OpenCode app token exchange unavailable: app token response was empty." | ||
| mark_unavailable | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "::add-mask::$app_token" | ||
| { | ||
| echo "available=true" | ||
| echo "token=$app_token" | ||
| } >>"$GITHUB_OUTPUT" | ||
|
|
||
| - name: Checkout trusted aggregator | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| with: | ||
| repository: ContextualWisdomLab/.github | ||
| ref: main | ||
| fetch-depth: 1 | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 | ||
| with: | ||
| python-version: "3.12" | ||
|
|
||
| - name: Self-test aggregator | ||
| run: python3 scripts/ci/sbom_inventory_aggregator.py --self-test | ||
|
|
||
| - name: Aggregate org SBOM inventory | ||
| env: | ||
| GH_TOKEN: ${{ secrets.SBOM_INVENTORY_TOKEN || steps.aggregator_app_token.outputs.token || github.token }} | ||
| run: | | ||
| set -euo pipefail | ||
| generated_at="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" | ||
| python3 scripts/ci/sbom_inventory_aggregator.py \ | ||
| --org "$ORG_LOGIN" \ | ||
| --output-dir docs/sbom \ | ||
| --generated-at "$generated_at" | ||
|
|
||
| - name: Open or update inventory PR | ||
| env: | ||
| GH_TOKEN: ${{ secrets.SBOM_INVENTORY_TOKEN || steps.aggregator_app_token.outputs.token || github.token }} | ||
| run: | | ||
| set -euo pipefail | ||
| if git diff --quiet -- docs/sbom; then | ||
| echo "No SBOM inventory changes; nothing to publish." | ||
| exit 0 | ||
| fi | ||
| branch="automation/sbom-inventory" | ||
| git config user.name "cwl-sbom-inventory[bot]" | ||
| git config user.email "cwl-sbom-inventory@users.noreply.github.com" | ||
| git checkout -B "$branch" | ||
| git add docs/sbom | ||
| git commit -m "chore: refresh org SBOM inventory" | ||
| git push --force-with-lease origin "$branch" | ||
| if [ -z "$(gh pr list --head "$branch" --state open --json number --jq '.[].number')" ]; then | ||
| gh pr create \ | ||
| --base main \ | ||
| --head "$branch" \ | ||
| --title "chore: refresh org SBOM inventory" \ | ||
| --body "Automated central SBOM inventory refresh. Review the license roll-up in docs/sbom/inventory.md for any flagged copyleft/NOASSERTION components." | ||
| fi | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| { | ||
| "schema": "cwl-sbom-inventory/v1", | ||
| "summary": { | ||
| "repo_count": 0, | ||
| "component_count": 0, | ||
| "flagged_count": 0, | ||
| "policy": "commercial-license-only" | ||
| }, | ||
| "license_totals": {}, | ||
| "flagged_licenses": [], | ||
| "repos": [] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # Organization SBOM inventory | ||
|
|
||
| Generated: pending first scheduled run | ||
|
|
||
| One central view of every managed repository's software components, | ||
| versions, and licenses. Feeds license and vulnerability governance | ||
| alongside the central Security Scan. | ||
|
|
||
| ## Summary | ||
|
|
||
| - Repositories: 0 | ||
| - Components: 0 | ||
| - Policy: commercial-license-only | ||
| - Flagged licenses: 0 | ||
|
|
||
| ## License roll-up | ||
|
|
||
| | License | Components | | ||
| | --- | ---: | | ||
|
|
||
| ## Flagged components (policy violations) | ||
|
|
||
| No copyleft or NOASSERTION components detected. | ||
|
|
||
| ## Per-repository components |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.