From d74edc9e2a74f706764fc8342b3c0f1633792359 Mon Sep 17 00:00:00 2001 From: Jo D Date: Wed, 20 May 2026 09:46:50 -0400 Subject: [PATCH 1/6] ci: ignore dependabot patch updates --- .github/dependabot.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 577de0d..c0e7f4f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,13 +7,15 @@ updates: open-pull-requests-limit: 10 cooldown: default-days: 7 - semver-patch-days: 1 + ignore: + - dependency-name: '*' + update-types: + - version-update:semver-patch groups: cargo-non-major: applies-to: version-updates update-types: - minor - - patch - package-ecosystem: npm directory: '/' @@ -22,13 +24,15 @@ updates: open-pull-requests-limit: 10 cooldown: default-days: 7 - semver-patch-days: 1 + ignore: + - dependency-name: '*' + update-types: + - version-update:semver-patch groups: npm-non-major: applies-to: version-updates update-types: - minor - - patch - package-ecosystem: github-actions directory: '/' @@ -36,6 +40,10 @@ updates: interval: weekly cooldown: default-days: 7 + ignore: + - dependency-name: '*' + update-types: + - version-update:semver-patch groups: actions: patterns: From c5b98dc7e665d8cfc397462b7283c2140db3a3c2 Mon Sep 17 00:00:00 2001 From: Jo D Date: Thu, 21 May 2026 09:13:32 -0400 Subject: [PATCH 2/6] ci: add dependency audits and miri --- .github/actions/setup/action.yml | 37 ++++++++++++++++-- .github/dependabot.yml | 4 +- .github/workflows/security.yml | 64 ++++++++++++++++++++++++++++++++ package.json | 8 +++- pnpm-lock.yaml | 24 ++---------- 5 files changed, 111 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/security.yml diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index d004dd8..9accaf9 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -2,6 +2,18 @@ name: 'Setup CI Environment' description: 'Shared setup for Rust, Solana, pnpm, and optional surfpool' inputs: + install-rust: + description: 'Install Rust toolchain' + required: false + default: 'true' + rust-toolchain: + description: 'Rust toolchain to install' + required: false + default: '1.92' + rust-components: + description: 'Rust components to install' + required: false + default: 'rustfmt, clippy' enable-rust-cache: description: 'Enable Rust caching (restore/save)' required: false @@ -30,18 +42,31 @@ inputs: description: "Solana CLI version to install (e.g., 'v4.0.0')" required: false default: 'v4.0.0' + install-just: + description: 'Install just' + required: false + default: 'true' + install-pnpm: + description: 'Install pnpm and Node.js' + required: false + default: 'true' + install-pnpm-dependencies: + description: 'Install pnpm dependencies' + required: false + default: 'true' runs: using: 'composite' steps: - name: Setup Rust + if: inputs.install-rust == 'true' uses: dtolnay/rust-toolchain@master with: - toolchain: '1.92' - components: rustfmt, clippy + toolchain: ${{ inputs.rust-toolchain }} + components: ${{ inputs.rust-components }} - name: Rust cache - if: inputs.enable-rust-cache == 'true' + if: inputs.install-rust == 'true' && inputs.enable-rust-cache == 'true' uses: Swatinem/rust-cache@v2 with: workspaces: '. -> target' @@ -93,24 +118,29 @@ runs: run: echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Install just + if: inputs.install-just == 'true' uses: extractions/setup-just@v4 with: just-version: '1.50.0' - name: Install pnpm + if: inputs.install-pnpm == 'true' uses: pnpm/action-setup@v4 - name: Setup Node.js + if: inputs.install-pnpm == 'true' uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' - name: Get pnpm store directory + if: inputs.install-pnpm == 'true' id: pnpm-store shell: bash run: echo "path=$(pnpm store path)" >> $GITHUB_OUTPUT - name: Cache pnpm store + if: inputs.install-pnpm == 'true' id: pnpm-cache uses: actions/cache@v4 with: @@ -120,6 +150,7 @@ runs: ${{ runner.os }}-pnpm- - name: Install pnpm dependencies + if: inputs.install-pnpm == 'true' && inputs.install-pnpm-dependencies == 'true' shell: bash run: pnpm install --frozen-lockfile diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c0e7f4f..b7f10b5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,7 +3,7 @@ updates: - package-ecosystem: cargo directory: '/' schedule: - interval: daily + interval: weekly open-pull-requests-limit: 10 cooldown: default-days: 7 @@ -20,7 +20,7 @@ updates: - package-ecosystem: npm directory: '/' schedule: - interval: daily + interval: weekly open-pull-requests-limit: 10 cooldown: default-days: 7 diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..9840cf1 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,64 @@ +name: Security + +on: + push: + branches: [main] + pull_request: + schedule: + - cron: '0 9 * * *' + workflow_dispatch: + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + +jobs: + cargo-audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup + with: + install-solana: 'false' + install-just: 'false' + install-pnpm: 'false' + rust-cache-key: 'cargo-audit' + - uses: taiki-e/install-action@v2 + with: + tool: cargo-audit + - run: cargo audit + + pnpm-audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup + with: + install-rust: 'false' + enable-rust-cache: 'false' + install-solana: 'false' + install-just: 'false' + install-pnpm-dependencies: 'false' + - run: pnpm audit --ignore GHSA-3gc7-fjrx-p6mg + + miri: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup + with: + rust-toolchain: 'nightly' + rust-components: 'miri' + install-solana: 'false' + install-just: 'false' + install-pnpm: 'false' + rust-cache-key: 'miri' + - uses: actions/cache@v5 + with: + path: | + ~/.cache/org.rust-lang.miri + key: miri-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} + - run: cargo +nightly miri setup + - run: cargo +nightly miri test -p subscriptions-program diff --git a/package.json b/package.json index 0a4ae85..b7e6e5b 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,13 @@ "flatted": "3.4.2", "minimatch": "9.0.9", "picomatch": "4.0.4", - "rollup": "4.60.2" + "rollup": "4.60.2", + "ws@^8.0.0": "8.20.1" + }, + "auditConfig": { + "ignoreCves": [ + "GHSA-3gc7-fjrx-p6mg" + ] } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f8ccfd0..69f9419 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,7 @@ overrides: minimatch: 9.0.9 picomatch: 4.0.4 rollup: 4.60.2 + ws@^8.0.0: 8.20.1 importers: @@ -2584,7 +2585,7 @@ packages: engines: {node: '>=20.18.0'} peerDependencies: typescript: '>=5.3.3' - ws: ^8.18.0 + ws: 8.20.1 '@solana/rpc-subscriptions-channel-websocket@5.5.1': resolution: {integrity: sha512-7tGfBBrYY8TrngOyxSHoCU5shy86iA9SRMRrPSyBhEaZRAk6dnbdpmUTez7gtdVo0BCvh9nzQtUycKWSS7PnFQ==} @@ -4544,7 +4545,7 @@ packages: isomorphic-ws@4.0.1: resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} peerDependencies: - ws: '*' + ws: 8.20.1 istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} @@ -6174,18 +6175,6 @@ packages: utf-8-validate: optional: true - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@8.20.1: resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} engines: {node: '>=10.0.0'} @@ -12889,7 +12878,7 @@ snapshots: buffer: 6.0.3 eventemitter3: 5.0.4 uuid: 8.3.2 - ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: bufferutil: 4.1.0 utf-8-validate: 5.0.10 @@ -13496,11 +13485,6 @@ snapshots: bufferutil: 4.1.0 utf-8-validate: 5.0.10 - ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.1.0 - utf-8-validate: 5.0.10 - ws@8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.1.0 From b30a944dc5ed20f8b2905a0822c4127e9c957960 Mon Sep 17 00:00:00 2001 From: Jo D Date: Thu, 21 May 2026 09:53:39 -0400 Subject: [PATCH 3/6] ci: gate pnpm audit on fixable advisories --- .github/workflows/security.yml | 39 +++++++++++++++++++++++++++++++++- package.json | 5 ----- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 9840cf1..b087676 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -41,7 +41,44 @@ jobs: install-solana: 'false' install-just: 'false' install-pnpm-dependencies: 'false' - - run: pnpm audit --ignore GHSA-3gc7-fjrx-p6mg + - run: | + report="${RUNNER_TEMP:-/tmp}/pnpm-audit.json" + set +e + pnpm audit --json > "$report" + audit_status=$? + set -e + if [ "$audit_status" -eq 0 ]; then + exit 0 + fi + node - "$report" <<'NODE' + const fs = require("node:fs"); + + const report = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); + const advisories = Object.values(report.advisories ?? {}); + if (advisories.length === 0) { + process.exit(1); + } + + const isFixable = (advisory) => { + const patchedVersions = advisory.patched_versions; + return patchedVersions && patchedVersions !== "<0.0.0"; + }; + const fixable = advisories.filter(isFixable); + + for (const advisory of advisories.filter((advisory) => !isFixable(advisory))) { + const id = advisory.github_advisory_id ?? advisory.cves?.[0] ?? advisory.id; + console.log(`Ignoring unfixable advisory ${id}`); + } + + if (fixable.length > 0) { + console.error("Fixable pnpm advisories found:"); + for (const advisory of fixable) { + const id = advisory.github_advisory_id ?? advisory.cves?.[0] ?? advisory.id; + console.error(`- ${id}: ${advisory.module_name} patched by ${advisory.patched_versions}`); + } + process.exit(1); + } + NODE miri: runs-on: ubuntu-latest diff --git a/package.json b/package.json index b7e6e5b..c95c55d 100644 --- a/package.json +++ b/package.json @@ -44,11 +44,6 @@ "picomatch": "4.0.4", "rollup": "4.60.2", "ws@^8.0.0": "8.20.1" - }, - "auditConfig": { - "ignoreCves": [ - "GHSA-3gc7-fjrx-p6mg" - ] } } } From 5ae9d2abaaece0ec85dd5a4e6be3661c1b2da8f3 Mon Sep 17 00:00:00 2001 From: Jo D Date: Thu, 21 May 2026 09:57:49 -0400 Subject: [PATCH 4/6] ci: simplify pnpm audit policy --- .github/workflows/security.yml | 39 +--------------------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index b087676..e9d7c69 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -41,44 +41,7 @@ jobs: install-solana: 'false' install-just: 'false' install-pnpm-dependencies: 'false' - - run: | - report="${RUNNER_TEMP:-/tmp}/pnpm-audit.json" - set +e - pnpm audit --json > "$report" - audit_status=$? - set -e - if [ "$audit_status" -eq 0 ]; then - exit 0 - fi - node - "$report" <<'NODE' - const fs = require("node:fs"); - - const report = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); - const advisories = Object.values(report.advisories ?? {}); - if (advisories.length === 0) { - process.exit(1); - } - - const isFixable = (advisory) => { - const patchedVersions = advisory.patched_versions; - return patchedVersions && patchedVersions !== "<0.0.0"; - }; - const fixable = advisories.filter(isFixable); - - for (const advisory of advisories.filter((advisory) => !isFixable(advisory))) { - const id = advisory.github_advisory_id ?? advisory.cves?.[0] ?? advisory.id; - console.log(`Ignoring unfixable advisory ${id}`); - } - - if (fixable.length > 0) { - console.error("Fixable pnpm advisories found:"); - for (const advisory of fixable) { - const id = advisory.github_advisory_id ?? advisory.cves?.[0] ?? advisory.id; - console.error(`- ${id}: ${advisory.module_name} patched by ${advisory.patched_versions}`); - } - process.exit(1); - } - NODE + - run: pnpm audit --ignore-unfixable miri: runs-on: ubuntu-latest From 7e43e10ac979f2217bc2d538bc99da0b7313e925 Mon Sep 17 00:00:00 2001 From: Jo D Date: Thu, 21 May 2026 10:24:19 -0400 Subject: [PATCH 5/6] ci: generate clients before vercel build --- webapp/vercel.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/vercel.json b/webapp/vercel.json index abe61a7..ee6f76e 100644 --- a/webapp/vercel.json +++ b/webapp/vercel.json @@ -1,7 +1,7 @@ { "$schema": "https://openapi.vercel.sh/vercel.json", "framework": "vite", - "buildCommand": "pnpm --filter @solana/subscriptions build && pnpm --filter webapp build", + "buildCommand": "pnpm run generate-clients && pnpm --filter @solana/subscriptions build && pnpm --filter webapp build", "installCommand": "pnpm install --frozen-lockfile", "outputDirectory": "dist", "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] From b2344b74463eb5e0c48978700dccfab1be958f8f Mon Sep 17 00:00:00 2001 From: Jo D Date: Thu, 21 May 2026 10:31:35 -0400 Subject: [PATCH 6/6] ci: fix vercel client generation --- webapp/vercel.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/vercel.json b/webapp/vercel.json index ee6f76e..90b37e3 100644 --- a/webapp/vercel.json +++ b/webapp/vercel.json @@ -1,7 +1,7 @@ { "$schema": "https://openapi.vercel.sh/vercel.json", "framework": "vite", - "buildCommand": "pnpm run generate-clients && pnpm --filter @solana/subscriptions build && pnpm --filter webapp build", + "buildCommand": "pnpm -w run generate-ts-client && pnpm --filter @solana/subscriptions build && pnpm --filter webapp build", "installCommand": "pnpm install --frozen-lockfile", "outputDirectory": "dist", "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]