Skip to content

Commit bfa937c

Browse files
Houseofmvpsclaude
andcommitted
harden: defensively coerce untrusted describe() metadata
The native-AST describe() export is untrusted plugin output. A plugin returning a malformed extensions array (e.g. [123], [null], [{}]) threw an uncaught TypeError out of buildNativeRegistry and crashed the scan (opt-in path only, but below the "coerce all untrusted plugin JSON" bar the rest of native-loader holds). - languageId: ignore unless it's a string (an array/object id no longer leaks into routing or warning messages). - extensions: keep only string entries; a malformed array degrades to the built-in default map (or is skipped) instead of throwing. 145/145 tests pass; default-off behavior unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 7f504dd commit bfa937c

1 file changed

Lines changed: 8 additions & 2 deletions

File tree

src/ast/native-loader.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,9 @@ export function buildNativeRegistry(r: NativeAstResolved): NativeRegistry {
166166
}
167167

168168
// `describe().languageId` is authoritative for identity; filename is fallback.
169-
const reported = loaded.metadata?.languageId;
169+
// Coerce defensively — metadata is untrusted plugin output (a non-string id
170+
// is ignored rather than allowed to crash the registry build).
171+
const reported = typeof loaded.metadata?.languageId === "string" ? loaded.metadata.languageId : undefined;
170172
const lang = reported && reported.length > 0 ? reported : requested;
171173
if (reported && reported.length > 0 && reported !== requested) {
172174
addWarning(
@@ -181,7 +183,11 @@ export function buildNativeRegistry(r: NativeAstResolved): NativeRegistry {
181183
}
182184
}
183185

184-
const declared = loaded.metadata?.extensions;
186+
// Untrusted: keep only string entries so a malformed array (numbers, null,
187+
// objects) can't throw out of the registry build and crash the scan.
188+
const declared = Array.isArray(loaded.metadata?.extensions)
189+
? loaded.metadata.extensions.filter((e): e is string => typeof e === "string")
190+
: undefined;
185191
const exts = (declared && declared.length > 0 ? declared : DEFAULT_EXTENSIONS[lang]) ?? [];
186192
if (exts.length === 0) {
187193
if (r.mode === "strict") {

0 commit comments

Comments
 (0)