Skip to content
Merged
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
129 changes: 108 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ jobs:
shared: ${{ steps.filter.outputs.shared }}
slo: ${{ steps.filter.outputs.slo }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- uses: dorny/paths-filter@v3
- uses: dorny/paths-filter@b41dfa943b1939b9b646f67753bfe35cf6e4de03 # v3.0.2
id: filter
with:
# On push events, compare against the previous commit so the first
Expand Down Expand Up @@ -69,11 +69,11 @@ jobs:
if: needs.changes.outputs.website == 'true' || needs.changes.outputs.shared == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v4.1.0

- uses: actions/setup-node@v4
- uses: actions/setup-node@e51e5fe84fc33b4c73ebe40526b2694712b5b858 # v4.3.0
with:
node-version: 20
cache: pnpm
Expand All @@ -96,9 +96,9 @@ jobs:
run:
working-directory: apps/intelligence
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- uses: actions/setup-python@v5
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v5.5.0
with:
python-version: "3.12"

Expand All @@ -122,11 +122,11 @@ jobs:
if: needs.changes.outputs.dapp == 'true' || needs.changes.outputs.shared == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v4.1.0

- uses: actions/setup-node@v4
- uses: actions/setup-node@e51e5fe84fc33b4c73ebe40526b2694712b5b858 # v4.3.0
with:
node-version: 22
cache: pnpm
Expand Down Expand Up @@ -279,7 +279,7 @@ jobs:
needs: changes
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0

Expand All @@ -292,7 +292,7 @@ jobs:
- name: Run gitleaks
run: gitleaks detect --source . --no-git --redact --no-banner --config .gitleaks.toml

- uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
if: needs.changes.outputs.contracts == 'true' || needs.changes.outputs.shared == 'true'

- name: Install cargo-audit
Expand All @@ -304,7 +304,7 @@ jobs:
run: cargo audit
working-directory: packages/contracts

- uses: actions/setup-go@v5
- uses: actions/setup-go@5064d95ba18d4a9dda7c4cc67adeb03aea1a2a14 # v5.3.0
if: needs.changes.outputs.api == 'true' || needs.changes.outputs.shared == 'true'
with:
go-version-file: apps/api/go.mod
Expand All @@ -327,10 +327,15 @@ jobs:
OUTPUT=$(govulncheck ./... 2>&1)
EXIT_CODE=$?
echo "$OUTPUT"

# Validate .vulnignore unconditionally: an expired, malformed, or
# ownerless waiver fails the job regardless of scan results. The
# printed accepted IDs are used to filter findings below.
ACCEPTED=$(python3 "${{ github.workspace }}/scripts/validate_vulnignore.py" "${{ github.workspace }}/.vulnignore") || exit 1

if [ $EXIT_CODE -eq 0 ]; then
exit 0
elif [ $EXIT_CODE -eq 3 ]; then
ACCEPTED="GO-2026-4316 GO-2026-5004 GO-2026-5037 GO-2026-5039"
UNKNOWN=$(echo "$OUTPUT" | grep -oE 'GO-[0-9]{4}-[0-9]+' | sort -u | while read id; do
echo "$ACCEPTED" | grep -qw "$id" || echo "$id"
done)
Expand Down Expand Up @@ -363,10 +368,10 @@ jobs:
run: gosec -severity medium ./...
working-directory: apps/api

- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v4.1.0
if: needs.changes.outputs.dapp == 'true' || needs.changes.outputs.shared == 'true'

- uses: actions/setup-node@v4
- uses: actions/setup-node@e51e5fe84fc33b4c73ebe40526b2694712b5b858 # v4.3.0
if: needs.changes.outputs.dapp == 'true' || needs.changes.outputs.shared == 'true'
with:
node-version: 22
Expand All @@ -390,7 +395,7 @@ jobs:
run: pnpm audit --audit-level=high || true
working-directory: apps/dapp/frontend

- uses: actions/setup-python@v5
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v5.5.0
if: needs.changes.outputs.intelligence == 'true' || needs.changes.outputs.shared == 'true'
with:
python-version: "3.12"
Expand Down Expand Up @@ -485,6 +490,23 @@ jobs:
echo "✓ Semgrep cloud scanning enabled"
fi

- name: Typosquat detection (new npm dependencies)
if: needs.changes.outputs.dapp == 'true' || needs.changes.outputs.shared == 'true'
# A fuzzy name-similarity heuristic must not be able to block an
# unrelated PR from merging; findings surface as warnings for manual
# review. See scripts/typosquat_check.py for the logic.
continue-on-error: true
env:
BASE_REF: ${{ github.event.pull_request.base.sha || github.event.before }}
run: |
# Detect typosquatting patterns in npm packages newly added to the
# dapp manifest. Compares the base manifest (merge base for PRs, the
# previous commit for pushes) against the head manifest and checks only
# dependencies added in this change set, using full scoped package
# names so a spoofed @attacker/react cannot hide behind a name
# collision with the unscoped "react".
python3 scripts/typosquat_check.py apps/dapp/frontend/package.json "$BASE_REF"

api:
name: API (Go)
needs: changes
Expand Down Expand Up @@ -526,9 +548,9 @@ jobs:
# silently skips in CI even though the redis service below is running.
REDIS_ADDR: localhost:6379
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- uses: actions/setup-go@v5
- uses: actions/setup-go@5064d95ba18d4a9dda7c4cc67adeb03aea1a2a14 # v5.3.0
with:
go-version-file: apps/api/go.mod
cache: true
Expand Down Expand Up @@ -598,9 +620,9 @@ jobs:
run:
working-directory: packages/contracts
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
with:
targets: wasm32-unknown-unknown

Expand Down Expand Up @@ -653,3 +675,68 @@ jobs:
echo "ERROR: No vault-contract tests ran — possible missing WASM artifact"
exit 1
fi

sbom:
name: SBOM Generation
needs: changes
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- uses: actions/setup-go@5064d95ba18d4a9dda7c4cc67adeb03aea1a2a14 # v5.3.0
with:
go-version-file: apps/api/go.mod

- name: Install cyclonedx-gomod (Go, pinned release)
run: go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@v1.10.0

- name: Install syft (pinned release, checksum-verified)
run: |
SYFT_VERSION=1.50.0
ASSET="syft_${SYFT_VERSION}_linux_amd64.tar.gz"
curl -sSfL -o "/tmp/${ASSET}" \
"https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/${ASSET}"
curl -sSfL -o "/tmp/syft_${SYFT_VERSION}_checksums.txt" \
"https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/syft_${SYFT_VERSION}_checksums.txt"
cd /tmp
grep " ${ASSET}$" "syft_${SYFT_VERSION}_checksums.txt" | sha256sum -c -
tar -xzf "${ASSET}"
sudo mv /tmp/syft /usr/local/bin/syft

- name: Generate Go API SBOM
if: needs.changes.outputs.api == 'true' || needs.changes.outputs.shared == 'true'
working-directory: apps/api
run: |
cyclonedx-gomod mod -json -output ../../sbom-api.json .
syft dir:. --output cyclonedx-json=../../sbom-api-syft.json

- name: Generate frontend SBOM (npm/pnpm)
if: needs.changes.outputs.dapp == 'true' || needs.changes.outputs.shared == 'true'
working-directory: apps/dapp/frontend
run: |
# syft catalogs the pnpm lockfile directly. @cyclonedx/cyclonedx-npm
# dropped --pnpm support in v6, so the already-checksum-verified syft
# binary (installed above) is used for the frontend SBOM.
syft dir:. --output cyclonedx-json=../../../sbom-frontend.json

- name: Generate Python SBOM
if: needs.changes.outputs.intelligence == 'true' || needs.changes.outputs.shared == 'true'
working-directory: apps/intelligence
run: |
syft dir:. --output cyclonedx-json=../../sbom-intelligence.json

- name: Generate contracts SBOM (Rust)
if: needs.changes.outputs.contracts == 'true' || needs.changes.outputs.shared == 'true'
working-directory: packages/contracts
run: |
syft dir:. --output cyclonedx-json=../../sbom-contracts.json

- name: Upload SBOM artifacts
# Only run when at least one SBOM-covered component changed; a docs- or
# website-only change generates no SBOMs and must not fail the job.
if: needs.changes.outputs.api == 'true' || needs.changes.outputs.dapp == 'true' || needs.changes.outputs.intelligence == 'true' || needs.changes.outputs.contracts == 'true' || needs.changes.outputs.shared == 'true'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: sbom-artifacts
path: sbom-*.json
if-no-files-found: error
6 changes: 3 additions & 3 deletions .github/workflows/contract-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
name: x402 Heuristic Contract Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Make audit script executable
run: chmod +x scripts/contract-audit.sh
Expand Down Expand Up @@ -81,14 +81,14 @@ jobs:
cat ../../wasm-hashes/wasm-hashes.txt

- name: Upload WASM Hashes Artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: contract-wasm-hashes
path: wasm-hashes/wasm-hashes.txt

- name: Upload audit reports
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: contract-audit-reports
path: audit-report-*.json
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
steps:
- name: Fetch Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2
uses: dependabot/fetch-metadata@7a4b92f93a688100997537756415299e15fc2431 # v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/load-soak.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ jobs:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
# The checked-out workspace is mounted into the k6 container; do not
# leave the default GITHUB_TOKEN in .git/config for test code to read.
persist-credentials: false

# A missing secret must never cause a test to fall back to localhost or
# production. The `secrets` context is not available in a job-level `if`,
Expand Down Expand Up @@ -46,7 +50,7 @@ jobs:
-e API_BASE_URL -e INTELLIGENCE_BASE_URL -e WS_URL -e VAULT_ID -e AUTH_TOKEN \
-v "$PWD:/work" -w /work grafana/k6:latest \
run --summary-export results/soak-summary.json tests/load/soak.js
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: always()
with:
name: k6-soak-summary-${{ github.run_id }}
Expand Down
Loading
Loading