From decfb9aea495d57382ba4458f509e25c66e42b35 Mon Sep 17 00:00:00 2001 From: rblake2320 <73768949+rblake2320@users.noreply.github.com> Date: Thu, 30 Jul 2026 13:39:44 -0500 Subject: [PATCH 1/2] fix random bound validation and harden CI --- .github/workflows/ci.yml | 5 ++++ crypto-boundary-suite.mts | 50 +++++++++++++++++++++++++++++++++++++ package.json | 2 +- packages/core/src/crypto.ts | 4 ++- 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 crypto-boundary-suite.mts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b836887..f0f66fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,9 +7,14 @@ on: permissions: contents: read +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: protocol: runs-on: ubuntu-latest + timeout-minutes: 60 services: redis: image: redis:7.4.5-alpine@sha256:bb186d083732f669da90be8b0f975a37812b15e913465bb14d845db72a4e3e08 diff --git a/crypto-boundary-suite.mts b/crypto-boundary-suite.mts new file mode 100644 index 0000000..3ae9242 --- /dev/null +++ b/crypto-boundary-suite.mts @@ -0,0 +1,50 @@ +import { secureRandomInt } from './packages/core/src/crypto.ts'; + +let passed = 0; + +function check(name: string, fn: () => void): void { + try { + fn(); + passed += 1; + console.log(` OK ${name}`); + } catch (error) { + console.error(` FAIL ${name}`); + throw error; + } +} + +function expectRangeError(value: number): void { + try { + secureRandomInt(value); + } catch (error) { + if (error instanceof RangeError) return; + throw new Error(`expected RangeError for ${String(value)}, got ${String(error)}`); + } + throw new Error(`expected secureRandomInt(${String(value)}) to throw`); +} + +console.log('\nTSK crypto boundary suite'); + +check('accepts the full uint32 sampling domain', () => { + const value = secureRandomInt(0x100000000); + if (!Number.isInteger(value) || value < 0 || value >= 0x100000000) { + throw new Error(`out-of-range result: ${value}`); + } +}); + +check('rejects zero and negative bounds', () => { + expectRangeError(0); + expectRangeError(-1); +}); + +check('rejects non-integer and non-finite bounds', () => { + expectRangeError(1.5); + expectRangeError(Number.NaN); + expectRangeError(Number.POSITIVE_INFINITY); +}); + +check('rejects bounds larger than the uint32 entropy source', () => { + expectRangeError(0x100000001); +}); + +console.log(`TSK crypto boundary suite: ${passed}/4 passed`); diff --git a/package.json b/package.json index 0780339..dc15bdb 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ ], "scripts": { "build": "npm run build -w packages/core && npm run build -w packages/server && npm run build -w packages/client-sdk && npm run build -w packages/bpc-bridge", - "test": "npx tsx test-suite.mts && npx tsx lifecycle-suite.mts && npx tsx hotp-exhaustion-suite.mts && npx tsx client-lifecycle-suite.mts && npx tsx store-durability-suite.mts && npx tsx anomaly-integration-suite.mts && npx tsx attack-suite.mts && npx tsx adversarial-proof.mts && npx tsx ultra-bridge-test.mts && npx tsx runtime-capture-suite.mts", + "test": "npx tsx test-suite.mts && npx tsx crypto-boundary-suite.mts && npx tsx lifecycle-suite.mts && npx tsx hotp-exhaustion-suite.mts && npx tsx client-lifecycle-suite.mts && npx tsx store-durability-suite.mts && npx tsx anomaly-integration-suite.mts && npx tsx attack-suite.mts && npx tsx adversarial-proof.mts && npx tsx ultra-bridge-test.mts && npx tsx runtime-capture-suite.mts", "test:ha": "npx tsx replicating-tumbler-suite.mts && npx tsx replica-receiver-suite.mts && npx tsx failover-promotion-suite.mts", "test:redis": "npx tsx redis-fencing-integration.mts", "test:postgres:ha": "npm run build -w packages/server && npx tsx tsk-hotp-outbox-integration.mts", diff --git a/packages/core/src/crypto.ts b/packages/core/src/crypto.ts index a700365..6829f9a 100644 --- a/packages/core/src/crypto.ts +++ b/packages/core/src/crypto.ts @@ -137,7 +137,9 @@ export function generateClientId(): string { * Uses rejection sampling to eliminate modulo bias. */ export function secureRandomInt(max: number): number { - if (max <= 0) throw new RangeError('max must be > 0'); + if (!Number.isSafeInteger(max) || max <= 0 || max > 0x100000000) { + throw new RangeError('max must be a positive safe integer no greater than 2^32'); + } if (max === 1) return 0; // Rejection sampling: discard values in the biased tail const limit = 0x100000000 - (0x100000000 % max); From ddcaf54b12025ecb92d4d6a87072965aa6b983ae Mon Sep 17 00:00:00 2001 From: rblake2320 <73768949+rblake2320@users.noreply.github.com> Date: Thu, 30 Jul 2026 13:42:18 -0500 Subject: [PATCH 2/2] deduplicate branch CI runs --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0f66fc..a79f3d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ permissions: contents: read concurrency: - group: ci-${{ github.workflow }}-${{ github.ref }} + group: ci-${{ github.workflow }}-${{ github.head_ref || github.ref_name }} cancel-in-progress: true jobs: