feat: add initial validatrix project with multi-language validator framework
Establish the Validatrix source code validation framework in Nim, including core detection, tokenization, and validation infrastructure for 10 languages (bash, HTML, JavaScript, Jinja, JSON, Nim, PHP, Python, TOML, YAML). The initial commit introduces the main validatrix.nim entry point with public API (validateSource, validateFile, inspectSource), a comprehensive test suite with per-language test files, build configuration via Makefile and .nimble, detailed LESSONS_LEARNED.md documenting 14 bug classes found during development, and a .gitignore excluding compiled binaries, logs, and build artifacts. The detector module provides extension-to-flavor mapping for 40+ file extensions and content-based language detection, while the debug module supports conditional compilation with -d:validatrixDebug for detailed tokenization logging.
2026-07-08 06:25:59 +02:00
|
|
|
## JavaScript validator -- exhaustive edge-case tests.
|
|
|
|
|
## Auto-generated. Tests valid code, invalid code, encoding attacks,
|
|
|
|
|
## nesting extremes, unicode bombs, binary injection, and more.
|
|
|
|
|
|
|
|
|
|
import std/[unittest, strutils, strformat, json]
|
2026-07-08 10:01:58 +02:00
|
|
|
import ../src/nimcheck
|
feat: add initial validatrix project with multi-language validator framework
Establish the Validatrix source code validation framework in Nim, including core detection, tokenization, and validation infrastructure for 10 languages (bash, HTML, JavaScript, Jinja, JSON, Nim, PHP, Python, TOML, YAML). The initial commit introduces the main validatrix.nim entry point with public API (validateSource, validateFile, inspectSource), a comprehensive test suite with per-language test files, build configuration via Makefile and .nimble, detailed LESSONS_LEARNED.md documenting 14 bug classes found during development, and a .gitignore excluding compiled binaries, logs, and build artifacts. The detector module provides extension-to-flavor mapping for 40+ file extensions and content-based language detection, while the debug module supports conditional compilation with -d:validatrixDebug for detailed tokenization logging.
2026-07-08 06:25:59 +02:00
|
|
|
|
|
|
|
|
proc checkValid(result: ValidationResult, context: string = "") =
|
|
|
|
|
if result.errors.len > 0:
|
|
|
|
|
var msg = &"Expected valid, got {result.errors.len} error(s)"
|
|
|
|
|
if context.len > 0: msg.add(&" [{context}]")
|
|
|
|
|
for e in result.errors: msg.add(&"\n [{e.code}] {e.message}")
|
|
|
|
|
doAssert false, msg
|
|
|
|
|
|
|
|
|
|
proc checkInvalid(result: ValidationResult, context: string = "") =
|
|
|
|
|
if result.errors.len == 0:
|
|
|
|
|
doAssert false, &"Expected errors, got none [{context}]"
|
|
|
|
|
|
|
|
|
|
proc checkHasError(result: ValidationResult, code: string, context: string = "") =
|
|
|
|
|
var found = false
|
|
|
|
|
for e in result.errors:
|
|
|
|
|
if e.code == code: found = true
|
|
|
|
|
if not found:
|
|
|
|
|
var codes: seq[string] = @[]
|
|
|
|
|
for e in result.errors: codes.add(e.code)
|
|
|
|
|
let codesJoined = codes.join(", ")
|
|
|
|
|
doAssert false, "Expected error code '" & code & "', got " & codesJoined & " [" & context & "]"
|
|
|
|
|
|
|
|
|
|
proc checkSeverity(result: ValidationResult, sev: string, context: string = "") =
|
|
|
|
|
var found = false
|
|
|
|
|
for e in result.errors:
|
|
|
|
|
if $e.severity == sev: found = true
|
|
|
|
|
if not found:
|
|
|
|
|
doAssert false, &"Expected severity '{sev}', none found [{context}]"
|
|
|
|
|
|
|
|
|
|
suite "JavaScript Exhaustive Tests":
|
|
|
|
|
|
|
|
|
|
test "basic valid JS":
|
|
|
|
|
let src = "function hello() { return 42; }\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "arrow function":
|
|
|
|
|
let src = "const add = (a, b) => a + b;\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "template literal":
|
|
|
|
|
let src = "const msg = `Hello, ${name}!`;\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "unclosed template literal":
|
|
|
|
|
let src = "const x = `hello\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "nested template literal":
|
|
|
|
|
let src = "const x = `${`${`deep`}`}`\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "regex literal":
|
|
|
|
|
let src = "const re = /abc[0-9]+/g;\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "regex with escapes":
|
|
|
|
|
let src = "const re = /\\d+\\.\\d+/gi;\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "class definition":
|
|
|
|
|
let src = "class Animal {\n constructor(name) { this.name = name; }\n speak() { return this.name; }\n}\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "async/await":
|
|
|
|
|
let src = "async function fetch() {\n const res = await fetch('/api');\n return res.json();\n}\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "destructuring":
|
|
|
|
|
let src = "const { a, b: c } = obj;\nconst [x, ...rest] = arr;\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "spread operator":
|
|
|
|
|
let src = "const merged = {...obj1, ...obj2};\nconst nums = [1, ...[2, 3], 4];\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "optional chaining":
|
|
|
|
|
let src = "const x = obj?.prop ?? 'default';\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "generator function":
|
|
|
|
|
let src = "function* gen() {\n yield 1;\n yield 2;\n}\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "unclosed string with escape":
|
|
|
|
|
let src = "const s = \"hello\\\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "line comment //":
|
|
|
|
|
let src = "// this is a comment\nconst x = 1;\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "block comment /* */":
|
|
|
|
|
let src = "const x = /* inline comment */ 42;\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "unclosed block comment":
|
|
|
|
|
let src = "const x = 1; /* never closed\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "empty source":
|
|
|
|
|
let src = ""
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "unicode escape in string":
|
|
|
|
|
let src = "const s = \"\\u00E9\\u00E0\\u00FC\";\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "binary null byte":
|
|
|
|
|
let src = "const x = 1;\x00const y = 2;\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "import/export ESM":
|
|
|
|
|
let src = "import { foo } from './bar.js';\nexport const baz = 42;\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "bigint literal":
|
|
|
|
|
let src = "const big = 9007199254740991n;\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfJavaScript)
|
|
|
|
|
check result.errors.len >= 0
|