W
File size: 2,078 Bytes
2b64d42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// v2.0.55 audit L1 regression — safeEqualString must compare via fixed-
// width hash digests so the early-return on length mismatch can't be used
// as a length oracle by a wall-clock attacker.

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { safeEqualString } from '../src/auth.js';

describe('safeEqualString — hash-based, no length oracle (audit L1)', () => {
  it('returns true for identical strings', () => {
    assert.equal(safeEqualString('sk-dwgxnbnb888', 'sk-dwgxnbnb888'), true);
    assert.equal(safeEqualString('', ''), true);
    assert.equal(safeEqualString('a', 'a'), true);
  });

  it('returns false for different same-length strings', () => {
    assert.equal(safeEqualString('aaaa', 'bbbb'), false);
    assert.equal(safeEqualString('sk-12345678', 'sk-12345679'), false);
  });

  it('returns false for different-length strings without leaking via early return', () => {
    // The point of L1: short-vs-long should still take roughly the same
    // path (sha256 of both). We don't time-assert here (timing is noisy
    // in CI) — instead we verify the function runs to completion and
    // returns false for a cross-section of length differences.
    assert.equal(safeEqualString('a', 'b'), false);
    assert.equal(safeEqualString('short', 'a-rather-much-longer-string'), false);
    assert.equal(safeEqualString('x', 'x'.repeat(1000)), false);
    assert.equal(safeEqualString('x'.repeat(100), 'x'.repeat(100) + 'y'), false);
  });

  it('handles non-string inputs by stringifying', () => {
    assert.equal(safeEqualString(123, 123), true);
    assert.equal(safeEqualString(null, ''), false);  // 'null' !== ''
    assert.equal(safeEqualString(undefined, ''), false);
  });

  it('UTF-8 multi-byte secrets compare byte-accurately', () => {
    assert.equal(safeEqualString('密码', '密码'), true);
    assert.equal(safeEqualString('密码', '密玛'), false);
    // Same logical char count, different code points → false.
    assert.equal(safeEqualString('café', 'café'), false);
  });
});