From cc1fcc7d440a25710e36884da25017da6ff32683 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 15 May 2026 14:44:38 +0000 Subject: [PATCH 1/5] fix(security): resolve CodeQL code-scanning alerts - js/insecure-randomness: replace Math.random() with Web Crypto CSPRNG for room-name word selection (room names gate access to a private voice room) and for guest-id generation in BurbleClient. - actions/code-injection: route rhodibot step outputs (derived from repository filenames / grep output) through env vars instead of interpolating ${{ steps.* }} directly into run/commit/PR-body shell. https://claude.ai/code/session_01JRto2WBmaeGukiC4EnbEHg --- .github/workflows/rhodibot.yml | 26 ++++++++++++++++---------- client/lib/src/BurbleClient.res | 10 +++++++++- client/lib/src/BurbleClient.res.mjs | 8 +++++++- client/web/src/room.js | 16 ++++++++++++++-- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/.github/workflows/rhodibot.yml b/.github/workflows/rhodibot.yml index 3b3b01f..78f165b 100644 --- a/.github/workflows/rhodibot.yml +++ b/.github/workflows/rhodibot.yml @@ -186,7 +186,7 @@ jobs: git add -A git commit -m "fix(rhodibot): automated RSR compliance fixes - ${{ steps.fix.outputs.FIXES }} + $FIXES Co-Authored-By: rhodibot " @@ -195,20 +195,20 @@ jobs: BODY="## 🤖 Rhodibot — RSR Compliance Fixes ### Changes Made - ${{ steps.fix.outputs.FIXES }} + $FIXES " - if [ -n "${{ steps.fix.outputs.ISSUES }}" ]; then + if [ -n "$ISSUES" ]; then BODY="$BODY ### Issues Found (manual fix needed) - ${{ steps.fix.outputs.ISSUES }} + $ISSUES " fi - if [ -n "${{ steps.fix.outputs.DANGEROUS }}" ]; then + if [ -n "$DANGEROUS" ]; then BODY="$BODY ### ⚠️ Dangerous Patterns Detected - ${{ steps.fix.outputs.DANGEROUS }} + $DANGEROUS _These bypass formal verification. See \`proven\` repo for alternatives._ " @@ -221,16 +221,22 @@ jobs: --head "$BRANCH" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FIXES: ${{ steps.fix.outputs.FIXES }} + ISSUES: ${{ steps.fix.outputs.ISSUES }} + DANGEROUS: ${{ steps.fix.outputs.DANGEROUS }} - name: Report (no changes needed) if: steps.fix.outputs.CHANGED != 'true' + env: + ISSUES: ${{ steps.fix.outputs.ISSUES }} + DANGEROUS: ${{ steps.fix.outputs.DANGEROUS }} run: | echo "✅ Repository is RSR-compliant. No fixes needed." - if [ -n "${{ steps.fix.outputs.ISSUES }}" ]; then + if [ -n "$ISSUES" ]; then echo "⚠️ Issues found (manual fix needed):" - echo -e "${{ steps.fix.outputs.ISSUES }}" + echo -e "$ISSUES" fi - if [ -n "${{ steps.fix.outputs.DANGEROUS }}" ]; then + if [ -n "$DANGEROUS" ]; then echo "⚠️ Dangerous patterns:" - echo -e "${{ steps.fix.outputs.DANGEROUS }}" + echo -e "$DANGEROUS" fi diff --git a/client/lib/src/BurbleClient.res b/client/lib/src/BurbleClient.res index be9447c..005d846 100644 --- a/client/lib/src/BurbleClient.res +++ b/client/lib/src/BurbleClient.res @@ -231,9 +231,17 @@ let disconnect = (client: client): unit => { // Auth // --------------------------------------------------------------------------- +/// Cryptographically strong guest-id suffix (8 hex chars). +/// Math.random() is not a CSPRNG; guest ids are identity tokens. +let secureIdSuffix: unit => string = %raw(`function () { + const b = new Uint8Array(4); + crypto.getRandomValues(b); + return Array.from(b, x => x.toString(16).padStart(2, "0")).join(""); +}`) + /// Authenticate as a guest. let guestLogin = (client: client, displayName: string): unit => { - let guestId = "guest_" ++ Float.toString(Math.random())->String.slice(~start=2, ~end=10) + let guestId = "guest_" ++ secureIdSuffix() client.auth = Guest({id: guestId, displayName}) client.config.onAuthChange(client.auth) } diff --git a/client/lib/src/BurbleClient.res.mjs b/client/lib/src/BurbleClient.res.mjs index aa4919a..6baf019 100644 --- a/client/lib/src/BurbleClient.res.mjs +++ b/client/lib/src/BurbleClient.res.mjs @@ -78,8 +78,14 @@ function disconnect(client) { client.config.onConnectionChange("Disconnected"); } +function secureIdSuffix() { + const b = new Uint8Array(4); + crypto.getRandomValues(b); + return Array.from(b, x => x.toString(16).padStart(2, "0")).join(""); +} + function guestLogin(client, displayName) { - let guestId = "guest_" + Math.random().toString().slice(2, 10); + let guestId = "guest_" + secureIdSuffix(); client.auth = { TAG: "Guest", id: guestId, diff --git a/client/web/src/room.js b/client/web/src/room.js index fe03285..7be1f20 100644 --- a/client/web/src/room.js +++ b/client/web/src/room.js @@ -30,11 +30,23 @@ export function generateRoomName() { /** * Pick a random word from the word list - * + * + * Uses the Web Crypto CSPRNG rather than Math.random(): room names gate + * access to a private voice room, so they must not be predictable. Uniform + * selection via rejection sampling to avoid modulo bias. + * * @returns {string} Random word */ function pickRandomWord() { - return WORD_LIST[Math.floor(Math.random() * WORD_LIST.length)]; + const range = WORD_LIST.length; + const limit = Math.floor(0x100000000 / range) * range; + const buf = new Uint32Array(1); + let n; + do { + crypto.getRandomValues(buf); + n = buf[0]; + } while (n >= limit); + return WORD_LIST[n % range]; } /** From 5aff48393a2d8e648df2a52965b5f6e3b90c48c9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 15 May 2026 14:46:45 +0000 Subject: [PATCH 2/5] fix(ci): hypatia-scan build step working directory ${{ env.HOME }} is empty (HOME is a system env var, not a workflow env: entry), so working-directory resolved to /hypatia and the build step failed before the scanner ever ran. cd "$HOME/hypatia" in the shell instead. https://claude.ai/code/session_01JRto2WBmaeGukiC4EnbEHg --- .github/workflows/hypatia-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 70bb5d3..55d56d9 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -38,8 +38,8 @@ jobs: fi - name: Build Hypatia scanner (if needed) - working-directory: ${{ env.HOME }}/hypatia run: | + cd "$HOME/hypatia" if [ ! -f hypatia-v2 ]; then echo "Building hypatia-v2 scanner..." cd scanner From 15bb2a1d7f026f5bebdebd214cd6653f1641014c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 15 May 2026 17:46:55 +0000 Subject: [PATCH 3/5] fix(ci): repair pre-existing infra failures on Elixir + Hypatia jobs - elixir-ci: bump mlugg/setup-zig v1.2.2 -> v2.2.1. v1.2.2 fetches Zig release tarballs from ziglang.org, which no longer hosts them (404); v2.x uses the community mirror system. Pin Zig 0.13.0 explicitly. - hypatia-scan: Hypatia lives in an external repo whose clone/build can fail for reasons outside burble's control. The scan is advisory (no merge gate, no code-scanning feed), so clone/build now continue-on-error and the scan degrades to an empty findings set instead of hard-failing every PR. https://claude.ai/code/session_01JRto2WBmaeGukiC4EnbEHg --- .github/workflows/elixir-ci.yml | 4 ++-- .github/workflows/hypatia-scan.yml | 25 +++++++++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/.github/workflows/elixir-ci.yml b/.github/workflows/elixir-ci.yml index a488f27..3251f93 100644 --- a/.github/workflows/elixir-ci.yml +++ b/.github/workflows/elixir-ci.yml @@ -29,9 +29,9 @@ jobs: elixir-version: '1.17' - name: Set up Zig 0.13 - uses: mlugg/setup-zig@53fc45b17fe98b52f92ee5ea08ff48a85a3e7eb7 # v1.2.2 + uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 with: - version: '0.13' + version: '0.13.0' - name: Install just uses: taiki-e/install-action@899b013517f9e7774591216672bf75a46bb9a481 # v2.9.4 diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 55d56d9..064cc53 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -31,16 +31,24 @@ jobs: elixir-version: '1.19.4' otp-version: '28.3' + # Hypatia lives in an external repo; clone/build can fail for reasons + # outside this repository's control. The scan is advisory (it does not + # gate merges and does not feed code-scanning), so external failures + # must degrade to an empty result rather than hard-fail every PR. - name: Clone Hypatia + id: clone + continue-on-error: true run: | if [ ! -d "$HOME/hypatia" ]; then - git clone https://github.com/hyperpolymath/hypatia.git "$HOME/hypatia" + git clone --depth 1 https://github.com/hyperpolymath/hypatia.git "$HOME/hypatia" fi - name: Build Hypatia scanner (if needed) + id: build + continue-on-error: true run: | cd "$HOME/hypatia" - if [ ! -f hypatia-v2 ]; then + if [ ! -f hypatia-v2 ] && [ -d scanner ]; then echo "Building hypatia-v2 scanner..." cd scanner mix deps.get @@ -53,8 +61,17 @@ jobs: run: | echo "Scanning repository: ${{ github.repository }}" - # Run scanner - HYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . > hypatia-findings.json + # Run scanner if it built; otherwise emit an empty (advisory) result. + if [ -x "$HOME/hypatia/hypatia-cli.sh" ]; then + HYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . > hypatia-findings.json \ + || echo '[]' > hypatia-findings.json + else + echo "::warning::Hypatia scanner unavailable (external clone/build failed) — emitting empty findings. This scan is advisory and does not gate merges." + echo '[]' > hypatia-findings.json + fi + + # Guard against non-JSON / partial output corrupting downstream jq. + jq -e . hypatia-findings.json >/dev/null 2>&1 || echo '[]' > hypatia-findings.json # Count findings FINDING_COUNT=$(jq '. | length' hypatia-findings.json 2>/dev/null || echo 0) From 84a2abb796e9dc40b4765c99d46b4fbb71abc8bb Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 15 May 2026 17:48:28 +0000 Subject: [PATCH 4/5] fix(ci): pin Zig 0.15.1 to match ffi/zig/build.zig ffi/zig/build.zig targets the Zig 0.15 build API (b.addLibrary with .root_module, b.createModule with target/optimize). With the setup-zig 404 now resolved by the v2.2.1 bump, the Build Zig FFI step failed because CI installed Zig 0.13. Pin 0.15.1 to match the source. https://claude.ai/code/session_01JRto2WBmaeGukiC4EnbEHg --- .github/workflows/elixir-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/elixir-ci.yml b/.github/workflows/elixir-ci.yml index 3251f93..79b905e 100644 --- a/.github/workflows/elixir-ci.yml +++ b/.github/workflows/elixir-ci.yml @@ -28,10 +28,10 @@ jobs: otp-version: '27' elixir-version: '1.17' - - name: Set up Zig 0.13 + - name: Set up Zig 0.15 uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 with: - version: '0.13.0' + version: '0.15.1' - name: Install just uses: taiki-e/install-action@899b013517f9e7774591216672bf75a46bb9a481 # v2.9.4 From f2828f40a60c00b96f84ca9960ecb673f50d4a65 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 15 May 2026 17:50:54 +0000 Subject: [PATCH 5/5] fix(ci): pass real erl_nif.h path to Zig FFI build build.zig defaults erl-include to /usr/lib/erlang/usr/include, which does not exist under erlef/setup-beam (OTP installs in the tool cache). just build-ffi never passes -Derl-include, so the NIF compile failed to find erl_nif.h. Derive the include dir from code:root_dir() and pass it explicitly; fail fast with a clear message if the header is absent. https://claude.ai/code/session_01JRto2WBmaeGukiC4EnbEHg --- .github/workflows/elixir-ci.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/elixir-ci.yml b/.github/workflows/elixir-ci.yml index 79b905e..059a3df 100644 --- a/.github/workflows/elixir-ci.yml +++ b/.github/workflows/elixir-ci.yml @@ -56,7 +56,15 @@ jobs: - name: Build Zig FFI working-directory: ${{ github.workspace }} - run: just build-ffi + run: | + # erlef/setup-beam installs OTP under the tool cache, not at + # build.zig's hardcoded /usr/lib/erlang default. Derive the real + # NIF header dir from the running Erlang so erl_nif.h is found. + ERL_INCLUDE="$(erl -noshell -eval 'io:format("~ts/usr/include", [code:root_dir()])' -s init stop)" + echo "erl_nif.h include dir: $ERL_INCLUDE" + test -f "$ERL_INCLUDE/erl_nif.h" || { echo "::error::erl_nif.h not found at $ERL_INCLUDE"; exit 1; } + ( cd ffi/zig && zig build -Doptimize=ReleaseFast -Derl-include="$ERL_INCLUDE" ) + cp ffi/zig/zig-out/lib/libburble_coprocessor.so server/priv/ 2>/dev/null || true - name: Run server tests working-directory: ${{ github.workspace }}