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
48 changes: 48 additions & 0 deletions .github/actions/build/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: "Build"
description: "Sets up environment and builds the WASM package"

inputs:
node-version:
description: "Node.js version to install"
required: false
default: "24"
setup-npm-registry:
description: "Setup NPM registry URL"
required: false
default: "false"

runs:
using: "composite"
steps:
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: ${{ inputs.node-version }}
registry-url: ${{ inputs.setup-npm-registry == 'true' && 'https://registry.npmjs.org' || '' }}

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown

- name: Rust cache
uses: Swatinem/rust-cache@v2

- name: Install cargo-binstall
uses: cargo-bins/cargo-binstall@main
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.

Unpinned third-party action in release publish pipeline

Medium Severity

cargo-bins/cargo-binstall@main references a mutable branch rather than a commit SHA, unlike the other actions in this pipeline (e.g., actions/checkout and actions/setup-node are SHA-pinned). Since this composite action is used in the npm-publish.yml workflow that has id-token: write permission and publishes to npm with provenance, a compromised main branch in the cargo-binstall repo could inject malicious code into the published package. At minimum, the version input available on this action could be set to pin the installed binary version.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 059ac8f. Configure here.


- name: Install wasm-pack
shell: bash
run: cargo binstall wasm-pack -y

- name: Install just
uses: extractions/setup-just@v2

- name: Install npm dependencies
shell: bash
working-directory: ./wasm
run: npm install

- name: Build WASM
shell: bash
run: just build-wasm
Comment thread
cursor[bot] marked this conversation as resolved.
93 changes: 93 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Publishes @bulletxyz/sdk-wasm to npm. Triggered by the `v<version>` git tag
# release-plz creates after a release PR is merged (see release-plz.yml).
# release-plz.toml pins this to a single workspace-wide `v<version>` tag rather
# than per-crate tags, so both crates ship under one release.
#
# The version is already baked into wasm/Cargo.toml and wasm/package.json at this
# point, so this workflow only builds and publishes — no version bump, no commits.
#
# Authentication uses npm OIDC Trusted Publishers; configure at:
# https://www.npmjs.com/package/@bulletxyz/sdk-wasm/access
# See: https://docs.npmjs.com/generating-provenance-statements#publishing-packages-with-provenance-via-github-actions

name: NPM Publish

on:
push:
tags:
- "v*"
Comment thread
cursor[bot] marked this conversation as resolved.
workflow_dispatch:
inputs:
tag:
description: "Existing git tag to publish (e.g. v0.0.14)"
required: true
type: string

env:
CARGO_TERM_COLOR: always
CARGO_NET_GIT_FETCH_WITH_CLI: true
CI: 1

concurrency:
group: npm-publish
cancel-in-progress: false

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Resolve ref
id: ref
env:
INPUT_TAG: ${{ inputs.tag }}
PUSH_REF: ${{ github.ref }}
run: |
if [ -n "${INPUT_TAG:-}" ]; then
echo "ref=${INPUT_TAG}" >> "$GITHUB_OUTPUT"
else
echo "ref=${PUSH_REF}" >> "$GITHUB_OUTPUT"
fi

- name: Checkout tag
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ steps.ref.outputs.ref }}
persist-credentials: false
fetch-depth: 1

- name: Verify tag matches Cargo + package.json versions
id: version
env:
REF: ${{ steps.ref.outputs.ref }}
run: |
set -euo pipefail
TAG="${REF#refs/tags/}"
V="${TAG#v}"
if ! echo "$V" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
echo "::error::Tag '$TAG' is not a valid semver tag" >&2
exit 1
fi
CARGO_V=$(grep -m1 '^version' wasm/Cargo.toml | sed -E 's/.*"([^"]+)".*/\1/')
PKG_V=$(node -p "require('./wasm/package.json').version")
if [ "$V" != "$CARGO_V" ] || [ "$V" != "$PKG_V" ]; then
echo "::error::Version mismatch — tag=$V wasm/Cargo.toml=$CARGO_V wasm/package.json=$PKG_V" >&2
exit 1
fi
echo "version=$V" >> "$GITHUB_OUTPUT"

- name: Build
uses: ./.github/actions/build
with:
setup-npm-registry: "true"

- name: Publish to npm
working-directory: wasm
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
TAG=$(echo "$VERSION" | grep -q '-' && echo "rc" || echo "latest")
npm publish --provenance --tag "$TAG" --access public
47 changes: 45 additions & 2 deletions .github/workflows/release-plz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,58 @@ jobs:
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
persist-credentials: false
persist-credentials: true

- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable

- name: Authenticate with crates.io
id: cargo-auth
uses: rust-lang/crates-io-auth-action@bbd81622f20ce9e2dd9622e3218b975523e45bbe # v1.0.4

- uses: release-plz/action@1528104d2ca23787631a1c1f022abb64b34c1e11 # v0.5.128
- name: Run release-plz
id: release-plz
uses: release-plz/action@1528104d2ca23787631a1c1f022abb64b34c1e11 # v0.5.128
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
CARGO_REGISTRY_TOKEN: ${{ steps.cargo-auth.outputs.token }}

# release-plz bumps Rust crate versions in Cargo.toml, but doesn't know about
# wasm/package.json. Mirror the new wasm/Cargo.toml version onto the same
# release PR so the Rust crate and the @bulletxyz/sdk-wasm npm package stay
# in lockstep. The actual `npm publish` is handled by npm-publish.yml,
# which is triggered by the v<version> tag release-plz creates after merge.
- name: Sync wasm/package.json to release PR
if: ${{ steps.release-plz.outputs.pr != '' }}
env:
PR_JSON: ${{ steps.release-plz.outputs.pr }}
run: |
set -euo pipefail

BRANCH=$(echo "$PR_JSON" | jq -r '.head_branch')
if [ -z "$BRANCH" ] || [ "$BRANCH" = "null" ]; then
echo "No head_branch in release PR output; skipping package.json sync"
exit 0
fi

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

git fetch origin "$BRANCH"
git checkout "$BRANCH"

# cargo pkgid outputs "path+file:///…/wasm#bullet-rust-sdk-wasm@0.0.14" on
# modern Cargo, or "…#0.0.14" on older versions. Take whatever follows the
# final '#' or '@'.
V=$(cargo pkgid -p bullet-rust-sdk-wasm | sed -E 's/^.*[#@]([^#@]+)$/\1/')
echo "Syncing wasm/package.json to version $V"

(cd wasm && npm version "$V" --no-git-tag-version --allow-same-version)

if git diff --quiet -- wasm/package.json; then
echo "wasm/package.json already at $V; nothing to commit"
exit 0
fi

git add wasm/package.json
git commit -m "chore: sync wasm/package.json to $V"
git push origin "$BRANCH"
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
[workspace]
members = ["rust", "wasm"]
resolver = "2"
resolver = "3"

[workspace.package]
edition = "2024"
license = "MIT"
repository = "https://github.com/bulletxyz/bullet-rust-sdk"

[workspace.dependencies]
bon = "3.9.0"
Expand Down
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
packages = [
rust
pkgs.cargo-nextest
pkgs.cargo-edit
pkgs.just
(makeWasmPack pkgs)
pkgs.pkg-config
Expand Down
19 changes: 15 additions & 4 deletions release-plz.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,21 @@ commit_parsers = [
{ message = "^build", skip = true },
]

# wasm crate ships to npm as @bulletxyz/sdk-wasm via a separate workflow_dispatch
# flow; release-plz only manages the Rust crate.
# `publish = false` mirrors wasm/Cargo.toml so release-plz's consistency check passes.
# Force a single shared `v<version>` tag for the whole release. By default
# release-plz uses per-package `<crate>-v<version>` tags in multi-package
# workspaces; we override that on the Rust crate and disable tag/release
# creation for the wasm crate entirely. The wasm crate still gets its version
# bumped in lockstep with the Rust crate (it path-depends on it) and its
# `npm publish` is driven from the same `v<version>` tag by npm-publish.yml.
[[package]]
name = "bullet-rust-sdk"
git_tag_name = "v{{ version }}"

# wasm crate ships to npm as @bulletxyz/sdk-wasm (not crates.io). Skip
# crates.io publish, and skip the separate git tag / GitHub release so we
# don't get a duplicate `bullet-rust-sdk-wasm-v<version>` tag.
[[package]]
name = "bullet-rust-sdk-wasm"
release = false
publish = false
git_tag_enable = false
git_release_enable = false
6 changes: 3 additions & 3 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "bullet-rust-sdk"
version = "0.0.13"
edition = "2024"
license = "MIT"
edition.workspace = true
license.workspace = true
repository.workspace = true
description = "Rust SDK for the Bullet trading platform"
homepage = "https://www.bullet.xyz"
repository = "https://github.com/bulletxyz/bullet-rust-sdk"
readme = "../README.md"
keywords = ["bullet", "trading", "exchange", "sdk"]
categories = ["api-bindings", "cryptography::cryptocurrencies"]
Expand Down
6 changes: 3 additions & 3 deletions wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "bullet-rust-sdk-wasm"
version = "0.0.13"
edition = "2024"
license = "MIT"
edition.workspace = true
license.workspace = true
repository.workspace = true
description = "WebAssembly bindings for the Bullet trading SDK"
repository = "https://github.com/bulletxyz/bullet-rust-sdk"
publish = false # ships to npm as @bulletxyz/sdk-wasm, not crates.io

[lib]
Expand Down
4 changes: 3 additions & 1 deletion wasm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
],
"type": "module",
"scripts": {
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
"build": "cd .. && just build-wasm",
"prepublishOnly": "[ -n \"$CI\" ] || npm run build"
},
"devDependencies": {
"@jest/globals": "30.3.0",
Expand Down