Skip to content

Commit 776aca9

Browse files
authored
fix: disallow null seed for gen_rng (#1015)
* fix: disallow null seed for gen_rng * docs: add changelog * fix: remove VNull from return types * chore: reorder imports
1 parent e1bc4b0 commit 776aca9

5 files changed

Lines changed: 21 additions & 23 deletions

File tree

src/interpreter/lib/math.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ export const stdMath: Record<`Math:${string}`, Value> = {
218218
else if (options?.type !== undefined) {
219219
throw new AiScriptRuntimeError('`options` must be an object if specified.');
220220
}
221-
if (seed.type !== 'num' && seed.type !== 'str' && seed.type !== 'null') throw new AiScriptRuntimeError('`seed` must be either number or string if specified.');
221+
if (seed.type !== 'num' && seed.type !== 'str') throw new AiScriptRuntimeError('`seed` must be either number or string.');
222222
switch (algo) {
223223
case 'rc4_legacy':
224224
return GenerateLegacyRandom(seed);

src/utils/random/chacha20.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,15 @@ export class ChaCha20 extends RandomBase {
5353
private buffer: Uint8Array;
5454
private filledBuffer: Uint8Array;
5555
private counter: bigint;
56-
constructor(seed?: Uint8Array | undefined) {
56+
constructor(seed: Uint8Array) {
5757
const keyNonceBytes = CHACHA_IVSIZE + CHACHA_KEYSIZE;
5858
super();
59-
let keynonce: Uint8Array;
60-
if (typeof seed === 'undefined') {
61-
keynonce = crypto.getRandomValues(new Uint8Array(keyNonceBytes));
62-
} else {
63-
keynonce = seed;
64-
if (keynonce.byteLength > keyNonceBytes) keynonce = seed.subarray(0, keyNonceBytes);
65-
if (keynonce.byteLength < keyNonceBytes) {
66-
const y = new Uint8Array(keyNonceBytes);
67-
y.set(keynonce);
68-
keynonce = y;
69-
}
59+
let keynonce = seed;
60+
if (keynonce.byteLength > keyNonceBytes) keynonce = seed.subarray(0, keyNonceBytes);
61+
if (keynonce.byteLength < keyNonceBytes) {
62+
const y = new Uint8Array(keyNonceBytes);
63+
y.set(keynonce);
64+
keynonce = y;
7065
}
7166
const key = keynonce.subarray(0, CHACHA_KEYSIZE);
7267
const nonce = keynonce.subarray(CHACHA_KEYSIZE, CHACHA_KEYSIZE + CHACHA_IVSIZE);

src/utils/random/genrng.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ import { FN_NATIVE, NULL, NUM } from '../../interpreter/value.js';
33
import { textEncoder } from '../../const.js';
44
import { SeedRandomWrapper } from './seedrandom.js';
55
import { ChaCha20 } from './chacha20.js';
6-
import type { VNativeFn, VNull, Value } from '../../interpreter/value.js';
6+
import type { VNativeFn, VNum, VStr } from '../../interpreter/value.js';
77

8-
export function GenerateLegacyRandom(seed: Value | undefined) : VNativeFn | VNull {
9-
if (!seed || seed.type !== 'num' && seed.type !== 'str') return NULL;
8+
export function GenerateLegacyRandom(seed: VNum | VStr): VNativeFn {
109
const rng = seedrandom(seed.value.toString());
1110
return FN_NATIVE(([min, max]) => {
1211
if (min && min.type === 'num' && max && max.type === 'num') {
@@ -16,8 +15,7 @@ export function GenerateLegacyRandom(seed: Value | undefined) : VNativeFn | VNul
1615
});
1716
}
1817

19-
export function GenerateRC4Random(seed: Value | undefined) : VNativeFn | VNull {
20-
if (!seed || seed.type !== 'num' && seed.type !== 'str') return NULL;
18+
export function GenerateRC4Random(seed: VNum | VStr): VNativeFn {
2119
const rng = new SeedRandomWrapper(seed.value);
2220
return FN_NATIVE(([min, max]) => {
2321
if (min && min.type === 'num' && max && max.type === 'num') {
@@ -28,13 +26,12 @@ export function GenerateRC4Random(seed: Value | undefined) : VNativeFn | VNull {
2826
});
2927
}
3028

31-
export async function GenerateChaCha20Random(seed: Value | undefined) : Promise<VNativeFn | VNull> {
32-
if (!seed || seed.type !== 'num' && seed.type !== 'str' && seed.type !== 'null') return NULL;
33-
let actualSeed : Uint8Array | undefined = undefined;
29+
export async function GenerateChaCha20Random(seed: VNum | VStr): Promise<VNativeFn> {
30+
let actualSeed: Uint8Array;
3431
if (seed.type === 'num')
3532
{
3633
actualSeed = new Uint8Array(await crypto.subtle.digest('SHA-384', new Uint8Array(new Float64Array([seed.value]))));
37-
} else if (seed.type === 'str') {
34+
} else {
3835
actualSeed = new Uint8Array(await crypto.subtle.digest('SHA-384', new Uint8Array(textEncoder.encode(seed.value))));
3936
}
4037
const rng = new ChaCha20(actualSeed);

test/std.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as assert from 'assert';
2-
import { describe, test } from 'vitest';
2+
import { describe, expect, test } from 'vitest';
33
import { utils } from '../src';
4+
import { AiScriptRuntimeError } from '../src/error';
45
import { NUM, STR, NULL, ARR, OBJ, BOOL, TRUE, FALSE, ERROR ,FN_NATIVE } from '../src/interpreter/value';
56
import { exe, eq } from './testutils';
67

@@ -156,6 +157,10 @@ describe('Math', () => {
156157
`)
157158
eq(res, ARR([BOOL(true), BOOL(true)]));
158159
});
160+
161+
test.concurrent('gen_rng should reject when null is provided as a seed', async () => {
162+
await expect(() => exe('Math:gen_rng(null)')).rejects.toThrow(AiScriptRuntimeError);
163+
});
159164
});
160165

161166
describe('Obj', () => {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fix: `Math:gen_rng``seed``null`を与えてもエラーが発生しない問題を修正

0 commit comments

Comments
 (0)