Skip to content

Commit e0ee900

Browse files
gaoyiabroofa
andauthoredJan 5, 2025··
fix: Test for invalid byte array sizes and ranges in v1(), v4(), and v7() (#845)
--------- Co-authored-by: Robert Kieffer <[email protected]>
1 parent 6e83b3a commit e0ee900

File tree

6 files changed

+100
-5
lines changed

6 files changed

+100
-5
lines changed
 

‎src/test/v1.test.ts

+24
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,28 @@ describe('v1', () => {
168168
assert.deepStrictEqual(updateV1State(state, now, RFC_RANDOM), expected, `Failed: ${title}`);
169169
}
170170
});
171+
172+
test('throws when option.random is too short', () => {
173+
const random = Uint8Array.of(16);
174+
const buffer = new Uint8Array(16).fill(0);
175+
assert.throws(() => {
176+
v1({ random }, buffer);
177+
});
178+
});
179+
180+
test('throws when options.rng() is too short', () => {
181+
const buffer = new Uint8Array(16);
182+
const rng = () => Uint8Array.of(0); // length = 1
183+
assert.throws(() => {
184+
v1({ rng }, buffer);
185+
});
186+
});
187+
188+
test('throws RangeError for out-of-range indexes', () => {
189+
const buf15 = new Uint8Array(15);
190+
const buf30 = new Uint8Array(30);
191+
assert.throws(() => v1({}, buf15));
192+
assert.throws(() => v1({}, buf30, -1));
193+
assert.throws(() => v1({}, buf30, 15));
194+
});
171195
});

‎src/test/v4.test.ts

+24
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,28 @@ describe('v4', () => {
114114

115115
assert.deepEqual(buffer, expectedBuf);
116116
});
117+
118+
test('throws when option.random is too short', () => {
119+
const random = Uint8Array.of(16);
120+
const buffer = new Uint8Array(16).fill(0);
121+
assert.throws(() => {
122+
v4({ random }, buffer);
123+
});
124+
});
125+
126+
test('throws when options.rng() is too short', () => {
127+
const buffer = new Uint8Array(16);
128+
const rng = () => Uint8Array.of(0); // length = 1
129+
assert.throws(() => {
130+
v4({ rng }, buffer);
131+
});
132+
});
133+
134+
test('throws RangeError for out-of-range indexes', () => {
135+
const buf15 = new Uint8Array(15);
136+
const buf30 = new Uint8Array(30);
137+
assert.throws(() => v4({}, buf15));
138+
assert.throws(() => v4({}, buf30, -1));
139+
assert.throws(() => v4({}, buf30, 15));
140+
});
117141
});

‎src/test/v7.test.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as assert from 'assert';
22
import test, { describe } from 'node:test';
3-
import { Version7Options } from '../types.js';
43
import parse from '../parse.js';
54
import stringify from '../stringify.js';
5+
import { Version7Options } from '../types.js';
66
import v7, { updateV7State } from '../v7.js';
77

88
// Fixture values for testing with the rfc v7 UUID example:
@@ -267,4 +267,28 @@ describe('v7', () => {
267267
assert.notStrictEqual(stringify(buf), id);
268268
}
269269
});
270+
271+
test('throws when option.random is too short', () => {
272+
const random = Uint8Array.of(16);
273+
const buffer = new Uint8Array(16).fill(0);
274+
assert.throws(() => {
275+
v7({ random }, buffer);
276+
});
277+
});
278+
279+
test('throws when options.rng() is too short', () => {
280+
const buffer = new Uint8Array(16);
281+
const rng = () => Uint8Array.of(0); // length = 1
282+
assert.throws(() => {
283+
v7({ rng }, buffer);
284+
});
285+
});
286+
287+
test('throws RangeError for out-of-range indexes', () => {
288+
const buf15 = new Uint8Array(15);
289+
const buf30 = new Uint8Array(30);
290+
assert.throws(() => v7({}, buf15));
291+
assert.throws(() => v7({}, buf30, -1));
292+
assert.throws(() => v7({}, buf30, 15));
293+
});
270294
});

‎src/v1.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { UUIDTypes, Version1Options } from './types.js';
21
import rng from './rng.js';
32
import { unsafeStringify } from './stringify.js';
3+
import { UUIDTypes, Version1Options } from './types.js';
44

55
// **`v1()` - Generate time-based UUID**
66
//
@@ -139,11 +139,20 @@ function v1Bytes(
139139
buf?: Uint8Array,
140140
offset = 0
141141
) {
142+
if (rnds.length < 16) {
143+
throw new Error('Random bytes length must be >= 16');
144+
}
145+
142146
// Defaults
143147
if (!buf) {
144148
buf = new Uint8Array(16);
145149
offset = 0;
150+
} else {
151+
if (offset < 0 || offset + 16 > buf.length) {
152+
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
153+
}
146154
}
155+
147156
msecs ??= Date.now();
148157
nsecs ??= 0;
149158
clockseq ??= ((rnds[8] << 8) | rnds[9]) & 0x3fff;

‎src/v4.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { UUIDTypes, Version4Options } from './types.js';
21
import native from './native.js';
32
import rng from './rng.js';
43
import { unsafeStringify } from './stringify.js';
4+
import { UUIDTypes, Version4Options } from './types.js';
55

66
function v4(options?: Version4Options, buf?: undefined, offset?: number): string;
77
function v4(options: Version4Options | undefined, buf: Uint8Array, offset?: number): Uint8Array;
@@ -12,7 +12,10 @@ function v4(options?: Version4Options, buf?: Uint8Array, offset?: number): UUIDT
1212

1313
options = options || {};
1414

15-
const rnds = options.random || (options.rng || rng)();
15+
const rnds = options.random ?? options.rng?.() ?? rng();
16+
if (rnds.length < 16) {
17+
throw new Error('Random bytes length must be >= 16');
18+
}
1619

1720
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
1821
rnds[6] = (rnds[6] & 0x0f) | 0x40;
@@ -21,6 +24,9 @@ function v4(options?: Version4Options, buf?: Uint8Array, offset?: number): UUIDT
2124
// Copy bytes to buffer, if provided
2225
if (buf) {
2326
offset = offset || 0;
27+
if (offset < 0 || offset + 16 > buf.length) {
28+
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
29+
}
2430

2531
for (let i = 0; i < 16; ++i) {
2632
buf[offset + i] = rnds[i];

‎src/v7.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { UUIDTypes, Version7Options } from './types.js';
21
import rng from './rng.js';
32
import { unsafeStringify } from './stringify.js';
3+
import { UUIDTypes, Version7Options } from './types.js';
44

55
type V7State = {
66
msecs?: number; // time, milliseconds
@@ -62,9 +62,17 @@ export function updateV7State(state: V7State, now: number, rnds: Uint8Array) {
6262
}
6363

6464
function v7Bytes(rnds: Uint8Array, msecs?: number, seq?: number, buf?: Uint8Array, offset = 0) {
65+
if (rnds.length < 16) {
66+
throw new Error('Random bytes length must be >= 16');
67+
}
68+
6569
if (!buf) {
6670
buf = new Uint8Array(16);
6771
offset = 0;
72+
} else {
73+
if (offset < 0 || offset + 16 > buf.length) {
74+
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
75+
}
6876
}
6977

7078
// Defaults

0 commit comments

Comments
 (0)
Please sign in to comment.