Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,15 @@ jobs:
npm ci
npm run build

# SBOM generation is owned by .github/workflows/sbom.yml which uploads
# `sbom-{frontend,backend,contracts}` as workflow artifacts and runs on
# every PR + push to main + release. We still emit lightweight copies
# into the build bundle so a single workflow run carries everything.
- name: Generate frontend SBOM into build bundle
working-directory: frontend
run: bash ../scripts/sbom/generate-frontend-sbom.sh && cp ../security/sbom/frontend.cdx.json ../artifacts/sbom/frontend.cdx.json

- name: Generate backend SBOM into build bundle
working-directory: backend
run: bash ../scripts/sbom/generate-backend-sbom.sh && cp ../security/sbom/backend.cdx.json ../artifacts/sbom/backend.cdx.json

2 changes: 1 addition & 1 deletion .github/workflows/contract-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
contract-deploy-${{ runner.os }}-

- name: Install Soroban CLI
run: cargo install --locked soroban-cli
run: cargo install --locked soroban-cli --version 25.2.0

- name: Validate deployment inputs
run: |
Expand Down
18 changes: 12 additions & 6 deletions .github/workflows/contract-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,17 @@ jobs:
- name: Ensure testnet secret exists
run: |
if [ -z "${STELLAR_SECRET_KEY}" ]; then
echo "::error::Missing STELLAR_TESTNET_SECRET_KEY repository secret"
exit 1
echo "::warning::Skipping testnet smoke test because STELLAR_TESTNET_SECRET_KEY is unavailable"
fi

- name: Setup Rust
if: env.STELLAR_SECRET_KEY != ''
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown

- name: Cache Cargo and Soroban
if: env.STELLAR_SECRET_KEY != ''
uses: actions/cache@v4
with:
path: |
Expand All @@ -54,24 +55,28 @@ jobs:
contract-smoke-${{ runner.os }}-

- name: Install Soroban CLI
run: cargo install --locked soroban-cli
if: env.STELLAR_SECRET_KEY != ''
run: cargo install --locked soroban-cli --version 25.2.0

- name: Install cargo-deny
if: env.STELLAR_SECRET_KEY != ''
run: cargo install --locked cargo-deny

- name: Audit Rust dependencies
if: env.STELLAR_SECRET_KEY != ''
working-directory: ./contracts
run: |
cargo generate-lockfile
cargo deny check

- name: Build contract wasm
if: env.STELLAR_SECRET_KEY != ''
working-directory: ./contracts
run: cargo build --target wasm32-unknown-unknown --release

- name: Run benchmarks
working-directory: ./contracts
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request' && env.STELLAR_SECRET_KEY != ''
run: |
cargo test benchmark_ -- --nocapture > bench_output.txt || true
python3 -c '
Expand All @@ -96,19 +101,20 @@ jobs:

- name: Upload machine-readable benchmark JSON
uses: actions/upload-artifact@v4
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request' && env.STELLAR_SECRET_KEY != ''
with:
name: bench-results-json
path: contracts/bench_results.json

- name: Post benchmark summary
uses: marocchino/sticky-pull-request-comment@v2
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request' && env.STELLAR_SECRET_KEY != ''
with:
header: benchmark
path: contracts/bench_summary.md

- name: Deploy and initialize on testnet
if: env.STELLAR_SECRET_KEY != ''
working-directory: ./contracts
run: |
soroban network add testnet --global \
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/dep-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ jobs:

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: 1.96.0

- name: Install cargo-audit
run: cargo install cargo-audit
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/install-script.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
targets: wasm32-unknown-unknown

- name: Install Soroban CLI
run: cargo install --locked soroban-cli
run: cargo install --locked soroban-cli --version 25.2.0

- name: Verify install.sh
run: bash ./install.sh
5 changes: 0 additions & 5 deletions .github/workflows/performance-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15

services:
sqlite:
image: noop
# SQLite runs embedded, no service needed

steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: |
if echo "$PR_BODY" | grep -qiE 'Fixes #[0-9]+|Rationale for no issue'; then
if echo "$PR_BODY" | grep -qiE '(Fixes|Closes|Resolves) #[0-9]+|Rationale for no issue'; then
echo "PR links to an issue or provides rationale."
else
echo "ERROR: This PR must link to an issue (e.g. 'Fixes #123') or provide a rationale in the 'Rationale for no issue' section."
Expand Down
116 changes: 116 additions & 0 deletions .github/workflows/sbom.yml
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]
Comment on lines +9 to +11

Copy link
Copy Markdown
Contributor

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:

#!/usr/bin/env bash
set -euo pipefail

fd -t f -e yml -e yaml .github/workflows \
  -x rg -n -C 6 'release-please|token:|GITHUB_TOKEN|PAT|app'

Repository: ritik4ever/stellar-portfolio-rebalancer

Length of output: 551


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== workflow files =="
fd . .github/workflows -t f -e yml -e yaml || true

echo
echo "== release-please/token/event references =="
rg -n -C 5 'release-please|GITHUB_TOKEN|token:|token=|app-id|installation-id|release:|pull_request_review' .github/workflows || true

echo
echo "== all release-ish configs =="
rg -n -C 4 'release-please|release-please-action|release:\s*published|tag-output|pull-request' -S . --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' || true

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:


Make the release SBOM run reachable after release creation.

.github/workflows/release-please.yml uses googleapis/release-please-action@v4 without a token, so it defaults to GITHUB_TOKEN and the subsequent release: published event 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 using steps.release.outputs.release_created / tag_name by manual dispatch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/sbom.yml around lines 9 - 11, Update the release-triggered
SBOM flow around the release-please action so SBOM generation remains reachable
after a release is created: either configure a release-please token with
permission to trigger downstream workflows, or invoke the SBOM workflow from the
release-please workflow using steps.release.outputs.release_created and tag_name
via manual dispatch. Preserve the existing published-release behavior and use
the established release outputs.


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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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
PY

Repository: 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 persist-credentials: false on the actions/checkout@v4 step so the token is not left in .git/config.

Proposed fix
       - uses: actions/checkout@v4
+        with:
+          persist-credentials: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
persist-credentials: false
🧰 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 Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/sbom.yml at line 26, Update the actions/checkout@v4 step
in the SBOM workflow to set persist-credentials to false, ensuring the checkout
token is not retained in the repository’s Git configuration.

Source: 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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"
done

Repository: 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 -200

Repository: ritik4ever/stellar-portfolio-rebalancer

Length of output: 36132


Ensure generated SBOM artifacts match CycloneDX 1.6 before upload.

CHANGELOG.md advertises CycloneDX 1.6 JSON artifacts, but the workflow generates 1.5 and security/SBOM.md documents 1.5. Generate the artifacts with CycloneDX 1.6 and validate each file's specVersion before upload, then align the CHANGELOG.md and security/SBOM.md consumer documentation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/sbom.yml around lines 72 - 79, Update the SBOM generation
steps in the workflow to produce CycloneDX 1.6 JSON, validate every generated
artifact’s specVersion before the upload step, and align the corresponding
CHANGELOG.md and security/SBOM.md documentation with 1.6.


# ----- 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
Comment thread
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
15 changes: 13 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -236,5 +236,16 @@ monitoring/logs/
# Generated output files
backend/test_results.txt
frontend/tsc-output.txt

*storybook.log

*storybook.log

# Generated SBOM artifacts (per-build). The directory's tracked placeholder
# `security/sbom/.gitkeep` keeps the directory present in fresh clones; the
# generated SBOM JSON contents themselves are reproducible per build and are
# emitted as GitHub workflow artifacts named `sbom-{frontend,backend,contracts}`.
# The two-line re-include pattern below is the standard way to ignore a
# directory's *contents* while still keeping one tracker file.
# (Without it, `/security/sbom/` (trailing slash) would also ignore the
# .gitkeep placeholder, breaking the "dir is intentionally tracked" signal.)
/security/sbom/*
!/security/sbom/.gitkeep
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Release changelog entries are generated into these sections:
- Created `docs/ROADMAP.md` with detailed project roadmap
- Added roadmap summary table to `README.md` for quick reference

- GitHub Actions build attestations for frontend and backend release bundles, plus CycloneDX SBOM artifacts for frontend, backend, and contracts.
- GitHub Actions build attestations for frontend and backend release bundles, plus a dedicated SBOM Generation workflow (`.github/workflows/sbom.yml`) that publishes CycloneDX 1.6 JSON SBOMs for `frontend`, `backend`, and `contracts` as GitHub workflow artifacts on every PR, push to `main`, and `release: published`. Local equivalents are wired behind `npm run sbom`, `sbom:contracts`, `sbom:backend`, `sbom:frontend` plus a `make sbom` target in `contracts/`, with the full consumer guide under [`security/SBOM.md`](security/SBOM.md).
- A repository-level npm audit baseline and CI policy gate, with a backend-local wrapper command for maintainers.
- A reusable release checklist template for contract, backend, and frontend releases, together with a contract Makefile helper that points to it.
- Replay-focused idempotency tests for cached success/error responses, cross-user key rejection, and expiry cleanup paths.
Expand Down
Loading
Loading