-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Open
Description
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_COMMENTExpected 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)
Metadata
Metadata
Assignees
Labels
No labels