-
Notifications
You must be signed in to change notification settings - Fork 231
[SECURITY] SBOM generation pipeline for contracts and backend (#1531) #1554
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,116 @@ | ||||||||||
| name: SBOM Generation | ||||||||||
|
|
||||||||||
| on: | ||||||||||
| pull_request: | ||||||||||
| push: | ||||||||||
| branches: | ||||||||||
| - main | ||||||||||
| workflow_dispatch: | ||||||||||
| # Re-run SBOM generation when a new release tag is cut by release-please. | ||||||||||
| release: | ||||||||||
| types: [published] | ||||||||||
|
|
||||||||||
| permissions: | ||||||||||
| contents: read | ||||||||||
|
|
||||||||||
| jobs: | ||||||||||
| sbom: | ||||||||||
| name: Generate CycloneDX SBOMs | ||||||||||
| runs-on: ubuntu-latest | ||||||||||
| timeout-minutes: 20 | ||||||||||
|
|
||||||||||
| env: | ||||||||||
| CARGO_TERM_COLOR: never | ||||||||||
|
|
||||||||||
| steps: | ||||||||||
| - uses: actions/checkout@v4 | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
sed -n '1,120p' .github/workflows/sbom.yml 2>/dev/null || true
echo
echo "== git references to credentials in workflow =="
rg -n "persist-credentials|git (push|fetch|clone|remote-add|remote set-url)|credential|GITHUB_TOKEN|GH_TOKEN|GIT_ASKPASS|SSH_AUTH_SOCK" .github/workflows/sbom.yml || true
echo
echo "== checkout usage summary =="
python3 - <<'PY'
from pathlib import Path
p=Path('.github/workflows/sbom.yml')
text=p.read_text().splitlines()
for i,line in enumerate(text,1):
if 'uses: actions/checkout' in line:
print(f"{i}: {line.strip()}")
j=i
while j <= len(text) and (line.startswith(' ') or line.strip()=='' or 'uses:' not in text[j-1] or text[j-1].strip().startswith('- uses: actions/checkout')):
line=text[j-1]
if 'uses:' in line and j>i:
break
j+=1
PYRepository: ritik4ever/stellar-portfolio-rebalancer Length of output: 4067 Disable persisted checkout credentials. This workflow runs pull request code and does not need authenticated Git operations. Set Proposed fix - uses: actions/checkout@v4
+ with:
+ persist-credentials: false📝 Committable suggestion
Suggested change
🧰 Tools🪛 zizmor (1.28.0)[warning] 26-28: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||
|
|
||||||||||
| # ----- Backend + frontend prerequisites ------------------------------ | ||||||||||
| - uses: actions/setup-node@v4 | ||||||||||
| with: | ||||||||||
| node-version: 20 | ||||||||||
| cache: 'npm' | ||||||||||
| cache-dependency-path: | | ||||||||||
| frontend/package-lock.json | ||||||||||
| backend/package-lock.json | ||||||||||
|
|
||||||||||
| - name: Install backend dependencies (for lockfile-based SBOM) | ||||||||||
| working-directory: backend | ||||||||||
| run: npm ci --ignore-scripts | ||||||||||
|
|
||||||||||
| - name: Install frontend dependencies (for lockfile-based SBOM) | ||||||||||
| working-directory: frontend | ||||||||||
| run: npm ci --ignore-scripts | ||||||||||
|
|
||||||||||
| # ----- Contracts prerequisites ---------------------------------------- | ||||||||||
| # `cargo cyclonedx` needs Rust + cargo-cyclonedx. Cache cargo like the | ||||||||||
| # `contract-smoke` workflow to keep cold installs short. | ||||||||||
| - name: Setup Rust | ||||||||||
| uses: dtolnay/rust-toolchain@stable | ||||||||||
| with: | ||||||||||
| targets: wasm32-unknown-unknown | ||||||||||
|
|
||||||||||
| - name: Cache Cargo | ||||||||||
| uses: actions/cache@v4 | ||||||||||
| with: | ||||||||||
| path: | | ||||||||||
| ~/.cargo/registry | ||||||||||
| ~/.cargo/git | ||||||||||
| key: sbom-cargo-${{ runner.os }}-${{ hashFiles('contracts/Cargo.lock', 'contracts/Cargo.toml') }} | ||||||||||
| restore-keys: | | ||||||||||
| sbom-cargo-${{ runner.os }}- | ||||||||||
|
|
||||||||||
| # ----- Install SBOM tools (cargo-cyclonedx; node tool is npx per-run) - | ||||||||||
| - name: Install SBOM generation tools | ||||||||||
| run: bash scripts/sbom/install-tools.sh | ||||||||||
|
|
||||||||||
| # ----- Prepare output directory -------------------------------------- | ||||||||||
| - name: Prepare SBOM artifact directory | ||||||||||
| run: mkdir -p security/sbom | ||||||||||
|
|
||||||||||
| # ----- Generate the three SBOMs -------------------------------------- | ||||||||||
| - name: Generate contracts SBOM (CycloneDX 1.5 JSON) | ||||||||||
| run: bash scripts/sbom/generate-contracts-sbom.sh | ||||||||||
|
|
||||||||||
| - name: Generate backend SBOM (CycloneDX 1.5 JSON) | ||||||||||
| run: bash scripts/sbom/generate-backend-sbom.sh | ||||||||||
|
|
||||||||||
| - name: Generate frontend SBOM (CycloneDX 1.5 JSON) | ||||||||||
| run: bash scripts/sbom/generate-frontend-sbom.sh | ||||||||||
|
Comment on lines
+72
to
+79
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
rg -n 'CYCLONEDX_SPEC_VERSION|specVersion|CycloneDX 1\.[56]|1\.[56]' \
scripts/sbom .github/workflows/sbom.yml security/SBOM.md CHANGELOG.md
for file in contracts.cdx.json backend.cdx.json frontend.cdx.json; do
test "$(node -p "require('./security/sbom/$file').specVersion")" = "1.6"
doneRepository: ritik4ever/stellar-portfolio-rebalancer Length of output: 4870 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== changed files =="
git diff --name-only -- 'security/SBOM.md' 'CHANGELOG.md' '.github/workflows/sbom.yml' 'scripts/sbom/*'
echo
echo "== workflow around runs =="
sed -n '1,120p' .github/workflows/sbom.yml
echo
echo "== SBOM docs top and version sections =="
sed -n '1,130p' security/SBOM.md
sed -n '170,210p' security/SBOM.md
echo
echo "== changelog relevant lines =="
sed -n '100,125p' CHANGELOG.md
echo
echo "== generator scripts relevant lines =="
for f in scripts/sbom/generate-contracts-sbom.sh scripts/sbom/generate-backend-sbom.sh scripts/sbom/generate-frontend-sbom.sh scripts/sbom/generate-all.sh scripts/sbom/install-tools.sh; do
echo "--- $f"
wc -l "$f"
sed -n '1,80p' "$f"
done
echo
echo "== tracked SBOM files and specVersion =="
git ls-files security/sbom
for file in contracts.cdx.json backend.cdx.json frontend.cdx.json; do
if [ -f "security/sbom/$file" ]; then
node -e "const fs=require('fs'); const f='security/sbom/${file}'; const j=JSON.parse(fs.readFileSync(f,'utf8')); console.log(f+' specVersion='+j.specVersion+' bomFormat='+j.bomFormat)"
else
echo "missing security/sbom/$file"
fi
done
echo
echo "== SBOM version references =="
rg -n 'CycloneDX 1\.[56]|1\.[56]|CYCLONEDX_SPEC_VERSION|specVersion|npm run sbom|sbom:contracts|sbom:backend|sbom:frontend|make sbom|Generate contracts SBOM|Generate backend SBOM|Generate frontend SBOM|SBOMs|SBOM' \
.github/workflows/sbom.yml security/SBOM.md CHANGELOG.md scripts/sbom security 2>/dev/null | head -200Repository: ritik4ever/stellar-portfolio-rebalancer Length of output: 36132 Ensure generated SBOM artifacts match CycloneDX 1.6 before upload.
🤖 Prompt for AI Agents |
||||||||||
|
|
||||||||||
| # ----- Surface the artifacts ------------------------------------------ | ||||||||||
| - name: List SBOM files | ||||||||||
| run: ls -la security/sbom | ||||||||||
|
|
||||||||||
| - name: Upload contracts SBOM | ||||||||||
| uses: actions/upload-artifact@v4 | ||||||||||
| with: | ||||||||||
| name: sbom-contracts | ||||||||||
| path: security/sbom/contracts.cdx.json | ||||||||||
| if-no-files-found: error | ||||||||||
|
|
||||||||||
| - name: Upload backend SBOM | ||||||||||
| uses: actions/upload-artifact@v4 | ||||||||||
| with: | ||||||||||
| name: sbom-backend | ||||||||||
| path: security/sbom/backend.cdx.json | ||||||||||
| if-no-files-found: error | ||||||||||
|
|
||||||||||
| - name: Upload frontend SBOM | ||||||||||
| uses: actions/upload-artifact@v4 | ||||||||||
| with: | ||||||||||
| name: sbom-frontend | ||||||||||
| path: security/sbom/frontend.cdx.json | ||||||||||
| if-no-files-found: error | ||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||
|
|
||||||||||
| # ----- PR-only comment so reviewers can audit the dependency graph ---- | ||||||||||
| - name: Summarize SBOM component counts (PR only) | ||||||||||
| if: github.event_name == 'pull_request' | ||||||||||
| run: | | ||||||||||
| for f in contracts.cdx.json backend.cdx.json frontend.cdx.json; do | ||||||||||
| count=$(node -e "const b=require('./security/sbom/${f}'); console.log(b.components ? b.components.length : 0);") | ||||||||||
| echo "::group::${f}: ${count} components" | ||||||||||
| head -c 600 "security/sbom/${f}" || true | ||||||||||
| echo | ||||||||||
| echo "::endgroup::" | ||||||||||
| done | ||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: ritik4ever/stellar-portfolio-rebalancer
Length of output: 551
🏁 Script executed:
Repository: ritik4ever/stellar-portfolio-rebalancer
Length of output: 15246
🌐 Web query:
Googleapis release-please-action v4 token GitHub Release created downstream release events workflows GITHUB_TOKEN💡 Result:
When using googleapis/release-please-action v4, the default GITHUB_TOKEN will not trigger downstream GitHub Actions workflows (such as those triggered by release.published or push events). This is a standard GitHub Actions security feature designed to prevent recursive workflow loops [1][2][3][4]. To enable downstream workflow triggering, you have two primary options: 1. Use a Personal Access Token (PAT): You can provide a PAT with the necessary permissions (e.g., repo, workflow) to the token input of the action [1][5]. Since the event is then perceived by GitHub as being generated by a user rather than a GITHUB_TOKEN, it will trigger subsequent workflows as expected [6][5]. 2. Explicitly trigger downstream workflows: You can use the outputs provided by the release-please-action (such as release_created and tag_name) to manually dispatch subsequent workflows within the same action run [7][8][3]. This approach avoids the need for a PAT and is often implemented using the GitHub CLI or GitHub API via actions/github-script [7][8][3]. Example using explicit dispatch: - uses: googleapis/release-please-action@v4 id: release with: token: ${{ secrets.GITHUB_TOKEN }} - name: Trigger downstream workflow if: ${{ steps.release.outputs.release_created == 'true' }} run: | gh workflow run publish.yml --ref "${{ steps.release.outputs.tag_name }}" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} Note that when using explicit dispatch, you must ensure your GITHUB_TOKEN has the required permissions (e.g., actions: write) to trigger other workflows [7][3]. Additionally, verify that you are referencing output variables correctly (e.g., using == 'true' for boolean outputs in v4) [9].
Citations:
outputs.releases_createdhas different behavior compared to v3 googleapis/release-please-action#912Make the release SBOM run reachable after release creation.
.github/workflows/release-please.ymlusesgoogleapis/release-please-action@v4without atoken, so it defaults toGITHUB_TOKENand the subsequentrelease: publishedevent will not start.github/workflows/sbom.yml. Add a token/input capability that can trigger downstream workflows, or generate the release SBOM from the release-please workflow usingsteps.release.outputs.release_created/tag_nameby manual dispatch.🤖 Prompt for AI Agents