mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
JS: Switch to whitelisting allowed properties
This commit is contained in:
@@ -2,9 +2,7 @@ package com.semmle.js.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -163,10 +161,7 @@ import com.semmle.util.data.IntList;
|
||||
*/
|
||||
public class TypeScriptASTConverter {
|
||||
private String source;
|
||||
private final JsonObject nodeFlags;
|
||||
private final JsonObject syntaxKinds;
|
||||
private final Map<Integer, String> nodeFlagMap = new LinkedHashMap<>();
|
||||
private final Map<Integer, String> syntaxKindMap = new LinkedHashMap<>();
|
||||
private final TypeScriptParserMetadata metadata;
|
||||
private int[] lineStarts;
|
||||
|
||||
private int syntaxKindExtends;
|
||||
@@ -180,24 +175,11 @@ public class TypeScriptASTConverter {
|
||||
private static final Pattern WHITESPACE_END_PAREN =
|
||||
Pattern.compile("^" + WHITESPACE_CHAR + "*\\)");
|
||||
|
||||
TypeScriptASTConverter(JsonObject nodeFlags, JsonObject syntaxKinds) {
|
||||
this.nodeFlags = nodeFlags;
|
||||
this.syntaxKinds = syntaxKinds;
|
||||
makeEnumIdMap(nodeFlags, nodeFlagMap);
|
||||
makeEnumIdMap(syntaxKinds, syntaxKindMap);
|
||||
TypeScriptASTConverter(TypeScriptParserMetadata metadata) {
|
||||
this.metadata = metadata;
|
||||
this.syntaxKindExtends = getSyntaxKind("ExtendsKeyword");
|
||||
}
|
||||
|
||||
/** Builds a mapping from ID to name given a TypeScript enum object. */
|
||||
private void makeEnumIdMap(JsonObject enumObject, Map<Integer, String> idToName) {
|
||||
for (Map.Entry<String, JsonElement> entry : enumObject.entrySet()) {
|
||||
JsonPrimitive prim = entry.getValue().getAsJsonPrimitive();
|
||||
if (prim.isNumber() && !idToName.containsKey(prim.getAsInt())) {
|
||||
idToName.put(prim.getAsInt(), entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given TypeScript AST (which was parsed from {@code source}) into a parser {@link
|
||||
* Result}.
|
||||
@@ -1617,7 +1599,7 @@ public class TypeScriptASTConverter {
|
||||
private Node convertMetaProperty(JsonObject node, SourceLocation loc) throws ParseError {
|
||||
Position metaStart = loc.getStart();
|
||||
String keywordKind =
|
||||
syntaxKinds.get(node.getAsJsonPrimitive("keywordToken").getAsInt() + "").getAsString();
|
||||
metadata.getSyntaxKinds().get(node.getAsJsonPrimitive("keywordToken").getAsInt() + "").getAsString();
|
||||
String identifier = keywordKind.equals("ImportKeyword") ? "import" : "new";
|
||||
Position metaEnd =
|
||||
new Position(
|
||||
@@ -1995,7 +1977,7 @@ public class TypeScriptASTConverter {
|
||||
|
||||
private String getOperator(JsonObject node) throws ParseError {
|
||||
int operatorId = node.get("operator").getAsInt();
|
||||
switch (syntaxKindMap.get(operatorId)) {
|
||||
switch (metadata.getSyntaxKindMap().get(operatorId)) {
|
||||
case "PlusPlusToken":
|
||||
return "++";
|
||||
case "MinusMinusToken":
|
||||
@@ -2219,7 +2201,7 @@ public class TypeScriptASTConverter {
|
||||
}
|
||||
|
||||
private Node convertTypeOperator(JsonObject node, SourceLocation loc) throws ParseError {
|
||||
String operator = syntaxKinds.get("" + node.get("operator").getAsInt()).getAsString();
|
||||
String operator = metadata.getSyntaxKinds().get("" + node.get("operator").getAsInt()).getAsString();
|
||||
if (operator.equals("KeyOfKeyword")) {
|
||||
return new UnaryTypeExpr(loc, UnaryTypeExpr.Kind.Keyof, convertChildAsType(node, "type"));
|
||||
}
|
||||
@@ -2537,7 +2519,7 @@ public class TypeScriptASTConverter {
|
||||
* <tt>ts.NodeFlags</tt> in enum.
|
||||
*/
|
||||
private boolean hasFlag(JsonObject node, String flagName) {
|
||||
JsonElement flagDescriptor = this.nodeFlags.get(flagName);
|
||||
JsonElement flagDescriptor = this.metadata.getNodeFlags().get(flagName);
|
||||
if (flagDescriptor == null) {
|
||||
throw new RuntimeException(
|
||||
"Incompatible version of TypeScript installed. Missing node flag " + flagName);
|
||||
@@ -2552,7 +2534,7 @@ public class TypeScriptASTConverter {
|
||||
|
||||
/** Gets the numeric value of the syntax kind enum with the given name. */
|
||||
private int getSyntaxKind(String syntaxKind) {
|
||||
JsonElement descriptor = this.syntaxKinds.get(syntaxKind);
|
||||
JsonElement descriptor = this.metadata.getSyntaxKinds().get(syntaxKind);
|
||||
if (descriptor == null) {
|
||||
throw new RuntimeException(
|
||||
"Incompatible version of TypeScript installed. Missing syntax kind " + syntaxKind);
|
||||
@@ -2581,7 +2563,7 @@ public class TypeScriptASTConverter {
|
||||
if (node instanceof JsonObject) {
|
||||
JsonElement kind = ((JsonObject) node).get("kind");
|
||||
if (kind instanceof JsonPrimitive && ((JsonPrimitive) kind).isNumber())
|
||||
return syntaxKindMap.get(kind.getAsInt());
|
||||
return metadata.getSyntaxKindMap().get(kind.getAsInt());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -150,6 +150,9 @@ public class TypeScriptParser {
|
||||
/** If non-zero, we use this instead of relying on the corresponding environment variable. */
|
||||
private int typescriptRam = 0;
|
||||
|
||||
/** Metadata requested immediately after starting the TypeScript parser. */
|
||||
private TypeScriptParserMetadata metadata;
|
||||
|
||||
/** Sets the amount of RAM to allocate to the TypeScript compiler.s */
|
||||
public void setTypescriptRam(int megabytes) {
|
||||
this.typescriptRam = megabytes;
|
||||
@@ -297,6 +300,7 @@ public class TypeScriptParser {
|
||||
InputStream is = parserWrapperProcess.getInputStream();
|
||||
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
|
||||
fromParserWrapper = new BufferedReader(isr);
|
||||
this.loadMetadata();
|
||||
} catch (IOException e) {
|
||||
throw new CatastrophicError(
|
||||
"Could not start TypeScript parser wrapper " + "(command: ." + parserWrapperCommand + ")",
|
||||
@@ -385,6 +389,17 @@ public class TypeScriptParser {
|
||||
return new CatastrophicError("Unexpected response from TypeScript parser wrapper:\n" + response, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests metadata from the TypeScript process. See {@link TypeScriptParserMetadata}.
|
||||
*/
|
||||
private void loadMetadata() {
|
||||
JsonObject request = new JsonObject();
|
||||
request.add("command", new JsonPrimitive("get-metadata"));
|
||||
JsonObject response = talkToParserWrapper(request);
|
||||
checkResponseType(response, "metadata");
|
||||
this.metadata = new TypeScriptParserMetadata(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AST for a given source file.
|
||||
*
|
||||
@@ -402,11 +417,9 @@ public class TypeScriptParser {
|
||||
metrics.stopPhase(ExtractionMetrics.ExtractionPhase.TypeScriptParser_talkToParserWrapper);
|
||||
try {
|
||||
checkResponseType(response, "ast");
|
||||
JsonObject nodeFlags = response.get("nodeFlags").getAsJsonObject();
|
||||
JsonObject syntaxKinds = response.get("syntaxKinds").getAsJsonObject();
|
||||
JsonObject ast = response.get("ast").getAsJsonObject();
|
||||
metrics.startPhase(ExtractionMetrics.ExtractionPhase.TypeScriptASTConverter_convertAST);
|
||||
Result converted = new TypeScriptASTConverter(nodeFlags, syntaxKinds).convertAST(ast, source);
|
||||
Result converted = new TypeScriptASTConverter(metadata).convertAST(ast, source);
|
||||
metrics.stopPhase(ExtractionMetrics.ExtractionPhase.TypeScriptASTConverter_convertAST);
|
||||
return converted;
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.semmle.js.parser;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
/**
|
||||
* Static data from the TypeScript compiler needed for decoding ASTs.
|
||||
* <p>
|
||||
* AST nodes store their kind and flags as integers, but the meaning of this integer changes
|
||||
* between compiler versions. The metadata contains mappings from integers to logical names
|
||||
* which are stable across versions.
|
||||
*/
|
||||
public class TypeScriptParserMetadata {
|
||||
private final JsonObject nodeFlags;
|
||||
private final JsonObject syntaxKinds;
|
||||
private final Map<Integer, String> nodeFlagMap = new LinkedHashMap<>();
|
||||
private final Map<Integer, String> syntaxKindMap = new LinkedHashMap<>();
|
||||
|
||||
public TypeScriptParserMetadata(JsonObject metadata) {
|
||||
this.nodeFlags = metadata.get("nodeFlags").getAsJsonObject();
|
||||
this.syntaxKinds = metadata.get("syntaxKinds").getAsJsonObject();
|
||||
makeEnumIdMap(getNodeFlags(), getNodeFlagMap());
|
||||
makeEnumIdMap(getSyntaxKinds(), getSyntaxKindMap());
|
||||
}
|
||||
|
||||
/** Builds a mapping from ID to name given a TypeScript enum object. */
|
||||
private void makeEnumIdMap(JsonObject enumObject, Map<Integer, String> idToName) {
|
||||
for (Map.Entry<String, JsonElement> entry : enumObject.entrySet()) {
|
||||
JsonPrimitive prim = entry.getValue().getAsJsonPrimitive();
|
||||
if (prim.isNumber() && !idToName.containsKey(prim.getAsInt())) {
|
||||
idToName.put(prim.getAsInt(), entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>NodeFlags</code> enum object from the TypeScript API.
|
||||
*/
|
||||
public JsonObject getNodeFlags() {
|
||||
return nodeFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>SyntaxKind</code> enum object from the TypeScript API.
|
||||
*/
|
||||
public JsonObject getSyntaxKinds() {
|
||||
return syntaxKinds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mapping from node flag bit to its name.
|
||||
*/
|
||||
public Map<Integer, String> getNodeFlagMap() {
|
||||
return nodeFlagMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mapping from syntax kind ID to its name.
|
||||
*/
|
||||
public Map<Integer, String> getSyntaxKindMap() {
|
||||
return syntaxKindMap;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user