Skip to content

Harden error handling: eliminate silent failures in parser and schema validation#353

Open
holodorum wants to merge 8 commits intokson-org:mainfrom
holodorum:hardening
Open

Harden error handling: eliminate silent failures in parser and schema validation#353
holodorum wants to merge 8 commits intokson-org:mainfrom
holodorum:hardening

Conversation

@holodorum
Copy link
Collaborator

  • Route FatalParseException through messageSink — parser invariant violations were caught, printed to stdout, and silently swallowed. Now reported as FATAL_PARSE_ERROR messages visible to callers and tests.

  • Fix KsonNumber.subCoordinatesLocation — passed subStartColumn where subEndColumn was needed, making end-column mapping always wrong for number sub-locations.

  • Surface schema parse errors from RefValidator — a local MessageSink captured $ref target parse errors then discarded them, causing malformed $ref targets to silently accept everything (fail-open). Three JSON Schema Test Suite tests that were false positives due to this are now correctly excluded.

  • Catch invalid regex patterns in schemaRegex() on user-provided patterns threw uncaught exceptions (PatternSyntaxException on JVM, SyntaxError on JS). Now caught via runCatching and reported as SCHEMA_INVALID_REGEX errors. Pattern regexes in PropertiesValidator are also pre-compiled once during construction.

  • Eliminate fail-open pattern in schema validators with null sub-schemas — NotValidator, IfValidator, PropertyNamesValidator, and DependencyValidatorSchema accepted nullable schemas and silently skipped validation when null. Now the type system enforces non-null schemas; null-handling is pushed to the construction site in SchemaParser.

The idempotency assertion in `assertParsesTo` was comparing
`ksonParseResult.kson` to itself, meaning it could never fail.
The intent was to re-parse the compiled Kson output and verify
it produces the same result — but the re-parse step was missing.

Every test using `assertParsesTo` (hundreds across all KsonCoreTest*
files) had its idempotency guarantee silently disabled.
A FatalParseException — meaning the parser violated its own
invariants — was caught, printed to stdout, and silently swallowed.
The caller got back a KsonRootError with no indication anything
catastrophic happened.

Now the error is properly reported through the messageSink via a
new FATAL_PARSE_ERROR MessageType. This makes these errors visible
to callers and test assertions.

Two error-path tests (testIllegalCharacterError, testEmptyCommas)
were updated to expect the now-visible FATAL_PARSE_ERROR messages
that were previously swallowed by println.
Copy-paste error: the last parameter to mapToOriginal was
subStartColumn instead of subEndColumn, meaning the end column
of sub-locations within numbers was always mapped from the start
column. KsonString at line 137 had this correct.

Added a test that verifies the end position differs from start
when different columns are requested — this test fails before
the fix and passes after.
RefValidator created a local MessageSink for parsing the $ref target
schema, then discarded it. If the target was malformed, validation
silently accepted everything (fail-open).

Now parse errors are captured alongside the schema result and forwarded
to the caller's messageSink when parsing fails. This makes broken $ref
targets visible rather than silently permissive.

Three generated JSON Schema Test Suite tests that were passing only
because of this fail-open behavior are now correctly excluded: they
exercise complex relative URI / URN ref resolution that KSON does not
yet support, and their "valid" results were false positives.
…xceptions

PatternValidator and PropertiesValidator both called Regex() on
user-provided schema patterns without catching the exception for
invalid regex syntax. This caused uncaught PatternSyntaxExceptions
during schema parsing and validation.

- Add SCHEMA_INVALID_REGEX MessageType to report invalid patterns
- Wrap PatternValidator creation in SchemaParser with try-catch
- Validate patternProperties regex keys during schema parsing,
  filtering out invalid ones and reporting errors
- Pre-compile pattern regexes in PropertiesValidator during
  construction instead of re-compiling on every validation call
NotValidator, IfValidator, PropertyNamesValidator, and
DependencyValidatorSchema all accepted nullable schemas and silently
skipped validation when the schema was null. This meant a malformed
sub-schema silently became a permissive schema.

The fix is two-fold:
- In SchemaParser, skip creating validators when parseSchemaElement
  returns null (the parse error is already reported to messageSink)
- Make the validator constructors require non-null schemas, enforced
  by the type system

This makes the null-handling explicit at the construction site and
eliminates four separate silent-accept code paths.
FatalParseException is meant to signal internal parser invariant
violations (i.e. bugs in KSON), but error-recovery paths were
producing marker trees that violated downstream structural invariants,
turning recoverable user errors into fatal internal errors.

1. Empty commas (`[1,,3]`, `{a: 1,, b: 2}`): processComma's error
   marker was created inside the element/property mark, giving
   LIST_ELEMENT and OBJECT_PROPERTY unexpected extra children. Fix:
   close the element mark before reporting extra commas, via a new
   consumeExtraCommas method that runs outside the element scope.

2. Illegal characters (`key: \value`, `[\value]`): delimitedValue's
   error-recovery marker for illegal chars appeared as an additional
   child in OBJECT_PROPERTY and LIST_ELEMENT, tripping their structural
   guards. Fix: count only non-error children for the structural guard
   while preserving the defensive check for genuinely unexpected
   parsing changes.
Compile Regex objects at parse time in SchemaParser and pass them
through to PatternValidator and PropertiesValidator, rather than
accepting raw strings and re-compiling later.  This uses the type
system to enforce that only valid regex patterns reach the validators,
eliminating the comment in PropertiesValidator that explained why
Regex compilation was "guaranteed to succeed."

PatternValidator now takes a Regex instead of a String.
PropertiesValidator now takes List<CompiledPatternSchema> instead of
Map<KsonString, JsonSchema?>, removing the compiledPatterns derivation
that re-parsed already-validated strings.

No new tests: this is a compile-time safety improvement exercised by
the existing JSON Schema Test Suite and SchemaParserTest coverage for
both pattern and patternProperties.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants