mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge branch 'main' into asyncCalls
This commit is contained in:
@@ -669,7 +669,9 @@ public class ASTExtractor {
|
||||
public Label visit(Program nd, Context c) {
|
||||
contextManager.enterContainer(toplevelLabel);
|
||||
|
||||
isStrict = hasUseStrict(nd.getBody());
|
||||
boolean prevIsStrict = isStrict;
|
||||
|
||||
isStrict = isStrict || hasUseStrict(nd.getBody());
|
||||
|
||||
// Add platform-specific globals.
|
||||
scopeManager.addVariables(platform.getPredefinedGlobals());
|
||||
@@ -715,6 +717,8 @@ public class ASTExtractor {
|
||||
|
||||
emitNodeSymbol(nd, toplevelLabel);
|
||||
|
||||
isStrict = prevIsStrict;
|
||||
|
||||
return toplevelLabel;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ import com.semmle.util.trap.TrapWriter;
|
||||
*
|
||||
* <ul>
|
||||
* <li>All JavaScript files, that is, files with one of the extensions supported by {@link
|
||||
* FileType#JS} (currently ".js", ".jsx", ".mjs", ".es6", ".es").
|
||||
* FileType#JS} (currently ".js", ".jsx", ".mjs", ".cjs", ".es6", ".es").
|
||||
* <li>All HTML files, that is, files with with one of the extensions supported by {@link
|
||||
* FileType#HTML} (currently ".htm", ".html", ".xhtm", ".xhtml", ".vue").
|
||||
* <li>All YAML files, that is, files with one of the extensions supported by {@link
|
||||
@@ -210,6 +210,7 @@ public class AutoBuild {
|
||||
private final String defaultEncoding;
|
||||
private ExecutorService threadPool;
|
||||
private volatile boolean seenCode = false;
|
||||
private volatile boolean seenFiles = false;
|
||||
private boolean installDependencies = false;
|
||||
private int installDependenciesTimeout;
|
||||
private final VirtualSourceRoot virtualSourceRoot;
|
||||
@@ -472,7 +473,11 @@ public class AutoBuild {
|
||||
shutdownThreadPool();
|
||||
}
|
||||
if (!seenCode) {
|
||||
warn("No JavaScript or TypeScript code found.");
|
||||
if (seenFiles) {
|
||||
warn("Only found JavaScript or TypeScript files that were empty or contained syntax errors.");
|
||||
} else {
|
||||
warn("No JavaScript or TypeScript code found.");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -1201,6 +1206,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
|
||||
long start = logBeginProcess("Extracting " + file);
|
||||
Integer loc = extractor.extract(f, state);
|
||||
if (!extractor.getConfig().isExterns() && (loc == null || loc != 0)) seenCode = true;
|
||||
if (!extractor.getConfig().isExterns()) seenFiles = true;
|
||||
logEndProcess(start, "Done extracting " + file);
|
||||
} catch (Throwable t) {
|
||||
System.err.println("Exception while extracting " + file + ".");
|
||||
|
||||
@@ -41,8 +41,66 @@ public class FileExtractor {
|
||||
public static final Pattern JSON_OBJECT_START =
|
||||
Pattern.compile("^(?s)\\s*\\{\\s*\"([^\"]|\\\\.)*\"\\s*:.*");
|
||||
|
||||
/** The charset for decoding UTF-8 strings. */
|
||||
private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
|
||||
/**
|
||||
* Returns true if the byte sequence contains invalid UTF-8 or unprintable ASCII characters.
|
||||
*/
|
||||
private static boolean hasUnprintableUtf8(byte[] bytes, int length) {
|
||||
// Constants for bytes with N high-order 1-bits.
|
||||
// They are typed as `int` as the subsequent byte-to-int promotion would
|
||||
// otherwise fill the high-order `int` bits with 1s.
|
||||
final int high1 = 0b10000000;
|
||||
final int high2 = 0b11000000;
|
||||
final int high3 = 0b11100000;
|
||||
final int high4 = 0b11110000;
|
||||
final int high5 = 0b11111000;
|
||||
|
||||
int startIndex = skipBOM(bytes, length);
|
||||
for (int i = startIndex; i < length; ++i) {
|
||||
int b = bytes[i];
|
||||
if ((b & high1) == 0) { // 0xxxxxxx is an ASCII character
|
||||
// ASCII values 0-31 are unprintable, except 9-13 are whitespace.
|
||||
// 127 is the unprintable DEL character.
|
||||
if (b <= 8 || 14 <= b && b <= 31 || b == 127) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Check for malformed UTF-8 multibyte code point
|
||||
int trailingBytes = 0;
|
||||
if ((b & high3) == high2) {
|
||||
trailingBytes = 1; // 110xxxxx 10xxxxxx
|
||||
} else if ((b & high4) == high3) {
|
||||
trailingBytes = 2; // 1110xxxx 10xxxxxx 10xxxxxx
|
||||
} else if ((b & high5) == high4) {
|
||||
trailingBytes = 3; // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
} else {
|
||||
return true; // 10xxxxxx and 11111xxx are not valid here.
|
||||
}
|
||||
// Trailing bytes must be of form 10xxxxxx
|
||||
while (trailingBytes > 0) {
|
||||
++i;
|
||||
--trailingBytes;
|
||||
if (i >= length) {
|
||||
return false;
|
||||
}
|
||||
if ((bytes[i] & high2) != high1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the index after the initial BOM, if any, otherwise 0. */
|
||||
private static int skipBOM(byte[] bytes, int length) {
|
||||
if (length >= 2
|
||||
&& (bytes[0] == (byte) 0xfe && bytes[1] == (byte) 0xff
|
||||
|| bytes[0] == (byte) 0xff && bytes[1] == (byte) 0xfe)) {
|
||||
return 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Information about supported file types. */
|
||||
public static enum FileType {
|
||||
@@ -58,7 +116,7 @@ public class FileExtractor {
|
||||
}
|
||||
},
|
||||
|
||||
JS(".js", ".jsx", ".mjs", ".es6", ".es") {
|
||||
JS(".js", ".jsx", ".mjs", ".cjs", ".es6", ".es") {
|
||||
@Override
|
||||
public IExtractor mkExtractor(ExtractorConfig config, ExtractorState state) {
|
||||
return new ScriptExtractor(config);
|
||||
@@ -66,6 +124,10 @@ public class FileExtractor {
|
||||
|
||||
@Override
|
||||
protected boolean contains(File f, String lcExt, ExtractorConfig config) {
|
||||
if (isBinaryFile(f, lcExt, config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (super.contains(f, lcExt, config)) return true;
|
||||
|
||||
// detect Node.js scripts that are meant to be run from
|
||||
@@ -90,6 +152,32 @@ public class FileExtractor {
|
||||
public String toString() {
|
||||
return "javascript";
|
||||
}
|
||||
|
||||
/** Number of bytes to read from the beginning of a ".js" file to detect if it is a binary file. */
|
||||
private static final int fileHeaderSize = 128;
|
||||
|
||||
/** Computes if `f` is a binary file based on whether the initial `fileHeaderSize` bytes are printable UTF-8 chars. */
|
||||
private boolean isBinaryFile(File f, String lcExt, ExtractorConfig config) {
|
||||
if (!config.getDefaultEncoding().equals(StandardCharsets.UTF_8.name())) {
|
||||
return false;
|
||||
}
|
||||
try (FileInputStream fis = new FileInputStream(f)) {
|
||||
byte[] bytes = new byte[fileHeaderSize];
|
||||
int length = fis.read(bytes);
|
||||
|
||||
if (length == -1) return false;
|
||||
|
||||
// Avoid invalid or unprintable UTF-8 files.
|
||||
if (hasUnprintableUtf8(bytes, length)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Exceptions.ignore(e, "Let extractor handle this one.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
JSON(".json") {
|
||||
@@ -160,7 +248,7 @@ public class FileExtractor {
|
||||
if (length == -1) return false;
|
||||
|
||||
// Avoid invalid or unprintable UTF-8 files.
|
||||
if (config.getDefaultEncoding().equals("UTF-8") && hasUnprintableUtf8(bytes, length)) {
|
||||
if (config.getDefaultEncoding().equals(StandardCharsets.UTF_8.name()) && hasUnprintableUtf8(bytes, length)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -182,17 +270,6 @@ public class FileExtractor {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the index after the initial BOM, if any, otherwise 0. */
|
||||
private int skipBOM(byte[] bytes, int length) {
|
||||
if (length >= 2
|
||||
&& (bytes[0] == (byte) 0xfe && bytes[1] == (byte) 0xff
|
||||
|| bytes[0] == (byte) 0xff && bytes[1] == (byte) 0xfe)) {
|
||||
return 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isXml(byte[] bytes, int length) {
|
||||
int startIndex = skipBOM(bytes, length);
|
||||
// Check for `<` encoded in Ascii/UTF-8 or litte-endian UTF-16.
|
||||
@@ -211,56 +288,6 @@ public class FileExtractor {
|
||||
return s.startsWith("! TOUCHSTONE file ") || s.startsWith("[Version] 2.0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the byte sequence contains invalid UTF-8 or unprintable ASCII characters.
|
||||
*/
|
||||
private boolean hasUnprintableUtf8(byte[] bytes, int length) {
|
||||
// Constants for bytes with N high-order 1-bits.
|
||||
// They are typed as `int` as the subsequent byte-to-int promotion would
|
||||
// otherwise fill the high-order `int` bits with 1s.
|
||||
final int high1 = 0b10000000;
|
||||
final int high2 = 0b11000000;
|
||||
final int high3 = 0b11100000;
|
||||
final int high4 = 0b11110000;
|
||||
final int high5 = 0b11111000;
|
||||
|
||||
int startIndex = skipBOM(bytes, length);
|
||||
for (int i = startIndex; i < length; ++i) {
|
||||
int b = bytes[i];
|
||||
if ((b & high1) == 0) { // 0xxxxxxx is an ASCII character
|
||||
// ASCII values 0-31 are unprintable, except 9-13 are whitespace.
|
||||
// 127 is the unprintable DEL character.
|
||||
if (b <= 8 || 14 <= b && b <= 31 || b == 127) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Check for malformed UTF-8 multibyte code point
|
||||
int trailingBytes = 0;
|
||||
if ((b & high3) == high2) {
|
||||
trailingBytes = 1; // 110xxxxx 10xxxxxx
|
||||
} else if ((b & high4) == high3) {
|
||||
trailingBytes = 2; // 1110xxxx 10xxxxxx 10xxxxxx
|
||||
} else if ((b & high5) == high4) {
|
||||
trailingBytes = 3; // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
} else {
|
||||
return true; // 10xxxxxx and 11111xxx are not valid here.
|
||||
}
|
||||
// Trailing bytes must be of form 10xxxxxx
|
||||
while (trailingBytes > 0) {
|
||||
++i;
|
||||
--trailingBytes;
|
||||
if (i >= length) {
|
||||
return false;
|
||||
}
|
||||
if ((bytes[i] & high2) != high1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the byte sequence starts with a shebang line that is not recognized as a
|
||||
* JavaScript interpreter.
|
||||
@@ -288,7 +315,7 @@ public class FileExtractor {
|
||||
// Extract the shebang text
|
||||
int startOfText = startIndex + "#!".length();
|
||||
int lengthOfText = endOfLine - startOfText;
|
||||
String text = new String(bytes, startOfText, lengthOfText, UTF8_CHARSET);
|
||||
String text = new String(bytes, startOfText, lengthOfText, StandardCharsets.UTF_8);
|
||||
// Check if the shebang is a recognized JavaScript intepreter.
|
||||
return !NODE_INVOCATION.matcher(text).find();
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class Main {
|
||||
* A version identifier that should be updated every time the extractor changes in such a way that
|
||||
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
|
||||
*/
|
||||
public static final String EXTRACTOR_VERSION = "2020-04-01";
|
||||
public static final String EXTRACTOR_VERSION = "2020-08-20-2";
|
||||
|
||||
public static final Pattern NEWLINE = Pattern.compile("\n");
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ public class ScriptExtractor implements IExtractor {
|
||||
return extension.equals(".mjs") || extension.equals(".es6") || extension.equals(".es");
|
||||
}
|
||||
|
||||
/** True if files with the given extension should always be treated as CommonJS modules. */
|
||||
private boolean isAlwaysCommonJSModule(String extension) {
|
||||
return extension.equals(".cjs");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoCInfo extract(TextualExtractor textualExtractor) {
|
||||
LocationManager locationManager = textualExtractor.getLocationManager();
|
||||
@@ -45,9 +50,13 @@ public class ScriptExtractor implements IExtractor {
|
||||
}
|
||||
|
||||
// Some file extensions are interpreted as modules by default.
|
||||
if (isAlwaysModule(locationManager.getSourceFileExtension())) {
|
||||
if (config.getSourceType() == SourceType.AUTO)
|
||||
if (config.getSourceType() == SourceType.AUTO) {
|
||||
if (isAlwaysModule(locationManager.getSourceFileExtension())) {
|
||||
config = config.withSourceType(SourceType.MODULE);
|
||||
}
|
||||
if (isAlwaysCommonJSModule(locationManager.getSourceFileExtension())) {
|
||||
config = config.withSourceType(SourceType.COMMONJS_MODULE);
|
||||
}
|
||||
}
|
||||
|
||||
ScopeManager scopeManager =
|
||||
|
||||
1
javascript/extractor/tests/extensions/input/tst4.cjs
Normal file
1
javascript/extractor/tests/extensions/input/tst4.cjs
Normal file
@@ -0,0 +1 @@
|
||||
console.log("Hello CommonJS");
|
||||
141
javascript/extractor/tests/extensions/output/trap/tst4.cjs.trap
Normal file
141
javascript/extractor/tests/extensions/output/trap/tst4.cjs.trap
Normal file
@@ -0,0 +1,141 @@
|
||||
#10000=@"/tst4.cjs;sourcefile"
|
||||
files(#10000,"/tst4.cjs","tst4","cjs",0)
|
||||
#10001=@"/;folder"
|
||||
folders(#10001,"/","")
|
||||
containerparent(#10001,#10000)
|
||||
#10002=@"loc,{#10000},0,0,0,0"
|
||||
locations_default(#10002,#10000,0,0,0,0)
|
||||
hasLocation(#10000,#10002)
|
||||
#20000=@"global_scope"
|
||||
scopes(#20000,0)
|
||||
#20001=@"script;{#10000},1,1"
|
||||
#20002=*
|
||||
lines(#20002,#20001,"console.log(""Hello CommonJS"");","
|
||||
")
|
||||
#20003=@"loc,{#10000},1,1,1,30"
|
||||
locations_default(#20003,#10000,1,1,1,30)
|
||||
hasLocation(#20002,#20003)
|
||||
numlines(#20001,1,1,0)
|
||||
#20004=*
|
||||
tokeninfo(#20004,6,#20001,0,"console")
|
||||
#20005=@"loc,{#10000},1,1,1,7"
|
||||
locations_default(#20005,#10000,1,1,1,7)
|
||||
hasLocation(#20004,#20005)
|
||||
#20006=*
|
||||
tokeninfo(#20006,8,#20001,1,".")
|
||||
#20007=@"loc,{#10000},1,8,1,8"
|
||||
locations_default(#20007,#10000,1,8,1,8)
|
||||
hasLocation(#20006,#20007)
|
||||
#20008=*
|
||||
tokeninfo(#20008,6,#20001,2,"log")
|
||||
#20009=@"loc,{#10000},1,9,1,11"
|
||||
locations_default(#20009,#10000,1,9,1,11)
|
||||
hasLocation(#20008,#20009)
|
||||
#20010=*
|
||||
tokeninfo(#20010,8,#20001,3,"(")
|
||||
#20011=@"loc,{#10000},1,12,1,12"
|
||||
locations_default(#20011,#10000,1,12,1,12)
|
||||
hasLocation(#20010,#20011)
|
||||
#20012=*
|
||||
tokeninfo(#20012,4,#20001,4,"""Hello CommonJS""")
|
||||
#20013=@"loc,{#10000},1,13,1,28"
|
||||
locations_default(#20013,#10000,1,13,1,28)
|
||||
hasLocation(#20012,#20013)
|
||||
#20014=*
|
||||
tokeninfo(#20014,8,#20001,5,")")
|
||||
#20015=@"loc,{#10000},1,29,1,29"
|
||||
locations_default(#20015,#10000,1,29,1,29)
|
||||
hasLocation(#20014,#20015)
|
||||
#20016=*
|
||||
tokeninfo(#20016,8,#20001,6,";")
|
||||
#20017=@"loc,{#10000},1,30,1,30"
|
||||
locations_default(#20017,#10000,1,30,1,30)
|
||||
hasLocation(#20016,#20017)
|
||||
#20018=*
|
||||
tokeninfo(#20018,0,#20001,7,"")
|
||||
#20019=@"loc,{#10000},2,1,2,0"
|
||||
locations_default(#20019,#10000,2,1,2,0)
|
||||
hasLocation(#20018,#20019)
|
||||
toplevels(#20001,0)
|
||||
#20020=@"loc,{#10000},1,1,2,0"
|
||||
locations_default(#20020,#10000,1,1,2,0)
|
||||
hasLocation(#20001,#20020)
|
||||
#20021=@"module;{#10000},1,1"
|
||||
scopes(#20021,3)
|
||||
scopenodes(#20001,#20021)
|
||||
scopenesting(#20021,#20000)
|
||||
#20022=@"var;{require};{#20021}"
|
||||
variables(#20022,"require",#20021)
|
||||
#20023=@"var;{module};{#20021}"
|
||||
variables(#20023,"module",#20021)
|
||||
#20024=@"var;{exports};{#20021}"
|
||||
variables(#20024,"exports",#20021)
|
||||
#20025=@"var;{__filename};{#20021}"
|
||||
variables(#20025,"__filename",#20021)
|
||||
#20026=@"var;{__dirname};{#20021}"
|
||||
variables(#20026,"__dirname",#20021)
|
||||
#20027=@"var;{arguments};{#20021}"
|
||||
variables(#20027,"arguments",#20021)
|
||||
isModule(#20001)
|
||||
#20028=*
|
||||
stmts(#20028,2,#20001,0,"console ... onJS"");")
|
||||
hasLocation(#20028,#20003)
|
||||
stmtContainers(#20028,#20001)
|
||||
#20029=*
|
||||
exprs(#20029,13,#20028,0,"console ... monJS"")")
|
||||
#20030=@"loc,{#10000},1,1,1,29"
|
||||
locations_default(#20030,#10000,1,1,1,29)
|
||||
hasLocation(#20029,#20030)
|
||||
enclosingStmt(#20029,#20028)
|
||||
exprContainers(#20029,#20001)
|
||||
#20031=*
|
||||
exprs(#20031,14,#20029,-1,"console.log")
|
||||
#20032=@"loc,{#10000},1,1,1,11"
|
||||
locations_default(#20032,#10000,1,1,1,11)
|
||||
hasLocation(#20031,#20032)
|
||||
enclosingStmt(#20031,#20028)
|
||||
exprContainers(#20031,#20001)
|
||||
#20033=*
|
||||
exprs(#20033,79,#20031,0,"console")
|
||||
hasLocation(#20033,#20005)
|
||||
enclosingStmt(#20033,#20028)
|
||||
exprContainers(#20033,#20001)
|
||||
literals("console","console",#20033)
|
||||
#20034=@"var;{console};{#20000}"
|
||||
variables(#20034,"console",#20000)
|
||||
bind(#20033,#20034)
|
||||
#20035=*
|
||||
exprs(#20035,0,#20031,1,"log")
|
||||
hasLocation(#20035,#20009)
|
||||
enclosingStmt(#20035,#20028)
|
||||
exprContainers(#20035,#20001)
|
||||
literals("log","log",#20035)
|
||||
#20036=*
|
||||
exprs(#20036,4,#20029,0,"""Hello CommonJS""")
|
||||
hasLocation(#20036,#20013)
|
||||
enclosingStmt(#20036,#20028)
|
||||
exprContainers(#20036,#20001)
|
||||
literals("Hello CommonJS","""Hello CommonJS""",#20036)
|
||||
#20037=*
|
||||
regexpterm(#20037,14,#20036,0,"Hello CommonJS")
|
||||
#20038=@"loc,{#10000},1,14,1,27"
|
||||
locations_default(#20038,#10000,1,14,1,27)
|
||||
hasLocation(#20037,#20038)
|
||||
regexpConstValue(#20037,"Hello CommonJS")
|
||||
#20039=*
|
||||
entry_cfg_node(#20039,#20001)
|
||||
#20040=@"loc,{#10000},1,1,1,0"
|
||||
locations_default(#20040,#10000,1,1,1,0)
|
||||
hasLocation(#20039,#20040)
|
||||
#20041=*
|
||||
exit_cfg_node(#20041,#20001)
|
||||
hasLocation(#20041,#20019)
|
||||
successor(#20028,#20033)
|
||||
successor(#20036,#20029)
|
||||
successor(#20035,#20031)
|
||||
successor(#20033,#20035)
|
||||
successor(#20031,#20036)
|
||||
successor(#20029,#20041)
|
||||
successor(#20039,#20028)
|
||||
numlines(#10000,1,1,0)
|
||||
filetype(#10000,"javascript")
|
||||
@@ -311,10 +311,10 @@ scopenodes(#20001,#20112)
|
||||
scopenesting(#20112,#20000)
|
||||
isModule(#20001)
|
||||
isES2015Module(#20001)
|
||||
#20113=@"var;{fun};{#20112}"
|
||||
variables(#20113,"fun",#20112)
|
||||
#20114=@"var;{Class};{#20112}"
|
||||
variables(#20114,"Class",#20112)
|
||||
#20113=@"var;{Class};{#20112}"
|
||||
variables(#20113,"Class",#20112)
|
||||
#20114=@"var;{fun};{#20112}"
|
||||
variables(#20114,"fun",#20112)
|
||||
#20115=@"var;{Class2};{#20112}"
|
||||
variables(#20115,"Class2",#20112)
|
||||
#20116=@"local_type_name;{Class};{#20112}"
|
||||
@@ -347,7 +347,7 @@ hasLocation(#20123,#20037)
|
||||
enclosingStmt(#20123,#20118)
|
||||
exprContainers(#20123,#20001)
|
||||
literals("Class","Class",#20123)
|
||||
decl(#20123,#20114)
|
||||
decl(#20123,#20113)
|
||||
typedecl(#20123,#20116)
|
||||
#20124=*
|
||||
scopes(#20124,10)
|
||||
@@ -499,7 +499,7 @@ exprs(#20161,78,#20159,-1,"fun")
|
||||
hasLocation(#20161,#20086)
|
||||
exprContainers(#20161,#20159)
|
||||
literals("fun","fun",#20161)
|
||||
decl(#20161,#20113)
|
||||
decl(#20161,#20114)
|
||||
#20162=*
|
||||
scopes(#20162,1)
|
||||
scopenodes(#20159,#20162)
|
||||
|
||||
@@ -155,12 +155,12 @@ scopenodes(#20001,#20055)
|
||||
scopenesting(#20055,#20000)
|
||||
isModule(#20001)
|
||||
isES2015Module(#20001)
|
||||
#20056=@"var;{f};{#20055}"
|
||||
variables(#20056,"f",#20055)
|
||||
#20057=@"var;{foo};{#20055}"
|
||||
variables(#20057,"foo",#20055)
|
||||
#20058=@"var;{C};{#20055}"
|
||||
variables(#20058,"C",#20055)
|
||||
#20056=@"var;{foo};{#20055}"
|
||||
variables(#20056,"foo",#20055)
|
||||
#20057=@"var;{C};{#20055}"
|
||||
variables(#20057,"C",#20055)
|
||||
#20058=@"var;{f};{#20055}"
|
||||
variables(#20058,"f",#20055)
|
||||
#20059=@"local_type_name;{C};{#20055}"
|
||||
local_type_names(#20059,"C",#20055)
|
||||
#20060=*
|
||||
@@ -186,7 +186,7 @@ hasLocation(#20065,#20017)
|
||||
enclosingStmt(#20065,#20061)
|
||||
exprContainers(#20065,#20001)
|
||||
literals("foo","foo",#20065)
|
||||
decl(#20065,#20057)
|
||||
decl(#20065,#20056)
|
||||
#20066=*
|
||||
exprs(#20066,3,#20063,1,"42")
|
||||
hasLocation(#20066,#20021)
|
||||
@@ -209,7 +209,7 @@ hasLocation(#20070,#20029)
|
||||
enclosingStmt(#20070,#20068)
|
||||
exprContainers(#20070,#20001)
|
||||
literals("C","C",#20070)
|
||||
decl(#20070,#20058)
|
||||
decl(#20070,#20057)
|
||||
typedecl(#20070,#20059)
|
||||
#20071=*
|
||||
scopes(#20071,10)
|
||||
@@ -260,7 +260,7 @@ exprs(#20083,78,#20081,-1,"f")
|
||||
hasLocation(#20083,#20041)
|
||||
exprContainers(#20083,#20081)
|
||||
literals("f","f",#20083)
|
||||
decl(#20083,#20056)
|
||||
decl(#20083,#20058)
|
||||
#20084=*
|
||||
scopes(#20084,1)
|
||||
scopenodes(#20081,#20084)
|
||||
|
||||
@@ -15,18 +15,18 @@ import DataFlow::PathGraph
|
||||
/**
|
||||
* An instance of `mysql.createConnection()`, tracked globally.
|
||||
*/
|
||||
class MysqlConnection extends TrackedNode {
|
||||
MysqlConnection() { this = moduleImport("mysql").getAMemberCall("createConnection") }
|
||||
|
||||
/**
|
||||
* Gets a call to the `query` method on this connection object.
|
||||
*/
|
||||
MethodCallNode getAQueryCall() {
|
||||
this.flowsTo(result.getReceiver()) and
|
||||
result.getMethodName() = "query"
|
||||
}
|
||||
DataFlow::SourceNode mysqlConnection(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = moduleImport("mysql").getAMemberCall("createConnection")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = mysqlConnection(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of `mysql.createConnection()`, tracked globally.
|
||||
*/
|
||||
DataFlow::SourceNode mysqlConnection() { result = mysqlConnection(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* Data returned from a MySQL query.
|
||||
*
|
||||
@@ -42,7 +42,7 @@ class MysqlConnection extends TrackedNode {
|
||||
* ```
|
||||
*/
|
||||
class MysqlSource extends StoredXss::Source {
|
||||
MysqlSource() { this = any(MysqlConnection con).getAQueryCall().getCallback(1).getParameter(1) }
|
||||
MysqlSource() { this = mysqlConnection().getAMethodCall("query").getCallback(1).getParameter(1) }
|
||||
}
|
||||
|
||||
from StoredXss::Configuration cfg, PathNode source, PathNode sink
|
||||
@@ -24,5 +24,6 @@ where
|
||||
// ignore ambient, abstract, and overloaded declarations in TypeScript
|
||||
f.hasBody() and
|
||||
g.hasBody()
|
||||
select f.getId(), "Declaration of " + f.describe() + " conflicts with $@ in the same scope.",
|
||||
g.getId(), "another declaration"
|
||||
select f.getIdentifier(),
|
||||
"Declaration of " + f.describe() + " conflicts with $@ in the same scope.", g.getIdentifier(),
|
||||
"another declaration"
|
||||
|
||||
@@ -42,11 +42,11 @@ where
|
||||
access.isAmbient()
|
||||
) and
|
||||
// don't flag function expressions
|
||||
not exists(FunctionExpr fe | dead = fe.getId()) and
|
||||
not exists(FunctionExpr fe | dead = fe.getIdentifier()) and
|
||||
// don't flag function declarations nested inside blocks or other compound statements;
|
||||
// their meaning is only partially specified by the standard
|
||||
not exists(FunctionDeclStmt fd, StmtContainer outer |
|
||||
dead = fd.getId() and outer = fd.getEnclosingContainer()
|
||||
dead = fd.getIdentifier() and outer = fd.getEnclosingContainer()
|
||||
|
|
||||
not fd = outer.getBody().(BlockStmt).getAStmt()
|
||||
) and
|
||||
|
||||
@@ -22,6 +22,6 @@ where
|
||||
not decl.isAmbient() and
|
||||
not redecl.isAmbient() and
|
||||
// Redeclaring a namespace extends the previous definition.
|
||||
not decl = any(NamespaceDeclaration ns).getId() and
|
||||
not redecl = any(NamespaceDeclaration ns).getId()
|
||||
not decl = any(NamespaceDeclaration ns).getIdentifier() and
|
||||
not redecl = any(NamespaceDeclaration ns).getIdentifier()
|
||||
select redecl, "This variable has already been declared $@.", decl, "here"
|
||||
|
||||
@@ -93,7 +93,7 @@ predicate isEnumMember(VarDecl decl) { decl = any(EnumMember member).getIdentifi
|
||||
* "function f", "variable v" or "class c".
|
||||
*/
|
||||
string describeVarDecl(VarDecl vd) {
|
||||
if vd = any(Function f).getId()
|
||||
if vd = any(Function f).getIdentifier()
|
||||
then result = "function " + vd.getName()
|
||||
else
|
||||
if vd = any(ClassDefinition c).getIdentifier()
|
||||
@@ -115,7 +115,7 @@ class ImportVarDeclProvider extends Stmt {
|
||||
*/
|
||||
VarDecl getAVarDecl() {
|
||||
result = this.(ImportDeclaration).getASpecifier().getLocal() or
|
||||
result = this.(ImportEqualsDeclaration).getId()
|
||||
result = this.(ImportEqualsDeclaration).getIdentifier()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,7 @@ private import semmle.javascript.dataflow.InferredTypes
|
||||
* if it is part of a const enum access, so we conservatively silence the alert in that case.
|
||||
*/
|
||||
predicate namespaceOrConstEnumAccess(VarAccess e) {
|
||||
exists(NamespaceDeclaration decl | e.getVariable().getADeclaration() = decl.getId())
|
||||
exists(NamespaceDeclaration decl | e.getVariable().getADeclaration() = decl.getIdentifier())
|
||||
or
|
||||
exists(EnumDeclaration decl | e.getVariable().getADeclaration() = decl.getIdentifier() |
|
||||
decl.isConst()
|
||||
|
||||
@@ -43,6 +43,10 @@ where
|
||||
or
|
||||
// target is a HTTP URL to a domain on any TLD
|
||||
target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/?")
|
||||
or
|
||||
// target is a HTTP URL to a domain on any TLD with path elements, and the check is an includes check
|
||||
check instanceof StringOps::Includes and
|
||||
target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/[a-z0-9/_-]+")
|
||||
) and
|
||||
(
|
||||
if check instanceof StringOps::StartsWith
|
||||
|
||||
@@ -67,7 +67,7 @@ predicate isBackslashEscape(StringReplaceCall mce, DataFlow::RegExpLiteralNode r
|
||||
*/
|
||||
predicate allBackslashesEscaped(DataFlow::Node nd) {
|
||||
// `JSON.stringify` escapes backslashes
|
||||
nd = DataFlow::globalVarRef("JSON").getAMemberCall("stringify")
|
||||
nd instanceof JsonStringifyCall
|
||||
or
|
||||
// check whether `nd` itself escapes backslashes
|
||||
exists(DataFlow::RegExpLiteralNode rel | isBackslashEscape(nd, rel) |
|
||||
|
||||
@@ -34,6 +34,7 @@ import semmle.javascript.InclusionTests
|
||||
import semmle.javascript.JSDoc
|
||||
import semmle.javascript.JSON
|
||||
import semmle.javascript.JsonParsers
|
||||
import semmle.javascript.JsonStringifiers
|
||||
import semmle.javascript.JSX
|
||||
import semmle.javascript.Lines
|
||||
import semmle.javascript.Locations
|
||||
|
||||
@@ -30,15 +30,17 @@ private predicate defn(ControlFlowNode def, Expr lhs, AST::ValueNode rhs) {
|
||||
or
|
||||
exists(VariableDeclarator vd | def = vd | lhs = vd.getBindingPattern() and rhs = vd.getInit())
|
||||
or
|
||||
exists(Function f | def = f.getId() | lhs = def and rhs = f)
|
||||
exists(Function f | def = f.getIdentifier() | lhs = def and rhs = f)
|
||||
or
|
||||
exists(ClassDefinition c | lhs = c.getIdentifier() | def = c and rhs = c and not c.isAmbient())
|
||||
or
|
||||
exists(NamespaceDeclaration n | def = n | lhs = n.getId() and rhs = n)
|
||||
exists(NamespaceDeclaration n | def = n | lhs = n.getIdentifier() and rhs = n)
|
||||
or
|
||||
exists(EnumDeclaration ed | def = ed.getIdentifier() | lhs = def and rhs = ed)
|
||||
or
|
||||
exists(ImportEqualsDeclaration i | def = i | lhs = i.getId() and rhs = i.getImportedEntity())
|
||||
exists(ImportEqualsDeclaration i | def = i |
|
||||
lhs = i.getIdentifier() and rhs = i.getImportedEntity()
|
||||
)
|
||||
or
|
||||
exists(ImportSpecifier i | def = i | lhs = i.getLocal() and rhs = i)
|
||||
or
|
||||
@@ -149,7 +151,7 @@ class RValue extends RefExpr {
|
||||
or
|
||||
this = any(UpdateExpr u).getOperand().getUnderlyingReference()
|
||||
or
|
||||
this = any(NamespaceDeclaration decl).getId()
|
||||
this = any(NamespaceDeclaration decl).getIdentifier()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -330,7 +330,7 @@ class ExportDefaultDeclaration extends ExportDeclaration, @exportdefaultdeclarat
|
||||
/** Gets the declaration, if any, exported by this default export. */
|
||||
VarDecl getADecl() {
|
||||
exists(ExprOrStmt op | op = getOperand() |
|
||||
result = op.(FunctionDeclStmt).getId() or
|
||||
result = op.(FunctionDeclStmt).getIdentifier() or
|
||||
result = op.(ClassDeclStmt).getIdentifier()
|
||||
)
|
||||
}
|
||||
@@ -364,13 +364,13 @@ class ExportNamedDeclaration extends ExportDeclaration, @exportnameddeclaration
|
||||
Identifier getAnExportedDecl() {
|
||||
exists(ExprOrStmt op | op = getOperand() |
|
||||
result = op.(DeclStmt).getADecl().getBindingPattern().getABindingVarRef() or
|
||||
result = op.(FunctionDeclStmt).getId() or
|
||||
result = op.(FunctionDeclStmt).getIdentifier() or
|
||||
result = op.(ClassDeclStmt).getIdentifier() or
|
||||
result = op.(NamespaceDeclaration).getId() or
|
||||
result = op.(NamespaceDeclaration).getIdentifier() or
|
||||
result = op.(EnumDeclaration).getIdentifier() or
|
||||
result = op.(InterfaceDeclaration).getIdentifier() or
|
||||
result = op.(TypeAliasDeclaration).getIdentifier() or
|
||||
result = op.(ImportEqualsDeclaration).getId()
|
||||
result = op.(ImportEqualsDeclaration).getIdentifier()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -77,8 +77,15 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
|
||||
result = getDocumentation().getATagByTitle("this").getType()
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getIdentifier()` instead.
|
||||
*
|
||||
* Gets the identifier specifying the name of this function, if any.
|
||||
*/
|
||||
deprecated VarDecl getId() { result = getIdentifier() }
|
||||
|
||||
/** Gets the identifier specifying the name of this function, if any. */
|
||||
VarDecl getId() { result = getChildExpr(-1) }
|
||||
VarDecl getIdentifier() { result = getChildExpr(-1) }
|
||||
|
||||
/**
|
||||
* Gets the name of this function if it has one, or a name inferred from its context.
|
||||
@@ -89,9 +96,9 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
|
||||
* can be inferred, there is no result.
|
||||
*/
|
||||
string getName() {
|
||||
result = getId().getName()
|
||||
result = getIdentifier().getName()
|
||||
or
|
||||
not exists(getId()) and
|
||||
not exists(getIdentifier()) and
|
||||
(
|
||||
exists(VarDef vd | this = vd.getSource() | result = vd.getTarget().(VarRef).getName())
|
||||
or
|
||||
@@ -111,7 +118,7 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
|
||||
}
|
||||
|
||||
/** Gets the variable holding this function. */
|
||||
Variable getVariable() { result = getId().getVariable() }
|
||||
Variable getVariable() { result = getIdentifier().getVariable() }
|
||||
|
||||
/** Gets the `arguments` variable of this function, if any. */
|
||||
ArgumentsVariable getArgumentsVariable() { result.getFunction() = this }
|
||||
@@ -183,11 +190,11 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
|
||||
not exists(getAParameter()) and
|
||||
(
|
||||
// if the function has a name, the opening parenthesis comes right after it
|
||||
result = getId().getLastToken().getNextToken()
|
||||
result = getIdentifier().getLastToken().getNextToken()
|
||||
or
|
||||
// otherwise this must be an arrow function with no parameters, so the opening
|
||||
// parenthesis is the very first token of the function
|
||||
not exists(getId()) and result = getFirstToken()
|
||||
not exists(getIdentifier()) and result = getFirstToken()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -309,8 +316,8 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
|
||||
*/
|
||||
private string inferNameFromVarDef() {
|
||||
// in ambiguous cases like `var f = function g() {}`, prefer `g` to `f`
|
||||
if exists(getId())
|
||||
then result = "function " + getId().getName()
|
||||
if exists(getIdentifier())
|
||||
then result = "function " + getIdentifier().getName()
|
||||
else
|
||||
exists(VarDef vd | this = vd.getSource() |
|
||||
result = "function " + vd.getTarget().(VarRef).getName()
|
||||
|
||||
@@ -267,7 +267,7 @@ module AccessPath {
|
||||
or
|
||||
exists(FunctionDeclStmt fun |
|
||||
node = DataFlow::valueNode(fun) and
|
||||
result = fun.getId().(GlobalVarDecl).getName() and
|
||||
result = fun.getIdentifier().(GlobalVarDecl).getName() and
|
||||
root.isGlobal()
|
||||
)
|
||||
or
|
||||
@@ -285,7 +285,7 @@ module AccessPath {
|
||||
or
|
||||
exists(NamespaceDeclaration decl |
|
||||
node = DataFlow::valueNode(decl) and
|
||||
result = decl.getId().(GlobalVarDecl).getName() and
|
||||
result = decl.getIdentifier().(GlobalVarDecl).getName() and
|
||||
root.isGlobal()
|
||||
)
|
||||
}
|
||||
|
||||
34
javascript/ql/src/semmle/javascript/JsonStringifiers.qll
Normal file
34
javascript/ql/src/semmle/javascript/JsonStringifiers.qll
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Provides classes for working with JSON serializers.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A call to a JSON stringifier such as `JSON.stringify` or `require("util").inspect`.
|
||||
*/
|
||||
class JsonStringifyCall extends DataFlow::CallNode {
|
||||
JsonStringifyCall() {
|
||||
exists(DataFlow::SourceNode callee | this = callee.getACall() |
|
||||
callee = DataFlow::globalVarRef("JSON").getAPropertyRead("stringify") or
|
||||
callee = DataFlow::moduleMember("json3", "stringify") or
|
||||
callee =
|
||||
DataFlow::moduleImport(["json-stringify-safe", "json-stable-stringify", "stringify-object",
|
||||
"fast-json-stable-stringify", "fast-safe-stringify", "javascript-stringify",
|
||||
"js-stringify"]) or
|
||||
// require("util").inspect() and similar
|
||||
callee = DataFlow::moduleMember("util", "inspect") or
|
||||
callee = DataFlow::moduleImport(["pretty-format", "object-inspect"])
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data flow node holding the input object to be stringified.
|
||||
*/
|
||||
DataFlow::Node getInput() { result = getArgument(0) }
|
||||
|
||||
/**
|
||||
* Gets the data flow node holding the resulting string.
|
||||
*/
|
||||
DataFlow::SourceNode getOutput() { result = this }
|
||||
}
|
||||
@@ -221,7 +221,7 @@ class Require extends CallExpr, Import {
|
||||
*
|
||||
* <ul>
|
||||
* <li> the file `c/p`;
|
||||
* <li> the file `c/p.{tsx,ts,jsx,es6,es,mjs}`;
|
||||
* <li> the file `c/p.{tsx,ts,jsx,es6,es,mjs,cjs}`;
|
||||
* <li> the file `c/p.js`;
|
||||
* <li> the file `c/p.json`;
|
||||
* <li> the file `c/p.node`;
|
||||
@@ -230,12 +230,12 @@ class Require extends CallExpr, Import {
|
||||
* <li> if `c/p/package.json` exists and specifies a `main` module `m`:
|
||||
* <ul>
|
||||
* <li> the file `c/p/m`;
|
||||
* <li> the file `c/p/m.{tsx,ts,jsx,es6,es,mjs}`;
|
||||
* <li> the file `c/p/m.{tsx,ts,jsx,es6,es,mjs,cjs}`;
|
||||
* <li> the file `c/p/m.js`;
|
||||
* <li> the file `c/p/m.json`;
|
||||
* <li> the file `c/p/m.node`;
|
||||
* </ul>
|
||||
* <li> the file `c/p/index.{tsx,ts,jsx,es6,es,mjs}`;
|
||||
* <li> the file `c/p/index.{tsx,ts,jsx,es6,es,mjs,cjs}`;
|
||||
* <li> the file `c/p/index.js`;
|
||||
* <li> the file `c/p/index.json`;
|
||||
* <li> the file `c/p/index.node`.
|
||||
|
||||
@@ -26,11 +26,13 @@ int getFileExtensionPriority(string ext) {
|
||||
or
|
||||
ext = "mjs" and result = 5
|
||||
or
|
||||
ext = "js" and result = 6
|
||||
ext = "cjs" and result = 6
|
||||
or
|
||||
ext = "json" and result = 7
|
||||
ext = "js" and result = 7
|
||||
or
|
||||
ext = "node" and result = 8
|
||||
ext = "json" and result = 8
|
||||
or
|
||||
ext = "node" and result = 9
|
||||
}
|
||||
|
||||
int prioritiesPerCandidate() { result = 3 * (numberOfExtensions() + 1) }
|
||||
|
||||
@@ -8,13 +8,16 @@ import javascript
|
||||
*/
|
||||
class NamespaceDefinition extends Stmt, @namespacedefinition, AST::ValueNode {
|
||||
/**
|
||||
* DEPRECATED: Use `getIdentifier()` instead.
|
||||
*
|
||||
* Gets the identifier naming the namespace.
|
||||
*/
|
||||
Identifier getId() {
|
||||
result = this.(NamespaceDeclaration).getId()
|
||||
or
|
||||
result = this.(EnumDeclaration).getIdentifier()
|
||||
}
|
||||
deprecated Identifier getId() { result = getIdentifier() }
|
||||
|
||||
/**
|
||||
* Gets the identifier naming the namespace.
|
||||
*/
|
||||
Identifier getIdentifier() { none() } // Overridden in subtypes.
|
||||
|
||||
/**
|
||||
* Gets unqualified name of the namespace being defined.
|
||||
@@ -29,7 +32,7 @@ class NamespaceDefinition extends Stmt, @namespacedefinition, AST::ValueNode {
|
||||
* Gets the local namespace name induced by this namespace.
|
||||
*/
|
||||
LocalNamespaceName getLocalNamespaceName() {
|
||||
result = getId().(LocalNamespaceDecl).getLocalNamespaceName()
|
||||
result = getIdentifier().(LocalNamespaceDecl).getLocalNamespaceName()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,10 +58,10 @@ class NamespaceDefinition extends Stmt, @namespacedefinition, AST::ValueNode {
|
||||
*/
|
||||
class NamespaceDeclaration extends NamespaceDefinition, StmtContainer, @namespacedeclaration {
|
||||
/** Gets the name of this namespace. */
|
||||
override Identifier getId() { result = getChildExpr(-1) }
|
||||
override Identifier getIdentifier() { result = getChildExpr(-1) }
|
||||
|
||||
/** Gets the name of this namespace as a string. */
|
||||
override string getName() { result = getId().getName() }
|
||||
override string getName() { result = getIdentifier().getName() }
|
||||
|
||||
/** Gets the `i`th statement in this namespace. */
|
||||
Stmt getStmt(int i) {
|
||||
@@ -83,7 +86,7 @@ class NamespaceDeclaration extends NamespaceDefinition, StmtContainer, @namespac
|
||||
predicate isInstantiated() { isInstantiated(this) }
|
||||
|
||||
override ControlFlowNode getFirstControlFlowNode() {
|
||||
if hasDeclareKeyword(this) then result = this else result = getId()
|
||||
if hasDeclareKeyword(this) then result = this else result = getIdentifier()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,13 +181,20 @@ class GlobalAugmentationDeclaration extends Stmt, StmtContainer, @globalaugmenta
|
||||
|
||||
/** A TypeScript "import-equals" declaration. */
|
||||
class ImportEqualsDeclaration extends Stmt, @importequalsdeclaration {
|
||||
/**
|
||||
* DEPRECATED: Use `getIdentifier()` instead.
|
||||
*
|
||||
* Gets the name under which the imported entity is imported.
|
||||
*/
|
||||
deprecated Identifier getId() { result = getIdentifier() }
|
||||
|
||||
/** Gets the name under which the imported entity is imported. */
|
||||
Identifier getId() { result = getChildExpr(0) }
|
||||
Identifier getIdentifier() { result = getChildExpr(0) }
|
||||
|
||||
/** Gets the expression specifying the imported module or entity. */
|
||||
Expr getImportedEntity() { result = getChildExpr(1) }
|
||||
|
||||
override ControlFlowNode getFirstControlFlowNode() { result = getId() }
|
||||
override ControlFlowNode getFirstControlFlowNode() { result = getIdentifier() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,7 +358,7 @@ class TypeDecl extends Identifier, TypeRef, LexicalDecl {
|
||||
this = any(ClassOrInterface ci).getIdentifier() or
|
||||
this = any(TypeParameter tp).getIdentifier() or
|
||||
this = any(ImportSpecifier im).getLocal() or
|
||||
this = any(ImportEqualsDeclaration im).getId() or
|
||||
this = any(ImportEqualsDeclaration im).getIdentifier() or
|
||||
this = any(TypeAliasDeclaration td).getIdentifier() or
|
||||
this = any(EnumDeclaration ed).getIdentifier() or
|
||||
this = any(EnumMember member).getIdentifier()
|
||||
@@ -1226,8 +1236,8 @@ abstract class NamespaceRef extends ASTNode { }
|
||||
*/
|
||||
class LocalNamespaceDecl extends VarDecl, NamespaceRef {
|
||||
LocalNamespaceDecl() {
|
||||
any(NamespaceDeclaration nd).getId() = this or
|
||||
any(ImportEqualsDeclaration im).getId() = this or
|
||||
any(NamespaceDeclaration nd).getIdentifier() = this or
|
||||
any(ImportEqualsDeclaration im).getIdentifier() = this or
|
||||
any(ImportSpecifier im).getLocal() = this or
|
||||
any(EnumDeclaration ed).getIdentifier() = this
|
||||
}
|
||||
@@ -1325,7 +1335,7 @@ class ImportVarTypeAccess extends VarTypeAccess, ImportTypeExpr, @importvartypea
|
||||
*/
|
||||
class EnumDeclaration extends NamespaceDefinition, @enumdeclaration, AST::ValueNode {
|
||||
/** Gets the name of this enum, such as `E` in `enum E { A, B }`. */
|
||||
Identifier getIdentifier() { result = getChildExpr(0) }
|
||||
override Identifier getIdentifier() { result = getChildExpr(0) }
|
||||
|
||||
/** Gets the name of this enum as a string. */
|
||||
override string getName() { result = getIdentifier().getName() }
|
||||
|
||||
@@ -312,8 +312,11 @@ class LocalVariable extends Variable {
|
||||
this = result.getScope().getAVariable()
|
||||
or
|
||||
exists(VarDecl d | d = getADeclaration() |
|
||||
if d = any(FunctionDeclStmt fds).getId()
|
||||
then exists(FunctionDeclStmt fds | d = fds.getId() | result = fds.getEnclosingContainer())
|
||||
if d = any(FunctionDeclStmt fds).getIdentifier()
|
||||
then
|
||||
exists(FunctionDeclStmt fds | d = fds.getIdentifier() |
|
||||
result = fds.getEnclosingContainer()
|
||||
)
|
||||
else result = d.getContainer()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Provides support for intra-procedural tracking of a customizable
|
||||
* set of data flow nodes.
|
||||
*
|
||||
* Note that unlike `TrackedNodes`, this library only performs
|
||||
* Note that unlike `TypeTracking.qll`, this library only performs
|
||||
* local tracking within a function.
|
||||
*/
|
||||
|
||||
|
||||
@@ -543,8 +543,8 @@ module TaintTracking {
|
||||
/**
|
||||
* A taint propagating data flow edge arising from JSON unparsing.
|
||||
*/
|
||||
private class JsonStringifyTaintStep extends AdditionalTaintStep, DataFlow::MethodCallNode {
|
||||
JsonStringifyTaintStep() { this = DataFlow::globalVarRef("JSON").getAMemberCall("stringify") }
|
||||
private class JsonStringifyTaintStep extends AdditionalTaintStep, DataFlow::CallNode {
|
||||
JsonStringifyTaintStep() { this instanceof JsonStringifyCall }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred = getArgument(0) and succ = this
|
||||
@@ -693,18 +693,6 @@ module TaintTracking {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint step through the Node.JS function `util.inspect(..)`.
|
||||
*/
|
||||
class UtilInspectTaintStep extends AdditionalTaintStep, DataFlow::InvokeNode {
|
||||
UtilInspectTaintStep() { this = DataFlow::moduleImport("util").getAMemberCall("inspect") }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ = this and
|
||||
this.getAnArgument() = pred
|
||||
}
|
||||
}
|
||||
|
||||
private module RegExpCaptureSteps {
|
||||
/** Gets a reference to a string derived from the most recent RegExp match, such as `RegExp.$1`. */
|
||||
private DataFlow::PropRead getAStaticCaptureRef() {
|
||||
|
||||
@@ -1,4 +1,52 @@
|
||||
/**
|
||||
* DEPRECATED: Use `TypeTracking.qll` instead.
|
||||
*
|
||||
* The following `TrackedNode` usage is usually equivalent to the type tracking usage below.
|
||||
*
|
||||
* ```
|
||||
* class MyTrackedNode extends TrackedNode {
|
||||
* MyTrackedNode() { isInteresting(this) }
|
||||
* }
|
||||
*
|
||||
* DataFlow::Node getMyTrackedNodeLocation(MyTrackedNode n) {
|
||||
* n.flowsTo(result)
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ```
|
||||
* DataFlow::SourceNode getMyTrackedNodeLocation(DataFlow::SourceNode start, DataFlow::TypeTracker t) {
|
||||
* t.start() and
|
||||
* isInteresting(result) and
|
||||
* result = start
|
||||
* or
|
||||
* exists (DataFlow::TypeTracker t2 |
|
||||
* result = getMyTrackedNodeLocation(start, t2).track(t2, t)
|
||||
* )
|
||||
* }
|
||||
*
|
||||
* DataFlow::SourceNode getMyTrackedNodeLocation(DataFlow::SourceNode n) {
|
||||
* result = getMyTrackedNodeLocation(n, DataFlow::TypeTracker::end())
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In rare cases, additional tracking is required, for instance when tracking string constants, and the following type tracking formulation is required instead.
|
||||
*
|
||||
* ```
|
||||
* DataFlow::Node getMyTrackedNodeLocation(DataFlow::Node start, DataFlow::TypeTracker t) {
|
||||
* t.start() and
|
||||
* isInteresting(result) and
|
||||
* result = start
|
||||
* or
|
||||
* exists(DataFlow::TypeTracker t2 |
|
||||
* t = t2.smallstep(getMyTrackedNodeLocation(start, t2), result)
|
||||
* )
|
||||
* }
|
||||
*
|
||||
* DataFlow::Node getMyTrackedNodeLocation(DataFlow::Node n) {
|
||||
* result = getMyTrackedNodeLocation(n, DataFlow::TypeTracker::end())
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Provides support for inter-procedural tracking of a customizable
|
||||
* set of data flow nodes.
|
||||
*/
|
||||
@@ -12,7 +60,7 @@ private import internal.FlowSteps as FlowSteps
|
||||
* To track additional values, extends this class with additional
|
||||
* subclasses.
|
||||
*/
|
||||
abstract class TrackedNode extends DataFlow::Node {
|
||||
abstract deprecated class TrackedNode extends DataFlow::Node {
|
||||
/**
|
||||
* Holds if this node flows into `sink` in zero or more (possibly
|
||||
* inter-procedural) steps.
|
||||
@@ -26,7 +74,7 @@ abstract class TrackedNode extends DataFlow::Node {
|
||||
* To track additional expressions, extends this class with additional
|
||||
* subclasses.
|
||||
*/
|
||||
abstract class TrackedExpr extends Expr {
|
||||
abstract deprecated class TrackedExpr extends Expr {
|
||||
predicate flowsTo(Expr sink) {
|
||||
exists(TrackedExprNode ten | ten.asExpr() = this | ten.flowsTo(DataFlow::valueNode(sink)))
|
||||
}
|
||||
@@ -35,7 +83,7 @@ abstract class TrackedExpr extends Expr {
|
||||
/**
|
||||
* Turn all `TrackedExpr`s into `TrackedNode`s.
|
||||
*/
|
||||
private class TrackedExprNode extends TrackedNode {
|
||||
deprecated private class TrackedExprNode extends TrackedNode {
|
||||
TrackedExprNode() { asExpr() instanceof TrackedExpr }
|
||||
}
|
||||
|
||||
@@ -64,7 +112,9 @@ private module NodeTracking {
|
||||
*
|
||||
* Summary steps through function calls are not taken into account.
|
||||
*/
|
||||
private predicate basicFlowStep(DataFlow::Node pred, DataFlow::Node succ, PathSummary summary) {
|
||||
deprecated private predicate basicFlowStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, PathSummary summary
|
||||
) {
|
||||
isRelevant(pred) and
|
||||
(
|
||||
// Local flow
|
||||
@@ -94,7 +144,7 @@ private module NodeTracking {
|
||||
*
|
||||
* No call/return matching is done, so this is a relatively coarse over-approximation.
|
||||
*/
|
||||
private predicate isRelevant(DataFlow::Node nd) {
|
||||
deprecated private predicate isRelevant(DataFlow::Node nd) {
|
||||
nd instanceof TrackedNode
|
||||
or
|
||||
exists(DataFlow::Node mid | isRelevant(mid) |
|
||||
@@ -115,7 +165,7 @@ private module NodeTracking {
|
||||
* either `pred` is an argument of `f` and `succ` the corresponding parameter, or
|
||||
* `pred` is a variable definition whose value is captured by `f` at `succ`.
|
||||
*/
|
||||
private predicate callInputStep(
|
||||
deprecated private predicate callInputStep(
|
||||
Function f, DataFlow::Node invk, DataFlow::Node pred, DataFlow::Node succ
|
||||
) {
|
||||
isRelevant(pred) and
|
||||
@@ -136,7 +186,7 @@ private module NodeTracking {
|
||||
* that is captured by `f`, may flow to `nd` (possibly through callees, but not containing
|
||||
* any unmatched calls or returns) along a path summarized by `summary`.
|
||||
*/
|
||||
private predicate reachableFromInput(
|
||||
deprecated private predicate reachableFromInput(
|
||||
Function f, DataFlow::Node invk, DataFlow::Node input, DataFlow::Node nd, PathSummary summary
|
||||
) {
|
||||
callInputStep(f, invk, input, nd) and
|
||||
@@ -154,7 +204,7 @@ private module NodeTracking {
|
||||
* Holds if `nd` may flow into a return statement of `f`
|
||||
* (possibly through callees) along a path summarized by `summary`.
|
||||
*/
|
||||
private predicate reachesReturn(Function f, DataFlow::Node nd, PathSummary summary) {
|
||||
deprecated private predicate reachesReturn(Function f, DataFlow::Node nd, PathSummary summary) {
|
||||
returnExpr(f, nd, _) and
|
||||
summary = PathSummary::level()
|
||||
or
|
||||
@@ -170,7 +220,7 @@ private module NodeTracking {
|
||||
* which is either an argument or a definition captured by the function, flows,
|
||||
* possibly through callees.
|
||||
*/
|
||||
private predicate flowThroughCall(DataFlow::Node input, DataFlow::Node output) {
|
||||
deprecated private predicate flowThroughCall(DataFlow::Node input, DataFlow::Node output) {
|
||||
exists(Function f, DataFlow::ValueNode ret |
|
||||
ret.asExpr() = f.getAReturnedExpr() and
|
||||
reachableFromInput(f, output, input, ret, _)
|
||||
@@ -187,7 +237,7 @@ private module NodeTracking {
|
||||
/**
|
||||
* Holds if `pred` may flow into property `prop` of `succ` along a path summarized by `summary`.
|
||||
*/
|
||||
private predicate storeStep(
|
||||
deprecated private predicate storeStep(
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string prop, PathSummary summary
|
||||
) {
|
||||
basicStoreStep(pred, succ, prop) and
|
||||
@@ -210,7 +260,7 @@ private module NodeTracking {
|
||||
* Holds if property `prop` of `pred` may flow into `succ` along a path summarized by
|
||||
* `summary`.
|
||||
*/
|
||||
private predicate loadStep(
|
||||
deprecated private predicate loadStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string prop, PathSummary summary
|
||||
) {
|
||||
basicLoadStep(pred, succ, prop) and
|
||||
@@ -226,7 +276,7 @@ private module NodeTracking {
|
||||
* Holds if `rhs` is the right-hand side of a write to property `prop`, and `nd` is reachable
|
||||
* from the base of that write (possibly through callees) along a path summarized by `summary`.
|
||||
*/
|
||||
private predicate reachableFromStoreBase(
|
||||
deprecated private predicate reachableFromStoreBase(
|
||||
string prop, DataFlow::Node rhs, DataFlow::Node nd, PathSummary summary
|
||||
) {
|
||||
storeStep(rhs, nd, prop, summary)
|
||||
@@ -244,7 +294,7 @@ private module NodeTracking {
|
||||
*
|
||||
* In other words, `pred` may flow to `succ` through a property.
|
||||
*/
|
||||
private predicate flowThroughProperty(
|
||||
deprecated private predicate flowThroughProperty(
|
||||
DataFlow::Node pred, DataFlow::Node succ, PathSummary summary
|
||||
) {
|
||||
exists(string prop, DataFlow::Node base, PathSummary oldSummary, PathSummary newSummary |
|
||||
@@ -259,7 +309,7 @@ private module NodeTracking {
|
||||
* invokes `cb`, passing `arg` as its `i`th argument. `arg` flows along a path summarized
|
||||
* by `summary`, while `cb` is only tracked locally.
|
||||
*/
|
||||
private predicate summarizedHigherOrderCall(
|
||||
deprecated private predicate summarizedHigherOrderCall(
|
||||
DataFlow::Node arg, DataFlow::Node cb, int i, PathSummary summary
|
||||
) {
|
||||
exists(
|
||||
@@ -293,7 +343,7 @@ private module NodeTracking {
|
||||
* Alternatively, the callback can flow into a call `f(callback)` which itself provides the `arg`.
|
||||
* That is, `arg` refers to a value defined in `f` or one of its callees.
|
||||
*/
|
||||
predicate higherOrderCall(
|
||||
deprecated predicate higherOrderCall(
|
||||
DataFlow::Node arg, DataFlow::SourceNode callback, int i, PathSummary summary
|
||||
) {
|
||||
// Summarized call
|
||||
@@ -328,7 +378,7 @@ private module NodeTracking {
|
||||
* of `cb`. `arg` flows along a path summarized by `summary`, while `cb` is only tracked
|
||||
* locally.
|
||||
*/
|
||||
private predicate flowIntoHigherOrderCall(
|
||||
deprecated private predicate flowIntoHigherOrderCall(
|
||||
DataFlow::Node pred, DataFlow::Node succ, PathSummary summary
|
||||
) {
|
||||
exists(DataFlow::FunctionNode cb, int i, PathSummary oldSummary |
|
||||
@@ -341,7 +391,9 @@ private module NodeTracking {
|
||||
/**
|
||||
* Holds if there is a flow step from `pred` to `succ` described by `summary`.
|
||||
*/
|
||||
private predicate flowStep(DataFlow::Node pred, DataFlow::Node succ, PathSummary summary) {
|
||||
deprecated private predicate flowStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, PathSummary summary
|
||||
) {
|
||||
basicFlowStep(pred, succ, summary)
|
||||
or
|
||||
// Flow through a function that returns a value that depends on one of its arguments
|
||||
@@ -360,7 +412,7 @@ private module NodeTracking {
|
||||
* Holds if there is a path from `source` to `nd` along a path summarized by
|
||||
* `summary`.
|
||||
*/
|
||||
predicate flowsTo(TrackedNode source, DataFlow::Node nd, PathSummary summary) {
|
||||
deprecated predicate flowsTo(TrackedNode source, DataFlow::Node nd, PathSummary summary) {
|
||||
source = nd and
|
||||
summary = PathSummary::level()
|
||||
or
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Provides the `TypeTracker` class for tracking types interprocedurally.
|
||||
*
|
||||
* This provides an alternative to `DataFlow::TrackedNode` and `AbstractValue`
|
||||
* This provides an alternative to `AbstractValue`
|
||||
* for tracking certain types interprocedurally without computing which source
|
||||
* a given value came from.
|
||||
*/
|
||||
|
||||
@@ -111,7 +111,7 @@ private class AnalyzedNamespaceDeclaration extends DataFlow::AnalyzedValueNode {
|
||||
|
||||
AbstractValue getPreviousValue() {
|
||||
exists(AnalyzedSsaDefinition def |
|
||||
def.getVariable().getAUse() = astNode.getId() and
|
||||
def.getVariable().getAUse() = astNode.getIdentifier() and
|
||||
result = def.getAnRhsValue()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Provides classes for modelling cryptographic libraries.
|
||||
* Provides classes for modeling cryptographic libraries.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -28,9 +28,7 @@ private class RemoteFlowPassword extends HeuristicSource, RemoteFlowSource {
|
||||
*/
|
||||
private class JSONStringifyAsCommandInjectionSource extends HeuristicSource,
|
||||
CommandInjection::Source {
|
||||
JSONStringifyAsCommandInjectionSource() {
|
||||
this = DataFlow::globalVarRef("JSON").getAMemberCall("stringify")
|
||||
}
|
||||
JSONStringifyAsCommandInjectionSource() { this instanceof JsonStringifyCall }
|
||||
|
||||
override string getSourceType() { result = "a string from JSON.stringify" }
|
||||
}
|
||||
|
||||
@@ -202,10 +202,9 @@ module CleartextLogging {
|
||||
exists(DataFlow::PropWrite write, DataFlow::PropRead read |
|
||||
read = write.getRhs()
|
||||
or
|
||||
exists(DataFlow::MethodCallNode stringify |
|
||||
stringify = write.getRhs() and
|
||||
stringify = DataFlow::globalVarRef("JSON").getAMethodCall("stringify") and
|
||||
stringify.getArgument(0) = read
|
||||
exists(JsonStringifyCall stringify |
|
||||
stringify.getOutput() = write.getRhs() and
|
||||
stringify.getInput() = read
|
||||
)
|
||||
|
|
||||
not exists(write.getPropertyName()) and
|
||||
|
||||
@@ -36,7 +36,7 @@ module ImproperCodeSanitization {
|
||||
* A call to `JSON.stringify()` seen as a source for improper code sanitization
|
||||
*/
|
||||
class JSONStringifyAsSource extends Source {
|
||||
JSONStringifyAsSource() { this = DataFlow::globalVarRef("JSON").getAMemberCall("stringify") }
|
||||
JSONStringifyAsSource() { this instanceof JsonStringifyCall }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,8 +49,7 @@ module PostMessageStar {
|
||||
exists(DataFlow::InvokeNode toString | toString = trg |
|
||||
toString.(DataFlow::MethodCallNode).calls(src, "toString")
|
||||
or
|
||||
toString = DataFlow::globalVarRef("JSON").getAMemberCall("stringify") and
|
||||
src = toString.getArgument(0)
|
||||
src = toString.(JsonStringifyCall).getInput()
|
||||
) and
|
||||
inlbl instanceof PartiallyTaintedObject and
|
||||
outlbl.isTaint()
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
(function () {
|
||||
if (true) {
|
||||
function foo() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
return foo(); // this resolves to `foo` above, because we have function-scope in non-strict mode.
|
||||
})();
|
||||
@@ -0,0 +1,10 @@
|
||||
(function () {
|
||||
if (true) {
|
||||
function foo() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
return foo(); // `foo` is not defined, because we are in strict-mode.
|
||||
})();
|
||||
|
||||
export default 3; // strict-mode implied because ES2015 module.
|
||||
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
(function () {
|
||||
"use strict";
|
||||
if (true) {
|
||||
function foo() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
return foo(); // `foo` is not defined, because we are in strict-mode.
|
||||
})();
|
||||
|
||||
export default 3; // strict-mode implied because ES2015 module.
|
||||
@@ -91,6 +91,11 @@ test_getAFunctionValue
|
||||
| m.js:3:1:3:16 | module.exports.f | m.js:1:13:1:25 | function() {} |
|
||||
| n.js:2:1:2:3 | m.f | m.js:1:13:1:25 | function() {} |
|
||||
| n.js:5:1:5:4 | m2.f | m2.js:2:6:2:18 | function() {} |
|
||||
| non-strict.js:1:1:8:2 | (functi ... ode.\\n}) | non-strict.js:1:2:8:1 | functio ... mode.\\n} |
|
||||
| non-strict.js:1:2:8:1 | functio ... mode.\\n} | non-strict.js:1:2:8:1 | functio ... mode.\\n} |
|
||||
| non-strict.js:3:5:5:5 | functio ... ;\\n } | non-strict.js:3:5:5:5 | functio ... ;\\n } |
|
||||
| non-strict.js:3:14:3:16 | foo | non-strict.js:3:5:5:5 | functio ... ;\\n } |
|
||||
| non-strict.js:7:10:7:12 | foo | non-strict.js:3:5:5:5 | functio ... ;\\n } |
|
||||
| protoclass.js:3:1:5:1 | functio ... it();\\n} | protoclass.js:3:1:5:1 | functio ... it();\\n} |
|
||||
| protoclass.js:3:10:3:10 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} |
|
||||
| protoclass.js:4:3:4:11 | this.init | protoclass.js:7:20:11:1 | functio ... m();\\n} |
|
||||
@@ -111,6 +116,12 @@ test_getAFunctionValue
|
||||
| reflection.js:7:1:7:3 | add | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
|
||||
| reflection.js:8:1:8:3 | add | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
|
||||
| reflection.js:8:1:8:9 | add.apply | reflection.js:5:15:5:39 | functio ... n 56; } |
|
||||
| strict2.js:2:1:10:2 | (functi ... ode.\\n}) | strict2.js:2:2:10:1 | functio ... mode.\\n} |
|
||||
| strict2.js:2:2:10:1 | functio ... mode.\\n} | strict2.js:2:2:10:1 | functio ... mode.\\n} |
|
||||
| strict2.js:5:5:7:5 | functio ... ;\\n } | strict2.js:5:5:7:5 | functio ... ;\\n } |
|
||||
| strict.js:1:1:8:2 | (functi ... ode.\\n}) | strict.js:1:2:8:1 | functio ... mode.\\n} |
|
||||
| strict.js:1:2:8:1 | functio ... mode.\\n} | strict.js:1:2:8:1 | functio ... mode.\\n} |
|
||||
| strict.js:3:5:5:5 | functio ... ;\\n } | strict.js:3:5:5:5 | functio ... ;\\n } |
|
||||
| tst3.js:1:1:1:22 | functio ... fn() {} | tst3.js:1:1:1:22 | functio ... fn() {} |
|
||||
| tst3.js:2:1:2:23 | functio ... n2() {} | tst3.js:2:1:2:23 | functio ... n2() {} |
|
||||
| tst.js:1:1:1:15 | function f() {} | tst.js:1:1:1:15 | function f() {} |
|
||||
@@ -226,6 +237,8 @@ test_getNumArgument
|
||||
| n.js:2:1:2:5 | m.f() | 0 |
|
||||
| n.js:4:10:4:24 | require('./m2') | 1 |
|
||||
| n.js:5:1:5:6 | m2.f() | 0 |
|
||||
| non-strict.js:1:1:8:4 | (functi ... e.\\n})() | 0 |
|
||||
| non-strict.js:7:10:7:14 | foo() | 0 |
|
||||
| protoclass.js:4:3:4:13 | this.init() | 0 |
|
||||
| protoclass.js:8:3:8:15 | this.method() | 0 |
|
||||
| protoclass.js:9:11:9:32 | this.me ... d(this) | 1 |
|
||||
@@ -234,6 +247,10 @@ test_getNumArgument
|
||||
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | 3 |
|
||||
| reflection.js:7:1:7:22 | reflective call | 2 |
|
||||
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | 2 |
|
||||
| strict2.js:2:1:10:4 | (functi ... e.\\n})() | 0 |
|
||||
| strict2.js:9:10:9:14 | foo() | 0 |
|
||||
| strict.js:1:1:8:4 | (functi ... e.\\n})() | 0 |
|
||||
| strict.js:7:10:7:14 | foo() | 0 |
|
||||
| tst.js:6:1:6:3 | f() | 0 |
|
||||
| tst.js:7:1:7:3 | g() | 0 |
|
||||
| tst.js:8:1:8:3 | h() | 0 |
|
||||
@@ -322,6 +339,8 @@ test_getCalleeNode
|
||||
| n.js:2:1:2:5 | m.f() | n.js:2:1:2:3 | m.f |
|
||||
| n.js:4:10:4:24 | require('./m2') | n.js:4:10:4:16 | require |
|
||||
| n.js:5:1:5:6 | m2.f() | n.js:5:1:5:4 | m2.f |
|
||||
| non-strict.js:1:1:8:4 | (functi ... e.\\n})() | non-strict.js:1:1:8:2 | (functi ... ode.\\n}) |
|
||||
| non-strict.js:7:10:7:14 | foo() | non-strict.js:7:10:7:12 | foo |
|
||||
| protoclass.js:4:3:4:13 | this.init() | protoclass.js:4:3:4:11 | this.init |
|
||||
| protoclass.js:8:3:8:15 | this.method() | protoclass.js:8:3:8:13 | this.method |
|
||||
| protoclass.js:9:11:9:32 | this.me ... d(this) | protoclass.js:9:11:9:26 | this.method.bind |
|
||||
@@ -331,6 +350,10 @@ test_getCalleeNode
|
||||
| reflection.js:7:1:7:22 | reflective call | reflection.js:7:1:7:3 | add |
|
||||
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:8:1:8:9 | add.apply |
|
||||
| reflection.js:8:1:8:25 | reflective call | reflection.js:8:1:8:3 | add |
|
||||
| strict2.js:2:1:10:4 | (functi ... e.\\n})() | strict2.js:2:1:10:2 | (functi ... ode.\\n}) |
|
||||
| strict2.js:9:10:9:14 | foo() | strict2.js:9:10:9:12 | foo |
|
||||
| strict.js:1:1:8:4 | (functi ... e.\\n})() | strict.js:1:1:8:2 | (functi ... ode.\\n}) |
|
||||
| strict.js:7:10:7:14 | foo() | strict.js:7:10:7:12 | foo |
|
||||
| tst.js:6:1:6:3 | f() | tst.js:6:1:6:1 | f |
|
||||
| tst.js:7:1:7:3 | g() | tst.js:7:1:7:1 | g |
|
||||
| tst.js:8:1:8:3 | h() | tst.js:8:1:8:1 | h |
|
||||
@@ -409,11 +432,15 @@ test_getACallee
|
||||
| m.js:3:1:3:18 | module.exports.f() | m.js:1:13:1:25 | function() {} |
|
||||
| n.js:2:1:2:5 | m.f() | m.js:1:13:1:25 | function() {} |
|
||||
| n.js:5:1:5:6 | m2.f() | m2.js:2:6:2:18 | function() {} |
|
||||
| non-strict.js:1:1:8:4 | (functi ... e.\\n})() | non-strict.js:1:2:8:1 | functio ... mode.\\n} |
|
||||
| non-strict.js:7:10:7:14 | foo() | non-strict.js:3:5:5:5 | functio ... ;\\n } |
|
||||
| protoclass.js:4:3:4:13 | this.init() | protoclass.js:7:20:11:1 | functio ... m();\\n} |
|
||||
| protoclass.js:8:3:8:15 | this.method() | protoclass.js:13:22:13:34 | function() {} |
|
||||
| reflection.js:7:1:7:22 | reflective call | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
|
||||
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:5:15:5:39 | functio ... n 56; } |
|
||||
| reflection.js:8:1:8:25 | reflective call | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
|
||||
| strict2.js:2:1:10:4 | (functi ... e.\\n})() | strict2.js:2:2:10:1 | functio ... mode.\\n} |
|
||||
| strict.js:1:1:8:4 | (functi ... e.\\n})() | strict.js:1:2:8:1 | functio ... mode.\\n} |
|
||||
| tst.js:6:1:6:3 | f() | tst.js:1:1:1:15 | function f() {} |
|
||||
| tst.js:7:1:7:3 | g() | tst.js:2:9:2:21 | function() {} |
|
||||
| tst.js:8:1:8:3 | h() | tst.js:3:5:3:17 | function() {} |
|
||||
@@ -464,6 +491,7 @@ test_getCalleeName
|
||||
| n.js:2:1:2:5 | m.f() | f |
|
||||
| n.js:4:10:4:24 | require('./m2') | require |
|
||||
| n.js:5:1:5:6 | m2.f() | f |
|
||||
| non-strict.js:7:10:7:14 | foo() | foo |
|
||||
| protoclass.js:4:3:4:13 | this.init() | init |
|
||||
| protoclass.js:8:3:8:15 | this.method() | method |
|
||||
| protoclass.js:9:11:9:32 | this.me ... d(this) | bind |
|
||||
@@ -471,6 +499,8 @@ test_getCalleeName
|
||||
| reflection.js:4:5:4:12 | sneaky() | sneaky |
|
||||
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | call |
|
||||
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | apply |
|
||||
| strict2.js:9:10:9:14 | foo() | foo |
|
||||
| strict.js:7:10:7:14 | foo() | foo |
|
||||
| tst.js:6:1:6:3 | f() | f |
|
||||
| tst.js:7:1:7:3 | g() | g |
|
||||
| tst.js:8:1:8:3 | h() | h |
|
||||
|
||||
BIN
javascript/ql/test/library-tests/Files/binary.js
Normal file
BIN
javascript/ql/test/library-tests/Files/binary.js
Normal file
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_getId(Function f, VarDecl res0, string res1) {
|
||||
res0 = f.getId() and res1 = f.getName()
|
||||
res0 = f.getIdentifier() and res1 = f.getName()
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
| missing | async.js:1:2:1:2 | source | async.js:8:15:8:27 | await async() |
|
||||
| missing | async.js:1:2:1:2 | source | async.js:26:12:26:12 | e |
|
||||
| missing | async.js:1:2:1:2 | source | async.js:26:12:26:12 | e |
|
||||
| missing | async.js:1:2:1:2 | source | async.js:27:17:27:17 | e |
|
||||
| missing | async.js:2:16:2:23 | "source" | async.js:8:15:8:27 | await async() |
|
||||
| missing | async.js:2:16:2:23 | "source" | async.js:26:12:26:12 | e |
|
||||
| missing | async.js:2:16:2:23 | "source" | async.js:26:12:26:12 | e |
|
||||
| missing | async.js:2:16:2:23 | "source" | async.js:27:17:27:17 | e |
|
||||
| missing | async.js:73:22:73:22 | x | async.js:80:14:80:36 | (await ... ce))).p |
|
||||
| missing | async.js:74:12:76:5 | {\\n p: x\\n } | async.js:80:14:80:34 | (await ... urce))) |
|
||||
| missing | async.js:74:12:76:5 | {\\n p: x\\n } | async.js:80:15:80:33 | await (foo(source)) |
|
||||
| missing | async.js:79:16:79:23 | "source" | async.js:80:14:80:36 | (await ... ce))).p |
|
||||
| missing | async.js:79:16:79:23 | "source" | async.js:92:15:92:30 | await (getP(o3)) |
|
||||
| missing | async.js:84:12:84:17 | base.p | async.js:92:15:92:30 | await (getP(o3)) |
|
||||
| missing | async.js:88:12:88:17 | base.q | async.js:93:15:93:30 | await (getQ(o3)) |
|
||||
| missing | async.js:96:18:96:25 | "source" | async.js:101:15:101:27 | await readP() |
|
||||
| missing | async.js:98:12:98:16 | obj.x | async.js:101:15:101:27 | await readP() |
|
||||
| missing | callback.js:17:15:17:23 | "source2" | callback.js:8:16:8:20 | xs[i] |
|
||||
| missing | callback.js:17:15:17:23 | "source2" | callback.js:12:16:12:16 | x |
|
||||
| missing | callback.js:17:15:17:23 | "source2" | callback.js:12:16:12:16 | x |
|
||||
| missing | callback.js:17:15:17:23 | "source2" | callback.js:13:14:13:14 | x |
|
||||
| missing | promises.js:1:2:1:2 | source | promises.js:6:26:6:28 | val |
|
||||
| missing | promises.js:1:2:1:2 | source | promises.js:6:26:6:28 | val |
|
||||
| missing | promises.js:1:2:1:2 | source | promises.js:7:16:7:18 | val |
|
||||
| missing | promises.js:1:2:1:2 | source | promises.js:37:11:37:11 | v |
|
||||
| missing | promises.js:1:2:1:2 | source | promises.js:37:11:37:11 | v |
|
||||
| missing | promises.js:1:2:1:2 | source | promises.js:38:32:38:32 | v |
|
||||
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:6:26:6:28 | val |
|
||||
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:6:26:6:28 | val |
|
||||
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:7:16:7:18 | val |
|
||||
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:37:11:37:11 | v |
|
||||
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:37:11:37:11 | v |
|
||||
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:38:32:38:32 | v |
|
||||
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:20:7:20:7 | v |
|
||||
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:20:7:20:7 | v |
|
||||
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:21:20:21:20 | v |
|
||||
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:23:19:23:19 | v |
|
||||
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:23:19:23:19 | v |
|
||||
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:24:20:24:20 | v |
|
||||
| missing | promises.js:11:22:11:31 | "resolved" | promises.js:18:18:18:18 | v |
|
||||
| missing | promises.js:11:22:11:31 | "resolved" | promises.js:18:18:18:18 | v |
|
||||
| missing | promises.js:11:22:11:31 | "resolved" | promises.js:19:20:19:20 | v |
|
||||
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:20:7:20:7 | v |
|
||||
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:20:7:20:7 | v |
|
||||
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:21:20:21:20 | v |
|
||||
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:23:19:23:19 | v |
|
||||
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:23:19:23:19 | v |
|
||||
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:24:20:24:20 | v |
|
||||
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:20:7:20:7 | v |
|
||||
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:20:7:20:7 | v |
|
||||
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:21:20:21:20 | v |
|
||||
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:23:19:23:19 | v |
|
||||
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:23:19:23:19 | v |
|
||||
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:24:20:24:20 | v |
|
||||
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:20:7:20:7 | v |
|
||||
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:20:7:20:7 | v |
|
||||
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:21:20:21:20 | v |
|
||||
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:23:19:23:19 | v |
|
||||
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:23:19:23:19 | v |
|
||||
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:24:20:24:20 | v |
|
||||
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:20:7:20:7 | v |
|
||||
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:20:7:20:7 | v |
|
||||
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:21:20:21:20 | v |
|
||||
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:23:19:23:19 | v |
|
||||
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:23:19:23:19 | v |
|
||||
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:24:20:24:20 | v |
|
||||
| missing | promises.js:32:24:32:37 | "also tainted" | promises.js:37:11:37:11 | v |
|
||||
| missing | promises.js:32:24:32:37 | "also tainted" | promises.js:37:11:37:11 | v |
|
||||
| missing | promises.js:32:24:32:37 | "also tainted" | promises.js:38:32:38:32 | v |
|
||||
| missing | tst.js:2:17:2:22 | "src1" | tst.js:27:22:27:24 | elt |
|
||||
| missing | tst.js:2:17:2:22 | "src1" | tst.js:27:22:27:24 | elt |
|
||||
| missing | tst.js:2:17:2:22 | "src1" | tst.js:28:20:28:22 | elt |
|
||||
| spurious | async.js:1:2:1:2 | source | async.js:7:14:7:20 | async() |
|
||||
| spurious | async.js:1:2:1:2 | source | async.js:8:21:8:27 | async() |
|
||||
| spurious | async.js:2:16:2:23 | "source" | async.js:7:14:7:20 | async() |
|
||||
| spurious | async.js:2:16:2:23 | "source" | async.js:8:21:8:27 | async() |
|
||||
@@ -1,31 +0,0 @@
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Track all nodes that do not have flow predecessors.
|
||||
*/
|
||||
class TrackAllSources extends DataFlow::TrackedNode {
|
||||
TrackAllSources() { not exists(getAPredecessor()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow configuration that emulates the flow tracking done by
|
||||
* `DataFlow::TrackedNode`.
|
||||
*/
|
||||
class AllSourcesTrackingConfig extends DataFlow::Configuration {
|
||||
AllSourcesTrackingConfig() { this = "TrackAllTrackedNodes" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src instanceof DataFlow::TrackedNode }
|
||||
|
||||
override predicate isSink(DataFlow::Node snk) { any() }
|
||||
}
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink, AllSourcesTrackingConfig cfg, string problem
|
||||
where
|
||||
cfg.hasFlow(source, sink) and
|
||||
not source.(DataFlow::TrackedNode).flowsTo(sink) and
|
||||
problem = "missing"
|
||||
or
|
||||
not cfg.hasFlow(source, sink) and
|
||||
source.(DataFlow::TrackedNode).flowsTo(sink) and
|
||||
problem = "spurious"
|
||||
select problem, source, sink
|
||||
@@ -0,0 +1,3 @@
|
||||
var fs = require("fs");
|
||||
console.log("I'm a .cjs file!");
|
||||
console.log(fs);
|
||||
5
javascript/ql/test/library-tests/ModuleTypes/import.js
Normal file
5
javascript/ql/test/library-tests/ModuleTypes/import.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import * as fs from "fs";
|
||||
|
||||
export default function (x) {
|
||||
return fs.readFileSync(x);
|
||||
};
|
||||
1
javascript/ql/test/library-tests/ModuleTypes/mjs.mjs
Normal file
1
javascript/ql/test/library-tests/ModuleTypes/mjs.mjs
Normal file
@@ -0,0 +1 @@
|
||||
console.log("I'm a .mjs file!");
|
||||
7
javascript/ql/test/library-tests/ModuleTypes/require.js
Normal file
7
javascript/ql/test/library-tests/ModuleTypes/require.js
Normal file
@@ -0,0 +1,7 @@
|
||||
var fs = require("fs");
|
||||
|
||||
module.exports = {
|
||||
foo: function (x) {
|
||||
return fs.readFileSync(x);
|
||||
}
|
||||
}
|
||||
1
javascript/ql/test/library-tests/ModuleTypes/script.js
Normal file
1
javascript/ql/test/library-tests/ModuleTypes/script.js
Normal file
@@ -0,0 +1 @@
|
||||
console.log("I'm a plain script!");
|
||||
@@ -0,0 +1,5 @@
|
||||
| commonjs.cjs:1:1:3:16 | <toplevel> | node |
|
||||
| import.js:1:1:5:2 | <toplevel> | es2015 |
|
||||
| mjs.mjs:1:1:1:32 | <toplevel> | es2015 |
|
||||
| require.js:1:1:7:1 | <toplevel> | node |
|
||||
| script.js:1:1:1:35 | <toplevel> | non-module |
|
||||
18
javascript/ql/test/library-tests/ModuleTypes/tests.ql
Normal file
18
javascript/ql/test/library-tests/ModuleTypes/tests.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
import javascript
|
||||
|
||||
query string getModuleType(TopLevel top) {
|
||||
not top.isExterns() and
|
||||
(
|
||||
not top instanceof Module and
|
||||
result = "non-module"
|
||||
or
|
||||
top instanceof NodeModule and
|
||||
result = "node"
|
||||
or
|
||||
top instanceof ES2015Module and
|
||||
result = "es2015"
|
||||
or
|
||||
top instanceof AmdModule and
|
||||
result = "amd"
|
||||
)
|
||||
}
|
||||
@@ -67,6 +67,18 @@ typeInferenceMismatch
|
||||
| exceptions.js:158:13:158:20 | source() | exceptions.js:161:10:161:10 | e |
|
||||
| importedReactComponent.jsx:4:40:4:47 | source() | exportedReactComponent.jsx:2:10:2:19 | props.text |
|
||||
| indexOf.js:4:11:4:18 | source() | indexOf.js:9:10:9:10 | x |
|
||||
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:5:8:5:29 | JSON.st ... source) |
|
||||
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:9:8:9:47 | require ... source) |
|
||||
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:10:8:10:42 | require ... source) |
|
||||
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:11:8:11:41 | require ... source) |
|
||||
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:12:8:12:52 | require ... source) |
|
||||
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:13:8:13:45 | require ... source) |
|
||||
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:14:8:14:46 | require ... source) |
|
||||
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:15:8:15:38 | require ... source) |
|
||||
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:16:8:16:38 | require ... source) |
|
||||
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:17:8:17:39 | require ... source) |
|
||||
| json-stringify.js:2:16:2:23 | source() | json-stringify.js:18:8:18:40 | require ... source) |
|
||||
| json-stringify.js:3:15:3:22 | source() | json-stringify.js:8:8:8:31 | jsonStr ... (taint) |
|
||||
| nested-props.js:4:13:4:20 | source() | nested-props.js:5:10:5:14 | obj.x |
|
||||
| nested-props.js:9:18:9:25 | source() | nested-props.js:10:10:10:16 | obj.x.y |
|
||||
| nested-props.js:35:13:35:20 | source() | nested-props.js:36:10:36:20 | doLoad(obj) |
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
function foo() {
|
||||
let source = source();
|
||||
let taint = source();
|
||||
|
||||
sink(JSON.stringify(source)); // NOT OK
|
||||
|
||||
var jsonStringifySafe = require("json-stringify-safe");
|
||||
sink(jsonStringifySafe(taint)); // NOT OK
|
||||
sink(require("json-stable-stringify")(source)); // NOT OK
|
||||
sink(require("stringify-object")(source)); // NOT OK
|
||||
sink(require("json3").stringify(source)); // NOT OK
|
||||
sink(require("fast-json-stable-stringify")(source)); // NOT OK
|
||||
sink(require("fast-safe-stringify")(source)); // NOT OK
|
||||
sink(require("javascript-stringify")(source)); // NOT OK
|
||||
sink(require("js-stringify")(source)); // NOT OK
|
||||
sink(require("util").inspect(source)); // NOT OK
|
||||
sink(require("pretty-format")(source)); // NOT OK
|
||||
sink(require("object-inspect")(source)); // NOT OK
|
||||
}
|
||||
@@ -10,7 +10,7 @@ class ResolveCall extends CallExpr {
|
||||
string getDeclaredValue() {
|
||||
result = getVariable().getAnAssignedExpr().getStringValue()
|
||||
or
|
||||
exists(NamespaceDeclaration decl | decl.getId() = getVariable().getADeclaration() |
|
||||
exists(NamespaceDeclaration decl | decl.getIdentifier() = getVariable().getADeclaration() |
|
||||
result = getNamespaceName(decl)
|
||||
)
|
||||
}
|
||||
@@ -21,7 +21,8 @@ string getNamespaceName(NamespaceDeclaration decl) {
|
||||
or
|
||||
not decl.getStmt(0).(ExprStmt).getExpr() instanceof ConstantString and
|
||||
result =
|
||||
"Namespace " + decl.getId() + " on line " + decl.getFirstToken().getLocation().getStartLine()
|
||||
"Namespace " + decl.getIdentifier() + " on line " +
|
||||
decl.getFirstToken().getLocation().getStartLine()
|
||||
}
|
||||
|
||||
from ResolveCall resolve
|
||||
|
||||
@@ -20,3 +20,6 @@
|
||||
| tst-IncompleteUrlSubstringSanitization.js:63:4:63:33 | x.index ... !== -1 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:63:14:63:25 | "secure.com" | secure.com |
|
||||
| tst-IncompleteUrlSubstringSanitization.js:64:3:64:26 | x.inclu ... e.com") | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:64:14:64:25 | "secure.com" | secure.com |
|
||||
| tst-IncompleteUrlSubstringSanitization.js:66:6:66:29 | x.inclu ... e.com") | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:66:17:66:28 | "secure.com" | secure.com |
|
||||
| tst-IncompleteUrlSubstringSanitization.js:73:5:73:48 | x.index ... ") >= 0 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:73:15:73:42 | "https: ... oo/bar" | https://secure.com/foo/bar |
|
||||
| tst-IncompleteUrlSubstringSanitization.js:74:5:74:40 | x.index ... ") >= 0 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:74:15:74:34 | "https://secure.com" | https://secure.com |
|
||||
| tst-IncompleteUrlSubstringSanitization.js:75:5:75:52 | x.index ... ") >= 0 | '$@' can be anywhere in the URL, and arbitrary hosts may come before or after it. | tst-IncompleteUrlSubstringSanitization.js:75:15:75:46 | "https: ... ar-baz" | https://secure.com/foo/bar-baz |
|
||||
|
||||
@@ -67,5 +67,10 @@
|
||||
|
||||
} else {
|
||||
doSomeThingWithTrustedURL(x);
|
||||
}
|
||||
}
|
||||
|
||||
x.startsWith("https://secure.com/foo/bar"); // OK - a forward slash after the domain makes prefix checks safe.
|
||||
x.indexOf("https://secure.com/foo/bar") >= 0 // NOT OK - the url can be anywhere in the string.
|
||||
x.indexOf("https://secure.com") >= 0 // NOT OK
|
||||
x.indexOf("https://secure.com/foo/bar-baz") >= 0 // NOT OK - the url can be anywhere in the string.
|
||||
});
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import javascript
|
||||
|
||||
class TrackedStringLiteral extends DataFlow::TrackedNode {
|
||||
TrackedStringLiteral() { this.asExpr() instanceof ConstantString }
|
||||
DataFlow::Node constantString(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result.asExpr() instanceof ConstantString
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | t = t2.smallstep(constantString(t2), result))
|
||||
}
|
||||
|
||||
query predicate test_query15(DataFlow::Node sink) {
|
||||
exists(TrackedStringLiteral source, SsaExplicitDefinition def |
|
||||
source.flowsTo(sink) and
|
||||
exists(SsaExplicitDefinition def |
|
||||
sink = constantString(DataFlow::TypeTracker::end()) and
|
||||
sink = DataFlow::ssaDefinitionNode(def) and
|
||||
def.getSourceVariable().getName().toLowerCase() = "password"
|
||||
|
|
||||
|
||||
Reference in New Issue
Block a user