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
10 changes: 9 additions & 1 deletion .github/workflows/elixir-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,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 }}
Expand Down
25 changes: 21 additions & 4 deletions .github/workflows/hypatia-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,21 @@ 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: |
# Use the shell $HOME, not ${{ env.HOME }}: the latter reads the
# workflow `env:` map (never set here) and resolves to an empty
Expand All @@ -46,7 +54,7 @@ jobs:
# scan (below) steps already use the shell "$HOME/hypatia"; this
# makes the build step consistent with them.
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
Expand All @@ -59,8 +67,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)
Expand Down
26 changes: 16 additions & 10 deletions .github/workflows/rhodibot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 <rhodibot@hyperpolymath.dev>"

Expand All @@ -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._
"
Expand All @@ -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
10 changes: 9 additions & 1 deletion client/lib/src/BurbleClient.res
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
8 changes: 7 additions & 1 deletion client/lib/src/BurbleClient.res.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 14 additions & 2 deletions client/web/src/room.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}

/**
Expand Down
Loading