COMMENT-INJECTION.md
Added a focused schema fixture:
test/inputs/schema/comment-injection.schematest/inputs/schema/comment-injection.1.jsonThe schema puts comment-closing text in both an object description and a property description:
*/ and /* for C-style block comments (the opener matters for Kotlin and Scala 3, whose block comments nest){- and -} for Elm/Haskell comments""" on its own line for Python/Elixir docstrings/heredocs</summary> & for C# XML doc comments\r}, \u0085}, \u2028}, and \u2029} for line-comment outputs whose lexers treat CR, NEL, LINE SEPARATOR, or PARAGRAPH SEPARATOR as line terminators\, ", and """ for docstring/heredoc delimiter boundaries and C-family line splicingRun:
CPUs=1 QUICKTEST=true FIXTURE=schema-typescript npm run test:fixtures -- test/inputs/schema/comment-injection.schema
Expected result: the generated TypeScript validates comment-injection.1.json and prints equivalent JSON.
Before escaping was added, this test failed before validation because TopLevel.ts was syntactically invalid; the schema description escaped the generated /** ... */ comment.
In JSON Schema input, packages/quicktype-core/src/attributes/Description.ts collects description into type and property-description attributes. Renderers then emit those attributes as documentation comments.
Observed comment sinks:
description on objects/classesdescription for object properties/fieldsdescription on enum schemasdescription on union schemas and other named types when a renderer emits docs for those named typestitle is different in the inspected path: JSON Schema input uses it for type/top-level naming, not as raw documentation text. The generated JSON Schema renderer can output JSON title fields, but those are JSON string values, not source comments.
Other non-schema inputs can also supply descriptions or leading comments, but the reproduction here is limited to JSON Schema description.
Outputs are affected when raw schema descriptions are placed into comments/docstrings without escaping that target's comment delimiter or line terminators. The shared fix now normalizes description line endings and escapes delimiter text at comment-emission time.
/** ... */: TypeScript, Flow, JavaScript when descriptions are emitted, Java, C (cjson), C++, PHP, Kotlin, Scala 3, Smithy4s. Trigger with */; escaped as * /. The opener /* is escaped as / *, too, because Kotlin and Scala 3 nest block comments, so an unmatched opener would swallow the rest of the file.{-| ... -}: Elm, Haskell. Trigger with {- or -}; escaped as { - and - }.""" is escaped as \"\"\", backslashes are doubled (otherwise a description ending in \ swallows the first quote of the closing delimiter), and a line-ending unescaped " directly before an inline closing """ is escaped as \" (otherwise four quotes in a row leave a stray quote after the closing delimiter)./// <summary>: C#. Trigger with </summary>, <, or &; escaped as XML entities (<, >, &) in both the regular and --density dense paths.//, ///): C#, Go, Rust, Ruby, Swift, Objective-C, Dart, Pike, Crystal, and enum comments in TypeScript-Zod/TypeScript-Effect-Schema. Trigger with a carriage return (\r) — or U+0085/U+2028/U+2029, which JavaScript and C# lexers also treat as line terminators — when descriptions are split only on \n; fixed by normalizing all of them to \n before comment emission.\ pulls the next generated line into the comment (and trips -Wcomment under -Werror). Fixed by appending . after a comment line's trailing backslash in those renderers.Plain JavaScript output did not emit the tested schema descriptions into model comments, so the object/property reproduction does not affect it the same way. TypeScript-Zod and TypeScript-Effect-Schema also did not emit the object/property descriptions from comment-injection.schema; they only emitted the enum description from comment-injection-enum.schema.
The tree-sitter fixture includes Go, Rust, and Ruby even though their grammars did not reproduce a syntax break with the CR line-comment payload; this keeps parser coverage in place for those generated outputs and future payload/escaping changes.
Still not covered by the tree-sitter fixture: Objective-C, Crystal, and Elm. Objective-C's available tree-sitter grammar reports baseline errors on generated .m output; Crystal and Elm have usable grammars but are not included in this parser-coverage pass.
test/inputs/schema/comment-injection.schema covers object and property descriptions, including both Elm/Haskell nested-comment delimiters, XML doc metacharacters, Unicode line terminators, and descriptions ending in \, ", and """.test/inputs/schema/comment-injection-enum.schema covers enum descriptions via an enum-valued property.test/inputs/schema/comment-injection-nested-comment.schema specifically covers unmatched nested-comment openers ({- and /*) in object and property descriptions.test/inputs/schema/comment-injection-enum-nested-comment.schema is the enum-description variant of the nested-comment payload; the tree-sitter fixture substitutes it for the enum-only targets so each sample run does distinct work.The existing JSONSchemaFixture instances pick these samples up for schema-based language tests. Additional narrow comment-injection-* fixtures cover affected outputs that did not already have full schema fixtures: Objective-C uses all four samples; TypeScript-Zod and TypeScript-Effect-Schema use the two enum-description samples. The comment-injection-objective-c fixture is registered but not run in CI: like the full objective-c fixture, the generated .m compiles cleanly with clang but the compiled ./test binary segfaults at runtime (SIGSEGV) on the sample JSON — a pre-existing Objective-C harness issue unrelated to comment injection. It can be run locally on macOS.
A parser-only fixture, comment-injection-treesitter, generates all configured targets and parses them with tree-sitter WASM grammars. It currently covers TypeScript, TypeScript-Zod, TypeScript-Effect-Schema, Swift, C#, Java, Dart, C (cjson), C++, PHP, Kotlin, Go, Pike, Rust, Ruby, Python, Elixir, Scala 3, and Haskell. It is intentionally one fixture/test that loops over all configured languages and reports all parse failures together. The Swift, Dart, Kotlin, Pike, and Elixir grammars are vendored under test/tree-sitter-wasms to avoid npm peer/native dependency issues in CI. Loaded grammars are cached per WASM path across targets and samples.
Two grammar quirks require per-target workarounds in the fixture:
// /*) as an ERROR, whereas real Dart runs line comments to the end of the line; the Dart target neutralizes /* inside line comments before parsing.extern "C" { brace across the two #ifdef __cplusplus blocks that cjson output uses as a guard, reporting a spurious MISSING #endif even for benign output; the cjson target strips those two guard blocks (which only wrap the file) before parsing, so a real injection that leaves a MISSING node is still caught.The fixture also scans the raw generated bytes for injection classes the grammars can't detect (their lexers are more lenient than the real compilers): no output may contain a surviving U+0085/U+2028/U+2029 line terminator, and per-target forbiddenSubstrings catch markup that parses as comment text but is dangerous downstream (C# rejects the raw </summary> & payload, verifying the XML entity escaping).
These are regression tests and should pass with the shared comment escaping/sanitization in place.