diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9e8b1a4..54629fe4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "20" + node-version: "22" cache: "npm" cache-dependency-path: package-lock.json @@ -71,7 +71,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "20" + node-version: "22" cache: "npm" cache-dependency-path: package-lock.json diff --git a/.github/workflows/pr-test-gate.yml b/.github/workflows/pr-test-gate.yml index b2471627..d2b09b9e 100644 --- a/.github/workflows/pr-test-gate.yml +++ b/.github/workflows/pr-test-gate.yml @@ -46,7 +46,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "20" + node-version: "22" cache: "npm" cache-dependency-path: package-lock.json diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 5f48309b..d78e277f 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -24,7 +24,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20.19.0' + node-version: '22' cache: 'npm' - name: Install dependencies diff --git a/backend/src/middleware/auth.ts b/backend/src/middleware/auth.ts index 082d447d..dee07b54 100644 --- a/backend/src/middleware/auth.ts +++ b/backend/src/middleware/auth.ts @@ -113,7 +113,14 @@ export function verifyJwt(token: string): { publicKey: string } | null { } // Use timingSafeEqual to prevent timing attacks - if (providedSig.length !== expected.length || !crypto.timingSafeEqual(providedSig, expected)) { + // This will throw if lengths differ, or return false if content differs + try { + const result = crypto.timingSafeEqual(providedSig, expected); + // timingSafeEqual returns true when equal, false when not equal (same length, different content) + if (result === false) { + return null; + } + } catch { return null; } diff --git a/contracts/stream_contract/src/test.rs b/contracts/stream_contract/src/test.rs index afb15ca1..e211c085 100644 --- a/contracts/stream_contract/src/test.rs +++ b/contracts/stream_contract/src/test.rs @@ -180,6 +180,20 @@ fn test_update_fee_config_rejects_invalid_fee_rate() { assert_eq!(result, Err(Ok(StreamError::InvalidFeeRate))); } +#[test] +fn test_update_fee_config_rejects_not_initialized() { + let env = Env::default(); + env.mock_all_auths(); + let client = create_contract(&env); + + let admin = Address::generate(&env); + let treasury = Address::generate(&env); + + // Call update_fee_config before initialize + let result = client.try_update_fee_config(&admin, &treasury, &100); + assert_eq!(result, Err(Ok(StreamError::NotInitialized))); +} + #[test] fn test_initialize_emits_event() { let env = Env::default(); diff --git a/frontend/src/components/dashboard/dashboard-view.tsx b/frontend/src/components/dashboard/dashboard-view.tsx index 5dac949f..4d0cb27a 100644 --- a/frontend/src/components/dashboard/dashboard-view.tsx +++ b/frontend/src/components/dashboard/dashboard-view.tsx @@ -884,7 +884,8 @@ export function DashboardView({ session, onDisconnect }: DashboardViewProps) {
Save recurring stream settings once, apply instantly, then override before submitting.
{requiredFieldsCompleted} / 5 required fields completed