Download packages/tree-sitter-bash/test/fuzz.test.ts from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 6.99 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/tree-sitter-bash/test/fuzz.test.ts
- Command line
-
hf download hf://SaylorTwift/kimi-code/packages/tree-sitter-bash/test/fuzz.test.ts
-
curl -L -o fuzz.test.ts https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/tree-sitter-bash/test/fuzz.test.ts
6.99 kB
| // test/fuzz.test.ts | |
| // | |
| // Deterministic fuzzing (fixed-seed PRNG — no per-run randomness; scale the | |
| // volume with TS_BASH_FUZZ_SCALE=N for deeper local runs): | |
| // a) token soup — random sequences of bash operators/keywords/fragments; | |
| // b) byte mutations of the curated fixtures (replace/delete/insert, | |
| // NUL included); | |
| // c) nesting bombs — programmatic deep $(…) / (…) / ${…} / if nesting. | |
| // | |
| // Contract under test: parse never throws; within budget it returns either | |
| // ok:true or { ok:false, reason:'aborted' }; an ok:true tree is always | |
| // structurally sound (assertTreeIntegrity); nesting beyond the documented | |
| // depth caps degrades locally (hasError) instead of throwing. | |
| import { readFileSync, readdirSync } from 'node:fs'; | |
| import path from 'node:path'; | |
| import { describe, expect, it } from 'vitest'; | |
| import { parse } from '#/parse'; | |
| import type { ParseOptions, ParseResult } from '#/parse'; | |
| import { assertTreeIntegrity, parseFixtureFile } from './helpers/differential'; | |
| const PKG_ROOT = path.resolve(import.meta.dirname, '..'); | |
| const SCALE = Math.max(1, Number(process.env['TS_BASH_FUZZ_SCALE'] ?? 1) || 1); | |
| /** Park–Miller minimal standard PRNG: small, portable, deterministic. */ | |
| function prng(seed: number): () => number { | |
| let state = seed % 2147483647; | |
| if (state <= 0) state += 2147483646; | |
| return () => { | |
| state = (state * 16807) % 2147483647; | |
| return (state - 1) / 2147483646; | |
| }; | |
| } | |
| function pick(rand: () => number, pool: readonly string[]): string { | |
| return pool[Math.floor(rand() * pool.length)]!; | |
| } | |
| function checkContract(source: string, options?: ParseOptions): ParseResult { | |
| let result: ParseResult | undefined; | |
| expect(() => { | |
| result = parse(source, options); | |
| }).not.toThrow(); | |
| expect(result).toBeDefined(); | |
| if (result!.ok) { | |
| assertTreeIntegrity(result!.rootNode, source); | |
| } else { | |
| expect(result!).toEqual({ ok: false, reason: 'aborted' }); | |
| } | |
| return result!; | |
| } | |
| const TOKEN_POOL = [ | |
| 'echo', 'ls', 'foo', 'bar', 'x', 'A=1', 'if', 'then', 'fi', 'while', 'do', 'done', 'for', 'in', 'case', 'esac', | |
| '&&', '||', '|', '|&', ';', '&', ';;', ';&', '(', ')', '{', '}', '>', '>>', '<', '>&1', '2>', '&>', '<<EOF', | |
| '<<<', '$x', '${v}', '${v:-d}', '$(cmd)', '`cmd`', '$((1+2))', '[[', ']]', '[', ']', '==', '=~', '-f', '-z', | |
| '"str"', "'raw'", '$"t"', "$'a'", '*', '?', '[a-z]', '@(a|b)', '\\\\', '\\n', '\n', '#c', 'function', 'return', | |
| '${v#p}', '${v/p/r}', '${v[@]}', '!<', '>(p)', '<(p)', '$#', '$?', '0x1F', '..', '<<-', '>&-', 'abc_def', | |
| ] as const; | |
| function fixtureSources(): string[] { | |
| const dir = path.join(PKG_ROOT, 'test/fixtures/differential'); | |
| const out: string[] = []; | |
| for (const file of readdirSync(dir).filter((f) => f.endsWith('.txt')).toSorted()) { | |
| for (const sample of parseFixtureFile(file, readFileSync(path.join(dir, file), 'utf8'))) { | |
| out.push(sample.source); | |
| } | |
| } | |
| return out; | |
| } | |
| describe('fuzz: token soup', () => { | |
| it('never throws and always yields a sound tree or a clean abort', () => { | |
| const rand = prng(0x5eed0001); | |
| const count = 250 * SCALE; | |
| let parsed = 0; | |
| for (let n = 0; n < count; n++) { | |
| const length = 3 + Math.floor(rand() * 22); | |
| const parts: string[] = []; | |
| for (let k = 0; k < length; k++) parts.push(pick(rand, TOKEN_POOL)); | |
| if (checkContract(parts.join(' ')).ok) parsed++; | |
| } | |
| expect(parsed).toBeGreaterThan(0); | |
| console.log(`token soup: ${count} inputs, ${parsed} parsed / ${count - parsed} aborted`); | |
| }); | |
| }); | |
| describe('fuzz: byte mutations of fixtures', () => { | |
| it('never throws and always yields a sound tree or a clean abort', () => { | |
| const rand = prng(0x5eed0002); | |
| const bases = fixtureSources(); | |
| const count = 300 * SCALE; | |
| let parsed = 0; | |
| for (let n = 0; n < count; n++) { | |
| const base = pick(rand, bases); | |
| if (base.length === 0) continue; | |
| const pos = Math.floor(rand() * base.length); | |
| const mode = rand(); | |
| let mutated: string; | |
| if (mode < 0.4) { | |
| // replace one code unit (may be NUL) | |
| const code = Math.floor(rand() * 256); | |
| mutated = base.slice(0, pos) + String.fromCodePoint(code) + base.slice(pos + 1); | |
| } else if (mode < 0.7) { | |
| mutated = base.slice(0, pos) + base.slice(pos + 1); | |
| } else { | |
| const code = Math.floor(rand() * 256); | |
| mutated = base.slice(0, pos) + String.fromCodePoint(code) + base.slice(pos); | |
| } | |
| if (checkContract(mutated).ok) parsed++; | |
| } | |
| expect(parsed).toBeGreaterThan(0); | |
| console.log(`byte mutations: ${count} inputs, ${parsed} parsed / ${count - parsed} aborted`); | |
| }); | |
| }); | |
| describe('fuzz: nesting bombs degrade locally per the documented depth caps', () => { | |
| const substitution = (depth: number): string => `echo ${'$('.repeat(depth)}x${')'.repeat(depth)}`; | |
| const subshell = (depth: number): string => `${'('.repeat(depth)}x${')'.repeat(depth)}`; | |
| const expansion = (depth: number): string => `echo ${'${a:-'.repeat(depth)}z${'}'.repeat(depth)}`; | |
| const ifs = (depth: number): string => `${'if x; then '.repeat(depth)}y${'; fi'.repeat(depth)}`; | |
| it('within the caps the trees are clean', () => { | |
| // literalDepth ticks twice per ${…} level (parseLiteral + parseExpansion), | |
| // so 200 levels stay under MAX_PARSE_DEPTH = 500. | |
| for (const source of [substitution(100), subshell(400), expansion(200), ifs(400)]) { | |
| const result = checkContract(source, { timeoutMs: 60_000 }); | |
| expect(result.ok).toBe(true); | |
| if (result.ok) expect(result.hasError).toBe(false); | |
| } | |
| }); | |
| it('beyond the caps the parse still succeeds and flags hasError (MAX_SUBSTITUTION_DEPTH = 150)', () => { | |
| const result = checkContract(substitution(200)); | |
| expect(result.ok).toBe(true); | |
| if (result.ok) expect(result.hasError).toBe(true); | |
| }); | |
| it('beyond the caps the parse still succeeds and flags hasError (MAX_PARSE_DEPTH = 500)', () => { | |
| for (const source of [subshell(600), expansion(600), ifs(600)]) { | |
| const result = checkContract(source, { timeoutMs: 60_000 }); | |
| expect(result.ok).toBe(true); | |
| if (result.ok) expect(result.hasError).toBe(true); | |
| } | |
| }); | |
| it('extreme nesting never overflows the stack', () => { | |
| for (const source of [substitution(5000), subshell(5000), expansion(5000), ifs(5000)]) { | |
| const result = checkContract(source, { timeoutMs: 60_000 }); | |
| // Either a locally degraded tree (hasError) or a clean budget abort. | |
| if (result.ok) expect(result.hasError).toBe(true); | |
| else expect(result.reason).toBe('aborted'); | |
| } | |
| }); | |
| it('the node budget aborts huge flat programs, and a raised budget parses them', () => { | |
| const source = 'echo a; '.repeat(20_000); | |
| expect(parse(source)).toEqual({ ok: false, reason: 'aborted' }); | |
| const result = parse(source, { timeoutMs: 60_000, maxNodes: 10_000_000 }); | |
| expect(result.ok).toBe(true); | |
| if (result.ok) assertTreeIntegrity(result.rootNode, source); | |
| }); | |
| }); | |