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
|
|
|
## HTML 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 "HTML Exhaustive Tests":
|
|
|
|
|
|
|
|
|
|
test "basic valid HTML5":
|
|
|
|
|
let src = "<!DOCTYPE html><html><body><p>Hi</p></body></html>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "unclosed tag":
|
|
|
|
|
let src = "<html><body><p>unclosed\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "self-closing void elements":
|
|
|
|
|
let src = "<html><body><br/><hr/><img src='x'/></body></html>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "nested tags deep":
|
|
|
|
|
let src = "<div><div><div><div><div><p>deep</p></div></div></div></div></div>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "mismatched tag order":
|
|
|
|
|
let src = "<div><p>text</div></p>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "attributes with quotes":
|
|
|
|
|
let src = "<a href=\"https://example.com\" class=\"link\">click</a>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "HTML comment":
|
|
|
|
|
let src = "<!-- this is a comment --><p>text</p>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "script tag with JS":
|
|
|
|
|
let src = "<html><script>alert('hi');</script></html>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "style tag with CSS":
|
|
|
|
|
let src = "<html><style>body { color: red; }</style></html>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "empty source":
|
|
|
|
|
let src = ""
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "doctype only":
|
|
|
|
|
let src = "<!DOCTYPE html>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "null byte in HTML":
|
|
|
|
|
let src = "<p>hello\x00world</p>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "HTML entities":
|
|
|
|
|
let src = "<p>&<>"'</p>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "data attributes":
|
|
|
|
|
let src = "<div data-id=\"123\" data-value=\"test\"></div>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "inline SVG":
|
|
|
|
|
let src = "<html><svg><circle cx=\"50\" cy=\"50\" r=\"40\"/></svg></html>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "table with rows":
|
|
|
|
|
let src = "<table><tr><td>A</td><td>B</td></tr></table>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|
|
|
|
|
|
|
|
|
|
test "form with inputs":
|
|
|
|
|
let src = "<form><input type=\"text\" name=\"user\"/><button>Go</button></form>\n"
|
|
|
|
|
let result = validateSource(src, flavor = lfHTML)
|
|
|
|
|
check result.errors.len >= 0
|