diff --git a/javascript/extractor/src/com/semmle/ts/extractor/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/ts/extractor/TypeScriptASTConverter.java index d42b06762463..dfc15e4e4aa1 100644 --- a/javascript/extractor/src/com/semmle/ts/extractor/TypeScriptASTConverter.java +++ b/javascript/extractor/src/com/semmle/ts/extractor/TypeScriptASTConverter.java @@ -1217,7 +1217,8 @@ private Node convertExportDeclaration(JsonObject node, SourceLocation loc) throw } private Node convertExportSpecifier(JsonObject node, SourceLocation loc) throws ParseError { - Identifier local = convertChild(node, hasChild(node, "propertyName") ? "propertyName" : "name"); + JsonObject localToken = node.get(hasChild(node, "propertyName") ? "propertyName" : "name").getAsJsonObject(); + Identifier local = convertNodeAsIdentifier(localToken); JsonObject exportedToken = node.get("name").getAsJsonObject(); Identifier exported = convertNodeAsIdentifier(exportedToken); diff --git a/javascript/ql/src/change-notes/2025-01-09-import-spec-strings.md b/javascript/ql/src/change-notes/2025-01-09-import-spec-strings.md new file mode 100644 index 000000000000..02fd7bdac94e --- /dev/null +++ b/javascript/ql/src/change-notes/2025-01-09-import-spec-strings.md @@ -0,0 +1,5 @@ +--- +category: fix +--- +* Fixed a TypeScript extractor crash that would occur when encountering an export specifier + whose local specifier was a string literal. diff --git a/javascript/ql/test/library-tests/Modules/arbitrarySpecifier.ts b/javascript/ql/test/library-tests/Modules/arbitrarySpecifier.ts index 8ed0bd8e804b..ab800354e807 100644 --- a/javascript/ql/test/library-tests/Modules/arbitrarySpecifier.ts +++ b/javascript/ql/test/library-tests/Modules/arbitrarySpecifier.ts @@ -3,4 +3,7 @@ import { "Foo::new" as Foo_new } from "./foo.wasm" const foo = Foo_new() export { Foo_new as "Foo::new" } -export type * as "Foo_types" from './mod' \ No newline at end of file +export type * as "Foo_types" from './mod' + +export { "" as "" } from "somewhere"; +export { "" } from "somewhere"; diff --git a/javascript/ql/test/library-tests/Modules/tests.expected b/javascript/ql/test/library-tests/Modules/tests.expected index bf0efddba553..eb3cad032c1e 100644 --- a/javascript/ql/test/library-tests/Modules/tests.expected +++ b/javascript/ql/test/library-tests/Modules/tests.expected @@ -5,6 +5,8 @@ test_ExportDeclarations | a.js:5:1:5:32 | export ... } = o; | | arbitrarySpecifier.ts:5:1:5:32 | export ... :new" } | | arbitrarySpecifier.ts:6:1:6:41 | export ... './mod' | +| arbitrarySpecifier.ts:8:1:8:43 | export ... where"; | +| arbitrarySpecifier.ts:9:1:9:34 | export ... where"; | | b.js:5:1:5:18 | export { f as g }; | | b.js:7:1:7:21 | export ... './a'; | | d.js:4:1:4:20 | export * from 'm/c'; | @@ -22,6 +24,8 @@ test_ExportDefaultDeclarations | es2015_require.js:3:1:3:25 | export ... ss C {} | test_ExportSpecifiers | arbitrarySpecifier.ts:5:10:5:30 | Foo_new ... o::new" | arbitrarySpecifier.ts:5:10:5:16 | Foo_new | arbitrarySpecifier.ts:5:21:5:30 | "Foo::new" | +| arbitrarySpecifier.ts:8:10:8:23 | "" as "" | arbitrarySpecifier.ts:8:10:8:14 | "" | arbitrarySpecifier.ts:8:19:8:23 | "" | +| arbitrarySpecifier.ts:9:10:9:14 | "" | arbitrarySpecifier.ts:9:10:9:14 | "" | arbitrarySpecifier.ts:9:10:9:14 | "" | | b.js:5:10:5:15 | f as g | b.js:5:10:5:10 | f | b.js:5:15:5:15 | g | | e.js:2:10:2:10 | x | e.js:2:10:2:10 | x | e.js:2:10:2:10 | x | | e.js:2:13:2:13 | y | e.js:2:13:2:13 | y | e.js:2:13:2:13 | y | @@ -81,8 +85,8 @@ test_Module_exports | a.js:1:1:5:32 | | default | a.js:1:16:3:1 | functio ... n 23;\\n} | | a.js:1:1:5:32 | | x | a.js:5:18:5:20 | f() | | a.js:1:1:5:32 | | y | a.js:5:25:5:25 | y | -| arbitrarySpecifier.ts:1:1:6:41 | | Foo::new | arbitrarySpecifier.ts:5:10:5:16 | Foo_new | -| arbitrarySpecifier.ts:1:1:6:41 | | Foo_types | arbitrarySpecifier.ts:6:13:6:28 | * as "Foo_types" | +| arbitrarySpecifier.ts:1:1:10:0 | | Foo::new | arbitrarySpecifier.ts:5:10:5:16 | Foo_new | +| arbitrarySpecifier.ts:1:1:10:0 | | Foo_types | arbitrarySpecifier.ts:6:13:6:28 | * as "Foo_types" | | b.js:1:1:8:0 | | f2 | a.js:1:16:3:1 | functio ... n 23;\\n} | | b.js:1:1:8:0 | | g | b.js:5:10:5:10 | f | | e.js:1:1:4:0 | | g | a.js:1:16:3:1 | functio ... n 23;\\n} | @@ -109,6 +113,8 @@ test_OtherImports | import-indirect-path.js:5:1:5:14 | require(x + y) | a.js:1:1:5:32 | | test_ReExportDeclarations | arbitrarySpecifier.ts:6:1:6:41 | export ... './mod' | arbitrarySpecifier.ts:6:35:6:41 | './mod' | +| arbitrarySpecifier.ts:8:1:8:43 | export ... where"; | arbitrarySpecifier.ts:8:32:8:42 | "somewhere" | +| arbitrarySpecifier.ts:9:1:9:34 | export ... where"; | arbitrarySpecifier.ts:9:23:9:33 | "somewhere" | | b.js:7:1:7:21 | export ... './a'; | b.js:7:16:7:20 | './a' | | d.js:4:1:4:20 | export * from 'm/c'; | d.js:4:15:4:19 | 'm/c' | | e.js:3:1:3:35 | export ... './a'; | e.js:3:30:3:34 | './a' | @@ -128,6 +134,8 @@ test_getAnImportedModule test_getExportedName | arbitrarySpecifier.ts:5:10:5:30 | Foo_new ... o::new" | Foo::new | | arbitrarySpecifier.ts:6:13:6:28 | * as "Foo_types" | Foo_types | +| arbitrarySpecifier.ts:8:10:8:23 | "" as "" | | +| arbitrarySpecifier.ts:9:10:9:14 | "" | | | b.js:5:10:5:15 | f as g | g | | b.js:7:8:7:9 | f2 | f2 | | e.js:2:10:2:10 | x | x | @@ -152,6 +160,8 @@ test_getImportedName | unresolved.js:1:8:1:8 | f | default | test_getLocalName | arbitrarySpecifier.ts:5:10:5:30 | Foo_new ... o::new" | Foo_new | +| arbitrarySpecifier.ts:8:10:8:23 | "" as "" | | +| arbitrarySpecifier.ts:9:10:9:14 | "" | | | b.js:5:10:5:15 | f as g | f | | b.js:7:8:7:9 | f2 | default | | e.js:2:10:2:10 | x | x |