[TS codegen] Escape JSDoc terminator in doc comments to prevent comment closure and import‑time code execution
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)
malicious.fbs
namespace Poc;
/// */console.log('INJECTED_FROM_DOC_COMMENT')/*
table A { x:int; }
root_type A;
- Generate TypeScript
flatc --ts -o out malicious.fbs
- Observe injection in generated module
/**
* */console.log('INJECTED_FROM_DOC_COMMENT')/*
*/
export class A {
- 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)
flatc25.9.23 (Windows release)
hey @Ky0toFu - thank you for a very comprehensive issue form! Are you interested in opening a PR for this issue? I'll defer to others (@bjornharrtell) on preference between your two fix proposals -- I would think that line comments would be the better, more robust fix as long as it doesn't negatively affect any users' tooling (like tooltips in an IDE)
I have no preference 😊
Thanks for the feedback!
Given the potential impact on IDEs/tooling that rely on /** ... */ for hover docs, I'll start with the minimal safe fix that escapes */ in the generated JSDoc.
This prevents doc comments from closing the block (and injecting code) while fully preserving JSDoc behavior for end users.
I'll open a PR shortly.
🥳