Skip to content

[TS codegen] Escape JSDoc terminator in doc comments to prevent comment closure and import‑time code execution #8725

@Ky0toFu

Description

@Ky0toFu

Summary

The TypeScript generator in flatc writes schema documentation comments directly into a JSDoc block without escaping the terminator. A crafted line like */…/* in a .fbs doc comment prematurely closes the block and injects top‑level JavaScript into the generated module, which then executes on import/bundling.

This is a first‑party codegen bug in this repository (not a third‑party dependency).

Affected code

  • File: src/idl_gen_ts.cpp
  • Function: GenDocComment
  • Behavior: concatenates " *" + line + "\n" into a /** … */ JSDoc block without escaping */.
// Generate a documentation comment, if available.
static void GenDocComment(const std::vector<std::string>& dc,
                          std::string* code_ptr,
                          const char* indent = nullptr) {
  if (dc.empty()) { return; }
  std::string& code = *code_ptr;
  if (indent) code += indent;
  code += "/**\n";
  for (auto it = dc.begin(); it != dc.end(); ++it) {
    if (indent) code += indent;
    code += " *" + *it + "\n";  // no escaping of "*/"
  }
  if (indent) code += indent;
  code += " */\n";
}

Minimal reproduction (safe payload)

  1. malicious.fbs
namespace Poc;

/// */console.log('INJECTED_FROM_DOC_COMMENT')/*
table A { x:int; }

root_type A;
  1. Generate TypeScript
flatc --ts -o out malicious.fbs
  1. Observe injection in generated module
/**
 * */console.log('INJECTED_FROM_DOC_COMMENT')/*
 */
export class A {
  1. Any TS→JS path will execute the payload at import‑time (example)
npx --yes esbuild out/poc/A.ts --format=cjs --bundle --outfile=out/bundle.cjs --external:flatbuffers
node out/bundle.cjs  # prints: INJECTED_FROM_DOC_COMMENT

Expected vs actual

  • Expected: doc comments should never be able to close comments and introduce executable code into generated sources.
  • Actual: unescaped */ terminates JSDoc and injects top‑level JS that runs on import/bundling.

Proposed fix (minimal and safe)

Escape */ in each doc comment line before emission to the JSDoc block.

// inside the for-loop over doc lines
auto safe = *it;
for (size_t pos = 0; (pos = safe.find("*/", pos)) != std::string::npos; ) {
  safe.replace(pos, 2, "*\\/");
  pos += 3; // advance past the replacement
}
code += " *" + safe + "\n";

Alternative (even safer)

Render documentation as line comments in TypeScript (// …) so early termination by input is impossible by construction.

Notes

  • This issue is opened to coordinate a code‑hardening fix in the TS generator.
  • A full end‑to‑end PoC (including generated output and import‑time execution) is available upon request and trivial to reproduce with the steps above.

Environment (used for reproduction)

  • Node.js ≥ 18 (tested on v22.17.1)
  • flatc 25.9.23 (Windows release)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions