Skip to content

fix(evolution): escape blueprint strings in generated TypeScript (#412) - #636

Closed
Kshot3000 wants to merge 1 commit into
IntersectMBO:mainfrom
Kshot3000:fix/codegen-escape-blueprint-strings
Closed

Kshot3000 wants to merge 1 commit into
IntersectMBO:mainfrom
Kshot3000:fix/codegen-escape-blueprint-strings

Conversation

@Kshot3000

Copy link
Copy Markdown

Summary

Fixes #412. generateTypeScript() in blueprint/Codegen.ts spliced blueprint-controlled strings into the generated TypeScript without escaping, so a crafted blueprint (e.g. a third party's plutus.json, the case GHSA-79p9-vmph-w58f is about) could inject statements into the output that run when a consumer builds or imports the generated file.

Reproduced locally on main with the new tests: a preamble title of x */ + newline + globalThis.__pwned = 1 + newline + /* closes the header comment and emits the payload as a bare statement; a validator title containing " breaks out of its string literal the same way.

Fix

Three small helpers in Codegen.ts, applied at every splice point:

  • stringLiteral() — emits a value with JSON.stringify, used for validator title / hash / compiledCode and for constructor tags in TSchema.Literal / TSchema.TaggedStruct.
  • propertyName() — emits a field or variant name bare when it is a valid identifier (the normal case), quoted via JSON.stringify otherwise. Applied to struct field names in generateTSchema, variant and explicit-union keys, and the property names generateTSType emits in type aliases.
  • commentText() — replaces the comment terminator in title/description text placed inside JSDoc (file header, definition comments, validator comments), so untrusted text stays inside its comment.

Output for ordinary blueprints is byte-identical: JSON.stringify of a plain string is the same double-quoted literal codegen already emitted, identifiers stay bare, and text without a comment terminator is untouched. The existing codegen tests (including the TSchema.Literal("Never" as const, { index: 1 }) assertions) pass unchanged.

Tests

Five new tests in test/Blueprint.test.ts under "Codegen - escaping untrusted blueprint strings (#412)", each verified to fail on main and pass with the fix:

  • preamble title cannot close the header comment (the comment holding the payload is still open when the payload text appears);
  • a definition title cannot close its JSDoc comment (same oracle, checked from the last comment opener — also stash-verified red on main);
  • validator title/hash/compiledCode with quotes and newlines are emitted as the exact JSON.stringify literals;
  • a constructor tag with a quote and a payload is emitted as an escaped TSchema.Literal;
  • a field title that is not a valid identifier (not an identifier) is emitted quoted.

Full package suite on this branch: 1683 passed / 75 skipped / 0 failed (test files 101 passed / 1 skipped of 102) — exactly main's 1678 baseline plus the 5 new tests. tsc --noEmit and eslint are clean, and the touched files add no new prettier drift (the source file's prettier diff-line count equals main's, 89 = 89; the test file is prettier-clean).

One note from writing the fix: the first draft's own doc comment for the comment-neutralising helper contained the literal terminator sequence and closed itself — the tests caught the resulting parse failure immediately, which is a fair demonstration of how easy this bug is to write.


Independent builder, not affiliated with the Cardano Foundation, IOG or Intersect.
@Kshot9000 (https://x.com/kshot9000) · Tips: addr1q8hnl6vl5a6k3rw3n5g3jtte696zcl76kfatzv7gpswa9r0dj7fma6klq55y4ffm7tf0em09udnyhuk4ah92pl5x9jpqjae44v

…ersectMBO#412)

generateTypeScript spliced blueprint-controlled strings into the output
raw: a preamble or definition title containing the comment terminator
closed its JSDoc early and emitted the rest as code, and validator
title/hash/compiledCode and constructor tags broke out of their string
literals with a quote. That is the injection GHSA-79p9-vmph-w58f covers
for codegen run over an untrusted blueprint.

Emit string values with JSON.stringify, quote property names that are
not valid identifiers, and neutralise the comment terminator in comment
text. Output for ordinary blueprints is unchanged. Five new tests cover
the header comment, a definition JSDoc, the validator literals, a
constructor tag, and a non-identifier field title; each fails on main.

Full suite: 1683 passed / 75 skipped / 0 failed (main baseline 1678 + 5).
@emmanuel-musau

Copy link
Copy Markdown
Collaborator

Closing: we're not accepting bulk
automated contributions. The linked issues stay open.

@IntersectMBO IntersectMBO locked as spam and limited conversation to collaborators Oct 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

blueprint/Codegen: escape blueprint-controlled strings in generated TypeScript

2 participants