mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge branch 'main' into CVE760-reexport
This commit is contained in:
@@ -12,7 +12,6 @@
|
||||
"watch": "tsc -p . -w --sourceMap"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "12.7.11",
|
||||
"tslint": "^5.9.1"
|
||||
"@types/node": "12.7.11"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,229 +6,6 @@
|
||||
version "12.7.11"
|
||||
resolved node-12.7.11.tgz#be879b52031cfb5d295b047f5462d8ef1a716446
|
||||
|
||||
ansi-regex@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df
|
||||
|
||||
ansi-styles@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe
|
||||
|
||||
ansi-styles@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.9"
|
||||
resolved argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86
|
||||
dependencies:
|
||||
sprintf-js "~1.0.2"
|
||||
|
||||
babel-code-frame@^6.22.0:
|
||||
version "6.26.0"
|
||||
resolved babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
esutils "^2.0.2"
|
||||
js-tokens "^3.0.2"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.8"
|
||||
resolved brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
builtin-modules@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f
|
||||
|
||||
chalk@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98
|
||||
dependencies:
|
||||
ansi-styles "^2.2.1"
|
||||
escape-string-regexp "^1.0.2"
|
||||
has-ansi "^2.0.0"
|
||||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chalk@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba
|
||||
dependencies:
|
||||
ansi-styles "^3.1.0"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^4.0.0"
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.1"
|
||||
resolved color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed
|
||||
dependencies:
|
||||
color-name "^1.1.1"
|
||||
|
||||
color-name@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25
|
||||
|
||||
commander@^2.12.1:
|
||||
version "2.13.0"
|
||||
resolved commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b
|
||||
|
||||
diff@^3.2.0:
|
||||
version "3.4.0"
|
||||
resolved diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c
|
||||
|
||||
escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4
|
||||
|
||||
esprima@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804
|
||||
|
||||
esutils@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f
|
||||
|
||||
glob@^7.1.1:
|
||||
version "7.1.2"
|
||||
resolved glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
has-ansi@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91
|
||||
dependencies:
|
||||
ansi-regex "^2.0.0"
|
||||
|
||||
has-flag@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2:
|
||||
version "2.0.3"
|
||||
resolved inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de
|
||||
|
||||
js-tokens@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b
|
||||
|
||||
js-yaml@^3.7.0:
|
||||
version "3.10.0"
|
||||
resolved js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f
|
||||
|
||||
path-parse@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1
|
||||
|
||||
resolve@^1.3.2:
|
||||
version "1.5.0"
|
||||
resolved resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36
|
||||
dependencies:
|
||||
path-parse "^1.0.5"
|
||||
|
||||
semver@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab
|
||||
|
||||
sprintf-js@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c
|
||||
|
||||
strip-ansi@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf
|
||||
dependencies:
|
||||
ansi-regex "^2.0.0"
|
||||
|
||||
supports-color@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7
|
||||
|
||||
supports-color@^4.0.0:
|
||||
version "4.5.0"
|
||||
resolved supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b
|
||||
dependencies:
|
||||
has-flag "^2.0.0"
|
||||
|
||||
tslib@^1.8.0, tslib@^1.8.1:
|
||||
version "1.9.0"
|
||||
resolved tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8
|
||||
|
||||
tslint@^5.9.1:
|
||||
version "5.9.1"
|
||||
resolved tslint-5.9.1.tgz#1255f87a3ff57eb0b0e1f0e610a8b4748046c9ae
|
||||
dependencies:
|
||||
babel-code-frame "^6.22.0"
|
||||
builtin-modules "^1.1.1"
|
||||
chalk "^2.3.0"
|
||||
commander "^2.12.1"
|
||||
diff "^3.2.0"
|
||||
glob "^7.1.1"
|
||||
js-yaml "^3.7.0"
|
||||
minimatch "^3.0.4"
|
||||
resolve "^1.3.2"
|
||||
semver "^5.3.0"
|
||||
tslib "^1.8.0"
|
||||
tsutils "^2.12.1"
|
||||
|
||||
tsutils@^2.12.1:
|
||||
version "2.19.1"
|
||||
resolved tsutils-2.19.1.tgz#76d7ebdea9d7a7bf4a05f50ead3701b0168708d7
|
||||
dependencies:
|
||||
tslib "^1.8.1"
|
||||
|
||||
typescript@4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f
|
||||
|
||||
@@ -531,9 +531,14 @@ public class Parser {
|
||||
int next2 = charAt(this.pos + 2);
|
||||
if (this.options.esnext()) {
|
||||
if (next == '.' && !('0' <= next2 && next2 <= '9')) // '?.', but not '?.X' where X is a digit
|
||||
return this.finishOp(TokenType.questiondot, 2);
|
||||
if (next == '?') // '??'
|
||||
return this.finishOp(TokenType.questionquestion, 2);
|
||||
return this.finishOp(TokenType.questiondot, 2);
|
||||
if (next == '?') { // '??'
|
||||
if (next2 == '=') { // ??=
|
||||
return this.finishOp(TokenType.assign, 3);
|
||||
}
|
||||
return this.finishOp(TokenType.questionquestion, 2);
|
||||
}
|
||||
|
||||
}
|
||||
return this.finishOp(TokenType.question, 1);
|
||||
}
|
||||
@@ -566,8 +571,11 @@ public class Parser {
|
||||
|
||||
private Token readToken_pipe_amp(int code) { // '|&'
|
||||
int next = charAt(this.pos + 1);
|
||||
if (next == code)
|
||||
int next2 = charAt(this.pos + 2);
|
||||
if (next == code) { // && ||
|
||||
if (next2 == 61) return this.finishOp(TokenType.assign, 3); // &&= ||=
|
||||
return this.finishOp(code == 124 ? TokenType.logicalOR : TokenType.logicalAND, 2);
|
||||
}
|
||||
if (next == 61) return this.finishOp(TokenType.assign, 2);
|
||||
return this.finishOp(code == 124 ? TokenType.bitwiseOR : TokenType.bitwiseAND, 1);
|
||||
}
|
||||
@@ -709,8 +717,8 @@ public class Parser {
|
||||
case 42: // '%*'
|
||||
return this.readToken_mult_modulo_exp(code);
|
||||
|
||||
case 124:
|
||||
case 38: // '|&'
|
||||
case 124: // '|'
|
||||
case 38: // '&'
|
||||
return this.readToken_pipe_amp(code);
|
||||
|
||||
case 94: // '^'
|
||||
|
||||
@@ -60,6 +60,7 @@ import com.semmle.util.io.WholeIO;
|
||||
import com.semmle.util.io.csv.CSVReader;
|
||||
import com.semmle.util.language.LegacyLanguage;
|
||||
import com.semmle.util.process.Env;
|
||||
import com.semmle.util.process.Env.OS;
|
||||
import com.semmle.util.projectstructure.ProjectLayout;
|
||||
import com.semmle.util.trap.TrapWriter;
|
||||
|
||||
@@ -1239,11 +1240,29 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
|
||||
protected void extractXml() throws IOException {
|
||||
if (xmlExtensions.isEmpty()) return;
|
||||
List<String> cmd = new ArrayList<>();
|
||||
cmd.add("odasa");
|
||||
cmd.add("index");
|
||||
cmd.add("--xml");
|
||||
cmd.add("--extensions");
|
||||
cmd.addAll(xmlExtensions);
|
||||
if (EnvironmentVariables.getCodeQLDist() == null) {
|
||||
// Use the legacy odasa XML extractor
|
||||
cmd.add("odasa");
|
||||
cmd.add("index");
|
||||
cmd.add("--xml");
|
||||
cmd.add("--extensions");
|
||||
cmd.addAll(xmlExtensions);
|
||||
} else {
|
||||
String command = Env.getOS() == OS.WINDOWS ? "codeql.exe" : "codeql";
|
||||
cmd.add(Paths.get(EnvironmentVariables.getCodeQLDist(), command).toString());
|
||||
cmd.add("database");
|
||||
cmd.add("index-files");
|
||||
cmd.add("--language");
|
||||
cmd.add("xml");
|
||||
cmd.add("--size-limit");
|
||||
cmd.add("10m");
|
||||
for (String extension : xmlExtensions) {
|
||||
cmd.add("--include-extension");
|
||||
cmd.add(extension);
|
||||
}
|
||||
cmd.add("--");
|
||||
cmd.add(EnvironmentVariables.getWipDatabase());
|
||||
}
|
||||
ProcessBuilder pb = new ProcessBuilder(cmd);
|
||||
try {
|
||||
pb.redirectError(Redirect.INHERIT);
|
||||
|
||||
@@ -14,6 +14,11 @@ public class EnvironmentVariables {
|
||||
public static final String LGTM_WORKSPACE_ENV_VAR =
|
||||
"LGTM_WORKSPACE";
|
||||
|
||||
public static final String CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE_ENV_VAR =
|
||||
"CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE";
|
||||
|
||||
public static final String CODEQL_DIST_ENV_VAR = "CODEQL_DIST";
|
||||
|
||||
/**
|
||||
* Gets the extractor root based on the <code>CODEQL_EXTRACTOR_JAVASCRIPT_ROOT</code> or <code>
|
||||
* SEMMLE_DIST</code> or environment variable, or <code>null</code> if neither is set.
|
||||
@@ -49,4 +54,13 @@ public class EnvironmentVariables {
|
||||
|
||||
throw new UserError(CODEQL_EXTRACTOR_JAVASCRIPT_SCRATCH_DIR_ENV_VAR + " or " + LGTM_WORKSPACE_ENV_VAR + " must be set");
|
||||
}
|
||||
|
||||
public static String getCodeQLDist() {
|
||||
return Env.systemEnv().getNonEmpty(CODEQL_DIST_ENV_VAR);
|
||||
}
|
||||
|
||||
/** Gets the output database directory. */
|
||||
public static String getWipDatabase() {
|
||||
return Env.systemEnv().getNonEmpty(CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE_ENV_VAR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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-09-12";
|
||||
public static final String EXTRACTOR_VERSION = "2020-09-17";
|
||||
|
||||
public static final Pattern NEWLINE = Pattern.compile("\n");
|
||||
|
||||
|
||||
@@ -126,7 +126,11 @@ public class ScriptExtractor implements IExtractor {
|
||||
}
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(file));
|
||||
String result = new Gson().fromJson(reader, PackageJSON.class).type;
|
||||
PackageJSON pkgjson = new Gson().fromJson(reader, PackageJSON.class);
|
||||
if (pkgjson == null) {
|
||||
return null;
|
||||
}
|
||||
String result = pkgjson.type;
|
||||
packageTypeCache.put(folder, Optional.ofNullable(result));
|
||||
return result;
|
||||
} catch (IOException | JsonSyntaxException e) {
|
||||
|
||||
@@ -1421,8 +1421,19 @@ public class TypeScriptASTConverter {
|
||||
importStart = advance(importStart, m.group(0));
|
||||
}
|
||||
}
|
||||
|
||||
Node rawPath = convertChild(node, "argument");
|
||||
ITypeExpression path;
|
||||
if (rawPath instanceof ITypeExpression) {
|
||||
path = (ITypeExpression)rawPath;
|
||||
} else if (rawPath instanceof TemplateLiteral) {
|
||||
// this is a type-error, so we just fall back to some behavior that does not crash the extractor.
|
||||
path = new Literal(rawPath.getLoc(), TokenType.string, ((TemplateLiteral)rawPath).getQuasis().stream().map(q -> q.getRaw()).collect(Collectors.joining("")));
|
||||
} else {
|
||||
throw new ParseError("Unsupported syntax in import", getSourceLocation(node).getStart());
|
||||
}
|
||||
|
||||
// Find the ending parenthesis in `import(path)` by skipping whitespace after `path`.
|
||||
ITypeExpression path = convertChild(node, "argument");
|
||||
String endSrc =
|
||||
loc.getSource().substring(path.getLoc().getEnd().getOffset() - loc.getStart().getOffset());
|
||||
Matcher m = WHITESPACE_END_PAREN.matcher(endSrc);
|
||||
|
||||
@@ -299,6 +299,8 @@ public class TypeScriptParser {
|
||||
: getMegabyteCountFromPrefixedEnv(TYPESCRIPT_RAM_SUFFIX, 2000);
|
||||
int reserveMemoryMb = getMegabyteCountFromPrefixedEnv(TYPESCRIPT_RAM_RESERVE_SUFFIX, 400);
|
||||
|
||||
System.out.println("Memory for TypeScript process: " + mainMemoryMb + " MB, and " + reserveMemoryMb + " MB reserve");
|
||||
|
||||
File parserWrapper = getParserWrapper();
|
||||
|
||||
String debugFlagString = Env.systemEnv().getNonEmpty(TYPESCRIPT_NODE_FLAGS);
|
||||
|
||||
9
javascript/extractor/tests/es2021/input/assign.js
Normal file
9
javascript/extractor/tests/es2021/input/assign.js
Normal file
@@ -0,0 +1,9 @@
|
||||
var x = 1;
|
||||
var y = 2;
|
||||
var foo = x && y;
|
||||
var bar = x &&= y;
|
||||
console.log(x); // 2
|
||||
|
||||
x &&= y;
|
||||
x ||= y;
|
||||
x ??= y;
|
||||
8
javascript/extractor/tests/es2021/input/numeric.js
Normal file
8
javascript/extractor/tests/es2021/input/numeric.js
Normal file
@@ -0,0 +1,8 @@
|
||||
1_000_000_000 // Ah, so a billion
|
||||
101_475_938.38 // And this is hundreds of millions
|
||||
|
||||
let fee = 123_00; // $123 (12300 cents, apparently)
|
||||
let fee = 12_300; // $12,300 (woah, that fee!)
|
||||
let amount = 12345_00; // 12,345 (1234500 cents, apparently)
|
||||
let amount = 123_4500; // 123.45 (4-fixed financial)
|
||||
let amount = 1_234_500; // 1,234,500
|
||||
3
javascript/extractor/tests/es2021/options.json
Normal file
3
javascript/extractor/tests/es2021/options.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"experimental": true
|
||||
}
|
||||
633
javascript/extractor/tests/es2021/output/trap/assign.js.trap
Normal file
633
javascript/extractor/tests/es2021/output/trap/assign.js.trap
Normal file
@@ -0,0 +1,633 @@
|
||||
#10000=@"/assign.js;sourcefile"
|
||||
files(#10000,"/assign.js","assign","js",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=*
|
||||
comments(#20002,0,#20001," 2","// 2")
|
||||
#20003=@"loc,{#10000},5,17,5,20"
|
||||
locations_default(#20003,#10000,5,17,5,20)
|
||||
hasLocation(#20002,#20003)
|
||||
#20004=*
|
||||
lines(#20004,#20001,"var x = 1;","
|
||||
")
|
||||
#20005=@"loc,{#10000},1,1,1,10"
|
||||
locations_default(#20005,#10000,1,1,1,10)
|
||||
hasLocation(#20004,#20005)
|
||||
#20006=*
|
||||
lines(#20006,#20001,"var y = 2;","
|
||||
")
|
||||
#20007=@"loc,{#10000},2,1,2,10"
|
||||
locations_default(#20007,#10000,2,1,2,10)
|
||||
hasLocation(#20006,#20007)
|
||||
#20008=*
|
||||
lines(#20008,#20001,"var foo = x && y;","
|
||||
")
|
||||
#20009=@"loc,{#10000},3,1,3,17"
|
||||
locations_default(#20009,#10000,3,1,3,17)
|
||||
hasLocation(#20008,#20009)
|
||||
#20010=*
|
||||
lines(#20010,#20001,"var bar = x &&= y;","
|
||||
")
|
||||
#20011=@"loc,{#10000},4,1,4,18"
|
||||
locations_default(#20011,#10000,4,1,4,18)
|
||||
hasLocation(#20010,#20011)
|
||||
#20012=*
|
||||
lines(#20012,#20001,"console.log(x); // 2","
|
||||
")
|
||||
#20013=@"loc,{#10000},5,1,5,20"
|
||||
locations_default(#20013,#10000,5,1,5,20)
|
||||
hasLocation(#20012,#20013)
|
||||
#20014=*
|
||||
lines(#20014,#20001,"","
|
||||
")
|
||||
#20015=@"loc,{#10000},6,1,6,0"
|
||||
locations_default(#20015,#10000,6,1,6,0)
|
||||
hasLocation(#20014,#20015)
|
||||
#20016=*
|
||||
lines(#20016,#20001,"x &&= y;","
|
||||
")
|
||||
#20017=@"loc,{#10000},7,1,7,8"
|
||||
locations_default(#20017,#10000,7,1,7,8)
|
||||
hasLocation(#20016,#20017)
|
||||
#20018=*
|
||||
lines(#20018,#20001,"x ||= y;","
|
||||
")
|
||||
#20019=@"loc,{#10000},8,1,8,8"
|
||||
locations_default(#20019,#10000,8,1,8,8)
|
||||
hasLocation(#20018,#20019)
|
||||
#20020=*
|
||||
lines(#20020,#20001,"x ??= y;","
|
||||
")
|
||||
#20021=@"loc,{#10000},9,1,9,8"
|
||||
locations_default(#20021,#10000,9,1,9,8)
|
||||
hasLocation(#20020,#20021)
|
||||
numlines(#20001,9,8,1)
|
||||
#20022=*
|
||||
tokeninfo(#20022,7,#20001,0,"var")
|
||||
#20023=@"loc,{#10000},1,1,1,3"
|
||||
locations_default(#20023,#10000,1,1,1,3)
|
||||
hasLocation(#20022,#20023)
|
||||
#20024=*
|
||||
tokeninfo(#20024,6,#20001,1,"x")
|
||||
#20025=@"loc,{#10000},1,5,1,5"
|
||||
locations_default(#20025,#10000,1,5,1,5)
|
||||
hasLocation(#20024,#20025)
|
||||
#20026=*
|
||||
tokeninfo(#20026,8,#20001,2,"=")
|
||||
#20027=@"loc,{#10000},1,7,1,7"
|
||||
locations_default(#20027,#10000,1,7,1,7)
|
||||
hasLocation(#20026,#20027)
|
||||
#20028=*
|
||||
tokeninfo(#20028,3,#20001,3,"1")
|
||||
#20029=@"loc,{#10000},1,9,1,9"
|
||||
locations_default(#20029,#10000,1,9,1,9)
|
||||
hasLocation(#20028,#20029)
|
||||
#20030=*
|
||||
tokeninfo(#20030,8,#20001,4,";")
|
||||
#20031=@"loc,{#10000},1,10,1,10"
|
||||
locations_default(#20031,#10000,1,10,1,10)
|
||||
hasLocation(#20030,#20031)
|
||||
#20032=*
|
||||
tokeninfo(#20032,7,#20001,5,"var")
|
||||
#20033=@"loc,{#10000},2,1,2,3"
|
||||
locations_default(#20033,#10000,2,1,2,3)
|
||||
hasLocation(#20032,#20033)
|
||||
#20034=*
|
||||
tokeninfo(#20034,6,#20001,6,"y")
|
||||
#20035=@"loc,{#10000},2,5,2,5"
|
||||
locations_default(#20035,#10000,2,5,2,5)
|
||||
hasLocation(#20034,#20035)
|
||||
#20036=*
|
||||
tokeninfo(#20036,8,#20001,7,"=")
|
||||
#20037=@"loc,{#10000},2,7,2,7"
|
||||
locations_default(#20037,#10000,2,7,2,7)
|
||||
hasLocation(#20036,#20037)
|
||||
#20038=*
|
||||
tokeninfo(#20038,3,#20001,8,"2")
|
||||
#20039=@"loc,{#10000},2,9,2,9"
|
||||
locations_default(#20039,#10000,2,9,2,9)
|
||||
hasLocation(#20038,#20039)
|
||||
#20040=*
|
||||
tokeninfo(#20040,8,#20001,9,";")
|
||||
#20041=@"loc,{#10000},2,10,2,10"
|
||||
locations_default(#20041,#10000,2,10,2,10)
|
||||
hasLocation(#20040,#20041)
|
||||
#20042=*
|
||||
tokeninfo(#20042,7,#20001,10,"var")
|
||||
#20043=@"loc,{#10000},3,1,3,3"
|
||||
locations_default(#20043,#10000,3,1,3,3)
|
||||
hasLocation(#20042,#20043)
|
||||
#20044=*
|
||||
tokeninfo(#20044,6,#20001,11,"foo")
|
||||
#20045=@"loc,{#10000},3,5,3,7"
|
||||
locations_default(#20045,#10000,3,5,3,7)
|
||||
hasLocation(#20044,#20045)
|
||||
#20046=*
|
||||
tokeninfo(#20046,8,#20001,12,"=")
|
||||
#20047=@"loc,{#10000},3,9,3,9"
|
||||
locations_default(#20047,#10000,3,9,3,9)
|
||||
hasLocation(#20046,#20047)
|
||||
#20048=*
|
||||
tokeninfo(#20048,6,#20001,13,"x")
|
||||
#20049=@"loc,{#10000},3,11,3,11"
|
||||
locations_default(#20049,#10000,3,11,3,11)
|
||||
hasLocation(#20048,#20049)
|
||||
#20050=*
|
||||
tokeninfo(#20050,8,#20001,14,"&&")
|
||||
#20051=@"loc,{#10000},3,13,3,14"
|
||||
locations_default(#20051,#10000,3,13,3,14)
|
||||
hasLocation(#20050,#20051)
|
||||
#20052=*
|
||||
tokeninfo(#20052,6,#20001,15,"y")
|
||||
#20053=@"loc,{#10000},3,16,3,16"
|
||||
locations_default(#20053,#10000,3,16,3,16)
|
||||
hasLocation(#20052,#20053)
|
||||
#20054=*
|
||||
tokeninfo(#20054,8,#20001,16,";")
|
||||
#20055=@"loc,{#10000},3,17,3,17"
|
||||
locations_default(#20055,#10000,3,17,3,17)
|
||||
hasLocation(#20054,#20055)
|
||||
#20056=*
|
||||
tokeninfo(#20056,7,#20001,17,"var")
|
||||
#20057=@"loc,{#10000},4,1,4,3"
|
||||
locations_default(#20057,#10000,4,1,4,3)
|
||||
hasLocation(#20056,#20057)
|
||||
#20058=*
|
||||
tokeninfo(#20058,6,#20001,18,"bar")
|
||||
#20059=@"loc,{#10000},4,5,4,7"
|
||||
locations_default(#20059,#10000,4,5,4,7)
|
||||
hasLocation(#20058,#20059)
|
||||
#20060=*
|
||||
tokeninfo(#20060,8,#20001,19,"=")
|
||||
#20061=@"loc,{#10000},4,9,4,9"
|
||||
locations_default(#20061,#10000,4,9,4,9)
|
||||
hasLocation(#20060,#20061)
|
||||
#20062=*
|
||||
tokeninfo(#20062,6,#20001,20,"x")
|
||||
#20063=@"loc,{#10000},4,11,4,11"
|
||||
locations_default(#20063,#10000,4,11,4,11)
|
||||
hasLocation(#20062,#20063)
|
||||
#20064=*
|
||||
tokeninfo(#20064,8,#20001,21,"&&=")
|
||||
#20065=@"loc,{#10000},4,13,4,15"
|
||||
locations_default(#20065,#10000,4,13,4,15)
|
||||
hasLocation(#20064,#20065)
|
||||
#20066=*
|
||||
tokeninfo(#20066,6,#20001,22,"y")
|
||||
#20067=@"loc,{#10000},4,17,4,17"
|
||||
locations_default(#20067,#10000,4,17,4,17)
|
||||
hasLocation(#20066,#20067)
|
||||
#20068=*
|
||||
tokeninfo(#20068,8,#20001,23,";")
|
||||
#20069=@"loc,{#10000},4,18,4,18"
|
||||
locations_default(#20069,#10000,4,18,4,18)
|
||||
hasLocation(#20068,#20069)
|
||||
#20070=*
|
||||
tokeninfo(#20070,6,#20001,24,"console")
|
||||
#20071=@"loc,{#10000},5,1,5,7"
|
||||
locations_default(#20071,#10000,5,1,5,7)
|
||||
hasLocation(#20070,#20071)
|
||||
#20072=*
|
||||
tokeninfo(#20072,8,#20001,25,".")
|
||||
#20073=@"loc,{#10000},5,8,5,8"
|
||||
locations_default(#20073,#10000,5,8,5,8)
|
||||
hasLocation(#20072,#20073)
|
||||
#20074=*
|
||||
tokeninfo(#20074,6,#20001,26,"log")
|
||||
#20075=@"loc,{#10000},5,9,5,11"
|
||||
locations_default(#20075,#10000,5,9,5,11)
|
||||
hasLocation(#20074,#20075)
|
||||
#20076=*
|
||||
tokeninfo(#20076,8,#20001,27,"(")
|
||||
#20077=@"loc,{#10000},5,12,5,12"
|
||||
locations_default(#20077,#10000,5,12,5,12)
|
||||
hasLocation(#20076,#20077)
|
||||
#20078=*
|
||||
tokeninfo(#20078,6,#20001,28,"x")
|
||||
#20079=@"loc,{#10000},5,13,5,13"
|
||||
locations_default(#20079,#10000,5,13,5,13)
|
||||
hasLocation(#20078,#20079)
|
||||
#20080=*
|
||||
tokeninfo(#20080,8,#20001,29,")")
|
||||
#20081=@"loc,{#10000},5,14,5,14"
|
||||
locations_default(#20081,#10000,5,14,5,14)
|
||||
hasLocation(#20080,#20081)
|
||||
#20082=*
|
||||
tokeninfo(#20082,8,#20001,30,";")
|
||||
#20083=@"loc,{#10000},5,15,5,15"
|
||||
locations_default(#20083,#10000,5,15,5,15)
|
||||
hasLocation(#20082,#20083)
|
||||
#20084=*
|
||||
tokeninfo(#20084,6,#20001,31,"x")
|
||||
#20085=@"loc,{#10000},7,1,7,1"
|
||||
locations_default(#20085,#10000,7,1,7,1)
|
||||
hasLocation(#20084,#20085)
|
||||
next_token(#20002,#20084)
|
||||
#20086=*
|
||||
tokeninfo(#20086,8,#20001,32,"&&=")
|
||||
#20087=@"loc,{#10000},7,3,7,5"
|
||||
locations_default(#20087,#10000,7,3,7,5)
|
||||
hasLocation(#20086,#20087)
|
||||
#20088=*
|
||||
tokeninfo(#20088,6,#20001,33,"y")
|
||||
#20089=@"loc,{#10000},7,7,7,7"
|
||||
locations_default(#20089,#10000,7,7,7,7)
|
||||
hasLocation(#20088,#20089)
|
||||
#20090=*
|
||||
tokeninfo(#20090,8,#20001,34,";")
|
||||
#20091=@"loc,{#10000},7,8,7,8"
|
||||
locations_default(#20091,#10000,7,8,7,8)
|
||||
hasLocation(#20090,#20091)
|
||||
#20092=*
|
||||
tokeninfo(#20092,6,#20001,35,"x")
|
||||
#20093=@"loc,{#10000},8,1,8,1"
|
||||
locations_default(#20093,#10000,8,1,8,1)
|
||||
hasLocation(#20092,#20093)
|
||||
#20094=*
|
||||
tokeninfo(#20094,8,#20001,36,"||=")
|
||||
#20095=@"loc,{#10000},8,3,8,5"
|
||||
locations_default(#20095,#10000,8,3,8,5)
|
||||
hasLocation(#20094,#20095)
|
||||
#20096=*
|
||||
tokeninfo(#20096,6,#20001,37,"y")
|
||||
#20097=@"loc,{#10000},8,7,8,7"
|
||||
locations_default(#20097,#10000,8,7,8,7)
|
||||
hasLocation(#20096,#20097)
|
||||
#20098=*
|
||||
tokeninfo(#20098,8,#20001,38,";")
|
||||
#20099=@"loc,{#10000},8,8,8,8"
|
||||
locations_default(#20099,#10000,8,8,8,8)
|
||||
hasLocation(#20098,#20099)
|
||||
#20100=*
|
||||
tokeninfo(#20100,6,#20001,39,"x")
|
||||
#20101=@"loc,{#10000},9,1,9,1"
|
||||
locations_default(#20101,#10000,9,1,9,1)
|
||||
hasLocation(#20100,#20101)
|
||||
#20102=*
|
||||
tokeninfo(#20102,8,#20001,40,"??=")
|
||||
#20103=@"loc,{#10000},9,3,9,5"
|
||||
locations_default(#20103,#10000,9,3,9,5)
|
||||
hasLocation(#20102,#20103)
|
||||
#20104=*
|
||||
tokeninfo(#20104,6,#20001,41,"y")
|
||||
#20105=@"loc,{#10000},9,7,9,7"
|
||||
locations_default(#20105,#10000,9,7,9,7)
|
||||
hasLocation(#20104,#20105)
|
||||
#20106=*
|
||||
tokeninfo(#20106,8,#20001,42,";")
|
||||
#20107=@"loc,{#10000},9,8,9,8"
|
||||
locations_default(#20107,#10000,9,8,9,8)
|
||||
hasLocation(#20106,#20107)
|
||||
#20108=*
|
||||
tokeninfo(#20108,0,#20001,43,"")
|
||||
#20109=@"loc,{#10000},10,1,10,0"
|
||||
locations_default(#20109,#10000,10,1,10,0)
|
||||
hasLocation(#20108,#20109)
|
||||
toplevels(#20001,0)
|
||||
#20110=@"loc,{#10000},1,1,10,0"
|
||||
locations_default(#20110,#10000,1,1,10,0)
|
||||
hasLocation(#20001,#20110)
|
||||
#20111=@"var;{x};{#20000}"
|
||||
variables(#20111,"x",#20000)
|
||||
#20112=@"var;{y};{#20000}"
|
||||
variables(#20112,"y",#20000)
|
||||
#20113=@"var;{foo};{#20000}"
|
||||
variables(#20113,"foo",#20000)
|
||||
#20114=@"var;{bar};{#20000}"
|
||||
variables(#20114,"bar",#20000)
|
||||
#20115=*
|
||||
stmts(#20115,18,#20001,0,"var x = 1;")
|
||||
hasLocation(#20115,#20005)
|
||||
stmt_containers(#20115,#20001)
|
||||
#20116=*
|
||||
exprs(#20116,64,#20115,0,"x = 1")
|
||||
#20117=@"loc,{#10000},1,5,1,9"
|
||||
locations_default(#20117,#10000,1,5,1,9)
|
||||
hasLocation(#20116,#20117)
|
||||
enclosing_stmt(#20116,#20115)
|
||||
expr_containers(#20116,#20001)
|
||||
#20118=*
|
||||
exprs(#20118,78,#20116,0,"x")
|
||||
hasLocation(#20118,#20025)
|
||||
enclosing_stmt(#20118,#20115)
|
||||
expr_containers(#20118,#20001)
|
||||
literals("x","x",#20118)
|
||||
decl(#20118,#20111)
|
||||
#20119=*
|
||||
exprs(#20119,3,#20116,1,"1")
|
||||
hasLocation(#20119,#20029)
|
||||
enclosing_stmt(#20119,#20115)
|
||||
expr_containers(#20119,#20001)
|
||||
literals("1","1",#20119)
|
||||
#20120=*
|
||||
stmts(#20120,18,#20001,1,"var y = 2;")
|
||||
hasLocation(#20120,#20007)
|
||||
stmt_containers(#20120,#20001)
|
||||
#20121=*
|
||||
exprs(#20121,64,#20120,0,"y = 2")
|
||||
#20122=@"loc,{#10000},2,5,2,9"
|
||||
locations_default(#20122,#10000,2,5,2,9)
|
||||
hasLocation(#20121,#20122)
|
||||
enclosing_stmt(#20121,#20120)
|
||||
expr_containers(#20121,#20001)
|
||||
#20123=*
|
||||
exprs(#20123,78,#20121,0,"y")
|
||||
hasLocation(#20123,#20035)
|
||||
enclosing_stmt(#20123,#20120)
|
||||
expr_containers(#20123,#20001)
|
||||
literals("y","y",#20123)
|
||||
decl(#20123,#20112)
|
||||
#20124=*
|
||||
exprs(#20124,3,#20121,1,"2")
|
||||
hasLocation(#20124,#20039)
|
||||
enclosing_stmt(#20124,#20120)
|
||||
expr_containers(#20124,#20001)
|
||||
literals("2","2",#20124)
|
||||
#20125=*
|
||||
stmts(#20125,18,#20001,2,"var foo = x && y;")
|
||||
hasLocation(#20125,#20009)
|
||||
stmt_containers(#20125,#20001)
|
||||
#20126=*
|
||||
exprs(#20126,64,#20125,0,"foo = x && y")
|
||||
#20127=@"loc,{#10000},3,5,3,16"
|
||||
locations_default(#20127,#10000,3,5,3,16)
|
||||
hasLocation(#20126,#20127)
|
||||
enclosing_stmt(#20126,#20125)
|
||||
expr_containers(#20126,#20001)
|
||||
#20128=*
|
||||
exprs(#20128,78,#20126,0,"foo")
|
||||
hasLocation(#20128,#20045)
|
||||
enclosing_stmt(#20128,#20125)
|
||||
expr_containers(#20128,#20001)
|
||||
literals("foo","foo",#20128)
|
||||
decl(#20128,#20113)
|
||||
#20129=*
|
||||
exprs(#20129,44,#20126,1,"x && y")
|
||||
#20130=@"loc,{#10000},3,11,3,16"
|
||||
locations_default(#20130,#10000,3,11,3,16)
|
||||
hasLocation(#20129,#20130)
|
||||
enclosing_stmt(#20129,#20125)
|
||||
expr_containers(#20129,#20001)
|
||||
#20131=*
|
||||
exprs(#20131,79,#20129,0,"x")
|
||||
hasLocation(#20131,#20049)
|
||||
enclosing_stmt(#20131,#20125)
|
||||
expr_containers(#20131,#20001)
|
||||
literals("x","x",#20131)
|
||||
bind(#20131,#20111)
|
||||
#20132=*
|
||||
exprs(#20132,79,#20129,1,"y")
|
||||
hasLocation(#20132,#20053)
|
||||
enclosing_stmt(#20132,#20125)
|
||||
expr_containers(#20132,#20001)
|
||||
literals("y","y",#20132)
|
||||
bind(#20132,#20112)
|
||||
#20133=*
|
||||
stmts(#20133,18,#20001,3,"var bar = x &&= y;")
|
||||
hasLocation(#20133,#20011)
|
||||
stmt_containers(#20133,#20001)
|
||||
#20134=*
|
||||
exprs(#20134,64,#20133,0,"bar = x &&= y")
|
||||
#20135=@"loc,{#10000},4,5,4,17"
|
||||
locations_default(#20135,#10000,4,5,4,17)
|
||||
hasLocation(#20134,#20135)
|
||||
enclosing_stmt(#20134,#20133)
|
||||
expr_containers(#20134,#20001)
|
||||
#20136=*
|
||||
exprs(#20136,78,#20134,0,"bar")
|
||||
hasLocation(#20136,#20059)
|
||||
enclosing_stmt(#20136,#20133)
|
||||
expr_containers(#20136,#20001)
|
||||
literals("bar","bar",#20136)
|
||||
decl(#20136,#20114)
|
||||
#20137=*
|
||||
exprs(#20137,116,#20134,1,"x &&= y")
|
||||
#20138=@"loc,{#10000},4,11,4,17"
|
||||
locations_default(#20138,#10000,4,11,4,17)
|
||||
hasLocation(#20137,#20138)
|
||||
enclosing_stmt(#20137,#20133)
|
||||
expr_containers(#20137,#20001)
|
||||
#20139=*
|
||||
exprs(#20139,79,#20137,0,"x")
|
||||
hasLocation(#20139,#20063)
|
||||
enclosing_stmt(#20139,#20133)
|
||||
expr_containers(#20139,#20001)
|
||||
literals("x","x",#20139)
|
||||
bind(#20139,#20111)
|
||||
#20140=*
|
||||
exprs(#20140,79,#20137,1,"y")
|
||||
hasLocation(#20140,#20067)
|
||||
enclosing_stmt(#20140,#20133)
|
||||
expr_containers(#20140,#20001)
|
||||
literals("y","y",#20140)
|
||||
bind(#20140,#20112)
|
||||
#20141=*
|
||||
stmts(#20141,2,#20001,4,"console.log(x);")
|
||||
#20142=@"loc,{#10000},5,1,5,15"
|
||||
locations_default(#20142,#10000,5,1,5,15)
|
||||
hasLocation(#20141,#20142)
|
||||
stmt_containers(#20141,#20001)
|
||||
#20143=*
|
||||
exprs(#20143,13,#20141,0,"console.log(x)")
|
||||
#20144=@"loc,{#10000},5,1,5,14"
|
||||
locations_default(#20144,#10000,5,1,5,14)
|
||||
hasLocation(#20143,#20144)
|
||||
enclosing_stmt(#20143,#20141)
|
||||
expr_containers(#20143,#20001)
|
||||
#20145=*
|
||||
exprs(#20145,14,#20143,-1,"console.log")
|
||||
#20146=@"loc,{#10000},5,1,5,11"
|
||||
locations_default(#20146,#10000,5,1,5,11)
|
||||
hasLocation(#20145,#20146)
|
||||
enclosing_stmt(#20145,#20141)
|
||||
expr_containers(#20145,#20001)
|
||||
#20147=*
|
||||
exprs(#20147,79,#20145,0,"console")
|
||||
hasLocation(#20147,#20071)
|
||||
enclosing_stmt(#20147,#20141)
|
||||
expr_containers(#20147,#20001)
|
||||
literals("console","console",#20147)
|
||||
#20148=@"var;{console};{#20000}"
|
||||
variables(#20148,"console",#20000)
|
||||
bind(#20147,#20148)
|
||||
#20149=*
|
||||
exprs(#20149,0,#20145,1,"log")
|
||||
hasLocation(#20149,#20075)
|
||||
enclosing_stmt(#20149,#20141)
|
||||
expr_containers(#20149,#20001)
|
||||
literals("log","log",#20149)
|
||||
#20150=*
|
||||
exprs(#20150,79,#20143,0,"x")
|
||||
hasLocation(#20150,#20079)
|
||||
enclosing_stmt(#20150,#20141)
|
||||
expr_containers(#20150,#20001)
|
||||
literals("x","x",#20150)
|
||||
bind(#20150,#20111)
|
||||
#20151=*
|
||||
stmts(#20151,2,#20001,5,"x &&= y;")
|
||||
hasLocation(#20151,#20017)
|
||||
stmt_containers(#20151,#20001)
|
||||
#20152=*
|
||||
exprs(#20152,116,#20151,0,"x &&= y")
|
||||
#20153=@"loc,{#10000},7,1,7,7"
|
||||
locations_default(#20153,#10000,7,1,7,7)
|
||||
hasLocation(#20152,#20153)
|
||||
enclosing_stmt(#20152,#20151)
|
||||
expr_containers(#20152,#20001)
|
||||
#20154=*
|
||||
exprs(#20154,79,#20152,0,"x")
|
||||
hasLocation(#20154,#20085)
|
||||
enclosing_stmt(#20154,#20151)
|
||||
expr_containers(#20154,#20001)
|
||||
literals("x","x",#20154)
|
||||
bind(#20154,#20111)
|
||||
#20155=*
|
||||
exprs(#20155,79,#20152,1,"y")
|
||||
hasLocation(#20155,#20089)
|
||||
enclosing_stmt(#20155,#20151)
|
||||
expr_containers(#20155,#20001)
|
||||
literals("y","y",#20155)
|
||||
bind(#20155,#20112)
|
||||
#20156=*
|
||||
stmts(#20156,2,#20001,6,"x ||= y;")
|
||||
hasLocation(#20156,#20019)
|
||||
stmt_containers(#20156,#20001)
|
||||
#20157=*
|
||||
exprs(#20157,117,#20156,0,"x ||= y")
|
||||
#20158=@"loc,{#10000},8,1,8,7"
|
||||
locations_default(#20158,#10000,8,1,8,7)
|
||||
hasLocation(#20157,#20158)
|
||||
enclosing_stmt(#20157,#20156)
|
||||
expr_containers(#20157,#20001)
|
||||
#20159=*
|
||||
exprs(#20159,79,#20157,0,"x")
|
||||
hasLocation(#20159,#20093)
|
||||
enclosing_stmt(#20159,#20156)
|
||||
expr_containers(#20159,#20001)
|
||||
literals("x","x",#20159)
|
||||
bind(#20159,#20111)
|
||||
#20160=*
|
||||
exprs(#20160,79,#20157,1,"y")
|
||||
hasLocation(#20160,#20097)
|
||||
enclosing_stmt(#20160,#20156)
|
||||
expr_containers(#20160,#20001)
|
||||
literals("y","y",#20160)
|
||||
bind(#20160,#20112)
|
||||
#20161=*
|
||||
stmts(#20161,2,#20001,7,"x ??= y;")
|
||||
hasLocation(#20161,#20021)
|
||||
stmt_containers(#20161,#20001)
|
||||
#20162=*
|
||||
exprs(#20162,118,#20161,0,"x ??= y")
|
||||
#20163=@"loc,{#10000},9,1,9,7"
|
||||
locations_default(#20163,#10000,9,1,9,7)
|
||||
hasLocation(#20162,#20163)
|
||||
enclosing_stmt(#20162,#20161)
|
||||
expr_containers(#20162,#20001)
|
||||
#20164=*
|
||||
exprs(#20164,79,#20162,0,"x")
|
||||
hasLocation(#20164,#20101)
|
||||
enclosing_stmt(#20164,#20161)
|
||||
expr_containers(#20164,#20001)
|
||||
literals("x","x",#20164)
|
||||
bind(#20164,#20111)
|
||||
#20165=*
|
||||
exprs(#20165,79,#20162,1,"y")
|
||||
hasLocation(#20165,#20105)
|
||||
enclosing_stmt(#20165,#20161)
|
||||
expr_containers(#20165,#20001)
|
||||
literals("y","y",#20165)
|
||||
bind(#20165,#20112)
|
||||
#20166=*
|
||||
entry_cfg_node(#20166,#20001)
|
||||
#20167=@"loc,{#10000},1,1,1,0"
|
||||
locations_default(#20167,#10000,1,1,1,0)
|
||||
hasLocation(#20166,#20167)
|
||||
#20168=*
|
||||
exit_cfg_node(#20168,#20001)
|
||||
hasLocation(#20168,#20109)
|
||||
successor(#20161,#20164)
|
||||
successor(#20164,#20165)
|
||||
successor(#20164,#20168)
|
||||
successor(#20165,#20164)
|
||||
successor(#20162,#20168)
|
||||
successor(#20156,#20159)
|
||||
#20169=*
|
||||
guard_node(#20169,1,#20159)
|
||||
hasLocation(#20169,#20093)
|
||||
successor(#20169,#20161)
|
||||
#20170=*
|
||||
guard_node(#20170,0,#20159)
|
||||
hasLocation(#20170,#20093)
|
||||
successor(#20170,#20160)
|
||||
successor(#20159,#20169)
|
||||
successor(#20159,#20170)
|
||||
successor(#20160,#20159)
|
||||
successor(#20157,#20161)
|
||||
successor(#20151,#20154)
|
||||
#20171=*
|
||||
guard_node(#20171,1,#20154)
|
||||
hasLocation(#20171,#20085)
|
||||
successor(#20171,#20155)
|
||||
#20172=*
|
||||
guard_node(#20172,0,#20154)
|
||||
hasLocation(#20172,#20085)
|
||||
successor(#20172,#20156)
|
||||
successor(#20154,#20171)
|
||||
successor(#20154,#20172)
|
||||
successor(#20155,#20154)
|
||||
successor(#20152,#20156)
|
||||
successor(#20141,#20147)
|
||||
successor(#20150,#20143)
|
||||
successor(#20149,#20145)
|
||||
successor(#20147,#20149)
|
||||
successor(#20145,#20150)
|
||||
successor(#20143,#20151)
|
||||
successor(#20133,#20136)
|
||||
#20173=*
|
||||
guard_node(#20173,1,#20139)
|
||||
hasLocation(#20173,#20063)
|
||||
successor(#20173,#20140)
|
||||
#20174=*
|
||||
guard_node(#20174,0,#20139)
|
||||
hasLocation(#20174,#20063)
|
||||
successor(#20174,#20134)
|
||||
successor(#20139,#20173)
|
||||
successor(#20139,#20174)
|
||||
successor(#20140,#20139)
|
||||
successor(#20137,#20134)
|
||||
successor(#20136,#20139)
|
||||
successor(#20134,#20141)
|
||||
successor(#20125,#20128)
|
||||
successor(#20129,#20131)
|
||||
#20175=*
|
||||
guard_node(#20175,1,#20131)
|
||||
hasLocation(#20175,#20049)
|
||||
successor(#20175,#20132)
|
||||
#20176=*
|
||||
guard_node(#20176,0,#20131)
|
||||
hasLocation(#20176,#20049)
|
||||
successor(#20176,#20126)
|
||||
successor(#20131,#20175)
|
||||
successor(#20131,#20176)
|
||||
successor(#20132,#20126)
|
||||
successor(#20128,#20129)
|
||||
successor(#20126,#20133)
|
||||
successor(#20120,#20123)
|
||||
successor(#20124,#20121)
|
||||
successor(#20123,#20124)
|
||||
successor(#20121,#20125)
|
||||
successor(#20115,#20118)
|
||||
successor(#20119,#20116)
|
||||
successor(#20118,#20119)
|
||||
successor(#20116,#20120)
|
||||
successor(#20166,#20115)
|
||||
numlines(#10000,9,8,1)
|
||||
filetype(#10000,"javascript")
|
||||
435
javascript/extractor/tests/es2021/output/trap/numeric.js.trap
Normal file
435
javascript/extractor/tests/es2021/output/trap/numeric.js.trap
Normal file
@@ -0,0 +1,435 @@
|
||||
#10000=@"/numeric.js;sourcefile"
|
||||
files(#10000,"/numeric.js","numeric","js",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=*
|
||||
comments(#20002,0,#20001," Ah, so a billion","// Ah, so a billion")
|
||||
#20003=@"loc,{#10000},1,25,1,43"
|
||||
locations_default(#20003,#10000,1,25,1,43)
|
||||
hasLocation(#20002,#20003)
|
||||
#20004=*
|
||||
comments(#20004,0,#20001," And this is hundreds of millions","// And ... illions")
|
||||
#20005=@"loc,{#10000},2,25,2,59"
|
||||
locations_default(#20005,#10000,2,25,2,59)
|
||||
hasLocation(#20004,#20005)
|
||||
#20006=*
|
||||
comments(#20006,0,#20001," $123 (12300 cents, apparently)","// $123 ... rently)")
|
||||
#20007=@"loc,{#10000},4,25,4,57"
|
||||
locations_default(#20007,#10000,4,25,4,57)
|
||||
hasLocation(#20006,#20007)
|
||||
#20008=*
|
||||
comments(#20008,0,#20001," $12,300 (woah, that fee!)","// $12, ... t fee!)")
|
||||
#20009=@"loc,{#10000},5,25,5,52"
|
||||
locations_default(#20009,#10000,5,25,5,52)
|
||||
hasLocation(#20008,#20009)
|
||||
#20010=*
|
||||
comments(#20010,0,#20001," 12,345 (1234500 cents, apparently)","// 12,3 ... rently)")
|
||||
#20011=@"loc,{#10000},6,25,6,61"
|
||||
locations_default(#20011,#10000,6,25,6,61)
|
||||
hasLocation(#20010,#20011)
|
||||
#20012=*
|
||||
comments(#20012,0,#20001," 123.45 (4-fixed financial)","// 123. ... ancial)")
|
||||
#20013=@"loc,{#10000},7,25,7,53"
|
||||
locations_default(#20013,#10000,7,25,7,53)
|
||||
hasLocation(#20012,#20013)
|
||||
#20014=*
|
||||
comments(#20014,0,#20001," 1,234,500","// 1,234,500")
|
||||
#20015=@"loc,{#10000},8,25,8,36"
|
||||
locations_default(#20015,#10000,8,25,8,36)
|
||||
hasLocation(#20014,#20015)
|
||||
#20016=*
|
||||
lines(#20016,#20001,"1_000_000_000 // Ah, so a billion","
|
||||
")
|
||||
#20017=@"loc,{#10000},1,1,1,43"
|
||||
locations_default(#20017,#10000,1,1,1,43)
|
||||
hasLocation(#20016,#20017)
|
||||
#20018=*
|
||||
lines(#20018,#20001,"101_475_938.38 // And this is hundreds of millions","
|
||||
")
|
||||
#20019=@"loc,{#10000},2,1,2,59"
|
||||
locations_default(#20019,#10000,2,1,2,59)
|
||||
hasLocation(#20018,#20019)
|
||||
#20020=*
|
||||
lines(#20020,#20001,"","
|
||||
")
|
||||
#20021=@"loc,{#10000},3,1,3,0"
|
||||
locations_default(#20021,#10000,3,1,3,0)
|
||||
hasLocation(#20020,#20021)
|
||||
#20022=*
|
||||
lines(#20022,#20001,"let fee = 123_00; // $123 (12300 cents, apparently)","
|
||||
")
|
||||
#20023=@"loc,{#10000},4,1,4,57"
|
||||
locations_default(#20023,#10000,4,1,4,57)
|
||||
hasLocation(#20022,#20023)
|
||||
#20024=*
|
||||
lines(#20024,#20001,"let fee = 12_300; // $12,300 (woah, that fee!)","
|
||||
")
|
||||
#20025=@"loc,{#10000},5,1,5,52"
|
||||
locations_default(#20025,#10000,5,1,5,52)
|
||||
hasLocation(#20024,#20025)
|
||||
#20026=*
|
||||
lines(#20026,#20001,"let amount = 12345_00; // 12,345 (1234500 cents, apparently)","
|
||||
")
|
||||
#20027=@"loc,{#10000},6,1,6,61"
|
||||
locations_default(#20027,#10000,6,1,6,61)
|
||||
hasLocation(#20026,#20027)
|
||||
#20028=*
|
||||
lines(#20028,#20001,"let amount = 123_4500; // 123.45 (4-fixed financial)","
|
||||
")
|
||||
#20029=@"loc,{#10000},7,1,7,53"
|
||||
locations_default(#20029,#10000,7,1,7,53)
|
||||
hasLocation(#20028,#20029)
|
||||
#20030=*
|
||||
lines(#20030,#20001,"let amount = 1_234_500; // 1,234,500","
|
||||
")
|
||||
#20031=@"loc,{#10000},8,1,8,36"
|
||||
locations_default(#20031,#10000,8,1,8,36)
|
||||
hasLocation(#20030,#20031)
|
||||
numlines(#20001,8,7,7)
|
||||
#20032=*
|
||||
tokeninfo(#20032,3,#20001,0,"1_000_000_000")
|
||||
#20033=@"loc,{#10000},1,1,1,13"
|
||||
locations_default(#20033,#10000,1,1,1,13)
|
||||
hasLocation(#20032,#20033)
|
||||
#20034=*
|
||||
tokeninfo(#20034,3,#20001,1,"101_475_938.38")
|
||||
#20035=@"loc,{#10000},2,1,2,14"
|
||||
locations_default(#20035,#10000,2,1,2,14)
|
||||
hasLocation(#20034,#20035)
|
||||
next_token(#20002,#20034)
|
||||
#20036=*
|
||||
tokeninfo(#20036,7,#20001,2,"let")
|
||||
#20037=@"loc,{#10000},4,1,4,3"
|
||||
locations_default(#20037,#10000,4,1,4,3)
|
||||
hasLocation(#20036,#20037)
|
||||
next_token(#20004,#20036)
|
||||
#20038=*
|
||||
tokeninfo(#20038,6,#20001,3,"fee")
|
||||
#20039=@"loc,{#10000},4,5,4,7"
|
||||
locations_default(#20039,#10000,4,5,4,7)
|
||||
hasLocation(#20038,#20039)
|
||||
#20040=*
|
||||
tokeninfo(#20040,8,#20001,4,"=")
|
||||
#20041=@"loc,{#10000},4,9,4,9"
|
||||
locations_default(#20041,#10000,4,9,4,9)
|
||||
hasLocation(#20040,#20041)
|
||||
#20042=*
|
||||
tokeninfo(#20042,3,#20001,5,"123_00")
|
||||
#20043=@"loc,{#10000},4,11,4,16"
|
||||
locations_default(#20043,#10000,4,11,4,16)
|
||||
hasLocation(#20042,#20043)
|
||||
#20044=*
|
||||
tokeninfo(#20044,8,#20001,6,";")
|
||||
#20045=@"loc,{#10000},4,17,4,17"
|
||||
locations_default(#20045,#10000,4,17,4,17)
|
||||
hasLocation(#20044,#20045)
|
||||
#20046=*
|
||||
tokeninfo(#20046,7,#20001,7,"let")
|
||||
#20047=@"loc,{#10000},5,1,5,3"
|
||||
locations_default(#20047,#10000,5,1,5,3)
|
||||
hasLocation(#20046,#20047)
|
||||
next_token(#20006,#20046)
|
||||
#20048=*
|
||||
tokeninfo(#20048,6,#20001,8,"fee")
|
||||
#20049=@"loc,{#10000},5,5,5,7"
|
||||
locations_default(#20049,#10000,5,5,5,7)
|
||||
hasLocation(#20048,#20049)
|
||||
#20050=*
|
||||
tokeninfo(#20050,8,#20001,9,"=")
|
||||
#20051=@"loc,{#10000},5,9,5,9"
|
||||
locations_default(#20051,#10000,5,9,5,9)
|
||||
hasLocation(#20050,#20051)
|
||||
#20052=*
|
||||
tokeninfo(#20052,3,#20001,10,"12_300")
|
||||
#20053=@"loc,{#10000},5,11,5,16"
|
||||
locations_default(#20053,#10000,5,11,5,16)
|
||||
hasLocation(#20052,#20053)
|
||||
#20054=*
|
||||
tokeninfo(#20054,8,#20001,11,";")
|
||||
#20055=@"loc,{#10000},5,17,5,17"
|
||||
locations_default(#20055,#10000,5,17,5,17)
|
||||
hasLocation(#20054,#20055)
|
||||
#20056=*
|
||||
tokeninfo(#20056,7,#20001,12,"let")
|
||||
#20057=@"loc,{#10000},6,1,6,3"
|
||||
locations_default(#20057,#10000,6,1,6,3)
|
||||
hasLocation(#20056,#20057)
|
||||
next_token(#20008,#20056)
|
||||
#20058=*
|
||||
tokeninfo(#20058,6,#20001,13,"amount")
|
||||
#20059=@"loc,{#10000},6,5,6,10"
|
||||
locations_default(#20059,#10000,6,5,6,10)
|
||||
hasLocation(#20058,#20059)
|
||||
#20060=*
|
||||
tokeninfo(#20060,8,#20001,14,"=")
|
||||
#20061=@"loc,{#10000},6,12,6,12"
|
||||
locations_default(#20061,#10000,6,12,6,12)
|
||||
hasLocation(#20060,#20061)
|
||||
#20062=*
|
||||
tokeninfo(#20062,3,#20001,15,"12345_00")
|
||||
#20063=@"loc,{#10000},6,14,6,21"
|
||||
locations_default(#20063,#10000,6,14,6,21)
|
||||
hasLocation(#20062,#20063)
|
||||
#20064=*
|
||||
tokeninfo(#20064,8,#20001,16,";")
|
||||
#20065=@"loc,{#10000},6,22,6,22"
|
||||
locations_default(#20065,#10000,6,22,6,22)
|
||||
hasLocation(#20064,#20065)
|
||||
#20066=*
|
||||
tokeninfo(#20066,7,#20001,17,"let")
|
||||
#20067=@"loc,{#10000},7,1,7,3"
|
||||
locations_default(#20067,#10000,7,1,7,3)
|
||||
hasLocation(#20066,#20067)
|
||||
next_token(#20010,#20066)
|
||||
#20068=*
|
||||
tokeninfo(#20068,6,#20001,18,"amount")
|
||||
#20069=@"loc,{#10000},7,5,7,10"
|
||||
locations_default(#20069,#10000,7,5,7,10)
|
||||
hasLocation(#20068,#20069)
|
||||
#20070=*
|
||||
tokeninfo(#20070,8,#20001,19,"=")
|
||||
#20071=@"loc,{#10000},7,12,7,12"
|
||||
locations_default(#20071,#10000,7,12,7,12)
|
||||
hasLocation(#20070,#20071)
|
||||
#20072=*
|
||||
tokeninfo(#20072,3,#20001,20,"123_4500")
|
||||
#20073=@"loc,{#10000},7,14,7,21"
|
||||
locations_default(#20073,#10000,7,14,7,21)
|
||||
hasLocation(#20072,#20073)
|
||||
#20074=*
|
||||
tokeninfo(#20074,8,#20001,21,";")
|
||||
#20075=@"loc,{#10000},7,22,7,22"
|
||||
locations_default(#20075,#10000,7,22,7,22)
|
||||
hasLocation(#20074,#20075)
|
||||
#20076=*
|
||||
tokeninfo(#20076,7,#20001,22,"let")
|
||||
#20077=@"loc,{#10000},8,1,8,3"
|
||||
locations_default(#20077,#10000,8,1,8,3)
|
||||
hasLocation(#20076,#20077)
|
||||
next_token(#20012,#20076)
|
||||
#20078=*
|
||||
tokeninfo(#20078,6,#20001,23,"amount")
|
||||
#20079=@"loc,{#10000},8,5,8,10"
|
||||
locations_default(#20079,#10000,8,5,8,10)
|
||||
hasLocation(#20078,#20079)
|
||||
#20080=*
|
||||
tokeninfo(#20080,8,#20001,24,"=")
|
||||
#20081=@"loc,{#10000},8,12,8,12"
|
||||
locations_default(#20081,#10000,8,12,8,12)
|
||||
hasLocation(#20080,#20081)
|
||||
#20082=*
|
||||
tokeninfo(#20082,3,#20001,25,"1_234_500")
|
||||
#20083=@"loc,{#10000},8,14,8,22"
|
||||
locations_default(#20083,#10000,8,14,8,22)
|
||||
hasLocation(#20082,#20083)
|
||||
#20084=*
|
||||
tokeninfo(#20084,8,#20001,26,";")
|
||||
#20085=@"loc,{#10000},8,23,8,23"
|
||||
locations_default(#20085,#10000,8,23,8,23)
|
||||
hasLocation(#20084,#20085)
|
||||
#20086=*
|
||||
tokeninfo(#20086,0,#20001,27,"")
|
||||
#20087=@"loc,{#10000},9,1,9,0"
|
||||
locations_default(#20087,#10000,9,1,9,0)
|
||||
hasLocation(#20086,#20087)
|
||||
next_token(#20014,#20086)
|
||||
toplevels(#20001,0)
|
||||
#20088=@"loc,{#10000},1,1,9,0"
|
||||
locations_default(#20088,#10000,1,1,9,0)
|
||||
hasLocation(#20001,#20088)
|
||||
#20089=@"var;{fee};{#20000}"
|
||||
variables(#20089,"fee",#20000)
|
||||
#20090=@"var;{amount};{#20000}"
|
||||
variables(#20090,"amount",#20000)
|
||||
#20091=*
|
||||
stmts(#20091,2,#20001,0,"1_000_000_000")
|
||||
hasLocation(#20091,#20033)
|
||||
stmt_containers(#20091,#20001)
|
||||
#20092=*
|
||||
exprs(#20092,3,#20091,0,"1_000_000_000")
|
||||
hasLocation(#20092,#20033)
|
||||
enclosing_stmt(#20092,#20091)
|
||||
expr_containers(#20092,#20001)
|
||||
literals("1000000000","1_000_000_000",#20092)
|
||||
#20093=*
|
||||
stmts(#20093,2,#20001,1,"101_475_938.38")
|
||||
hasLocation(#20093,#20035)
|
||||
stmt_containers(#20093,#20001)
|
||||
#20094=*
|
||||
exprs(#20094,3,#20093,0,"101_475_938.38")
|
||||
hasLocation(#20094,#20035)
|
||||
enclosing_stmt(#20094,#20093)
|
||||
expr_containers(#20094,#20001)
|
||||
literals("1.0147593838E8","101_475_938.38",#20094)
|
||||
#20095=*
|
||||
stmts(#20095,23,#20001,2,"let fee = 123_00;")
|
||||
#20096=@"loc,{#10000},4,1,4,17"
|
||||
locations_default(#20096,#10000,4,1,4,17)
|
||||
hasLocation(#20095,#20096)
|
||||
stmt_containers(#20095,#20001)
|
||||
#20097=*
|
||||
exprs(#20097,64,#20095,0,"fee = 123_00")
|
||||
#20098=@"loc,{#10000},4,5,4,16"
|
||||
locations_default(#20098,#10000,4,5,4,16)
|
||||
hasLocation(#20097,#20098)
|
||||
enclosing_stmt(#20097,#20095)
|
||||
expr_containers(#20097,#20001)
|
||||
#20099=*
|
||||
exprs(#20099,78,#20097,0,"fee")
|
||||
hasLocation(#20099,#20039)
|
||||
enclosing_stmt(#20099,#20095)
|
||||
expr_containers(#20099,#20001)
|
||||
literals("fee","fee",#20099)
|
||||
decl(#20099,#20089)
|
||||
#20100=*
|
||||
exprs(#20100,3,#20097,1,"123_00")
|
||||
hasLocation(#20100,#20043)
|
||||
enclosing_stmt(#20100,#20095)
|
||||
expr_containers(#20100,#20001)
|
||||
literals("12300","123_00",#20100)
|
||||
#20101=*
|
||||
stmts(#20101,23,#20001,3,"let fee = 12_300;")
|
||||
#20102=@"loc,{#10000},5,1,5,17"
|
||||
locations_default(#20102,#10000,5,1,5,17)
|
||||
hasLocation(#20101,#20102)
|
||||
stmt_containers(#20101,#20001)
|
||||
#20103=*
|
||||
exprs(#20103,64,#20101,0,"fee = 12_300")
|
||||
#20104=@"loc,{#10000},5,5,5,16"
|
||||
locations_default(#20104,#10000,5,5,5,16)
|
||||
hasLocation(#20103,#20104)
|
||||
enclosing_stmt(#20103,#20101)
|
||||
expr_containers(#20103,#20001)
|
||||
#20105=*
|
||||
exprs(#20105,78,#20103,0,"fee")
|
||||
hasLocation(#20105,#20049)
|
||||
enclosing_stmt(#20105,#20101)
|
||||
expr_containers(#20105,#20001)
|
||||
literals("fee","fee",#20105)
|
||||
decl(#20105,#20089)
|
||||
#20106=*
|
||||
exprs(#20106,3,#20103,1,"12_300")
|
||||
hasLocation(#20106,#20053)
|
||||
enclosing_stmt(#20106,#20101)
|
||||
expr_containers(#20106,#20001)
|
||||
literals("12300","12_300",#20106)
|
||||
#20107=*
|
||||
stmts(#20107,23,#20001,4,"let amo ... 345_00;")
|
||||
#20108=@"loc,{#10000},6,1,6,22"
|
||||
locations_default(#20108,#10000,6,1,6,22)
|
||||
hasLocation(#20107,#20108)
|
||||
stmt_containers(#20107,#20001)
|
||||
#20109=*
|
||||
exprs(#20109,64,#20107,0,"amount = 12345_00")
|
||||
#20110=@"loc,{#10000},6,5,6,21"
|
||||
locations_default(#20110,#10000,6,5,6,21)
|
||||
hasLocation(#20109,#20110)
|
||||
enclosing_stmt(#20109,#20107)
|
||||
expr_containers(#20109,#20001)
|
||||
#20111=*
|
||||
exprs(#20111,78,#20109,0,"amount")
|
||||
hasLocation(#20111,#20059)
|
||||
enclosing_stmt(#20111,#20107)
|
||||
expr_containers(#20111,#20001)
|
||||
literals("amount","amount",#20111)
|
||||
decl(#20111,#20090)
|
||||
#20112=*
|
||||
exprs(#20112,3,#20109,1,"12345_00")
|
||||
hasLocation(#20112,#20063)
|
||||
enclosing_stmt(#20112,#20107)
|
||||
expr_containers(#20112,#20001)
|
||||
literals("1234500","12345_00",#20112)
|
||||
#20113=*
|
||||
stmts(#20113,23,#20001,5,"let amo ... 3_4500;")
|
||||
#20114=@"loc,{#10000},7,1,7,22"
|
||||
locations_default(#20114,#10000,7,1,7,22)
|
||||
hasLocation(#20113,#20114)
|
||||
stmt_containers(#20113,#20001)
|
||||
#20115=*
|
||||
exprs(#20115,64,#20113,0,"amount = 123_4500")
|
||||
#20116=@"loc,{#10000},7,5,7,21"
|
||||
locations_default(#20116,#10000,7,5,7,21)
|
||||
hasLocation(#20115,#20116)
|
||||
enclosing_stmt(#20115,#20113)
|
||||
expr_containers(#20115,#20001)
|
||||
#20117=*
|
||||
exprs(#20117,78,#20115,0,"amount")
|
||||
hasLocation(#20117,#20069)
|
||||
enclosing_stmt(#20117,#20113)
|
||||
expr_containers(#20117,#20001)
|
||||
literals("amount","amount",#20117)
|
||||
decl(#20117,#20090)
|
||||
#20118=*
|
||||
exprs(#20118,3,#20115,1,"123_4500")
|
||||
hasLocation(#20118,#20073)
|
||||
enclosing_stmt(#20118,#20113)
|
||||
expr_containers(#20118,#20001)
|
||||
literals("1234500","123_4500",#20118)
|
||||
#20119=*
|
||||
stmts(#20119,23,#20001,6,"let amo ... 34_500;")
|
||||
#20120=@"loc,{#10000},8,1,8,23"
|
||||
locations_default(#20120,#10000,8,1,8,23)
|
||||
hasLocation(#20119,#20120)
|
||||
stmt_containers(#20119,#20001)
|
||||
#20121=*
|
||||
exprs(#20121,64,#20119,0,"amount = 1_234_500")
|
||||
#20122=@"loc,{#10000},8,5,8,22"
|
||||
locations_default(#20122,#10000,8,5,8,22)
|
||||
hasLocation(#20121,#20122)
|
||||
enclosing_stmt(#20121,#20119)
|
||||
expr_containers(#20121,#20001)
|
||||
#20123=*
|
||||
exprs(#20123,78,#20121,0,"amount")
|
||||
hasLocation(#20123,#20079)
|
||||
enclosing_stmt(#20123,#20119)
|
||||
expr_containers(#20123,#20001)
|
||||
literals("amount","amount",#20123)
|
||||
decl(#20123,#20090)
|
||||
#20124=*
|
||||
exprs(#20124,3,#20121,1,"1_234_500")
|
||||
hasLocation(#20124,#20083)
|
||||
enclosing_stmt(#20124,#20119)
|
||||
expr_containers(#20124,#20001)
|
||||
literals("1234500","1_234_500",#20124)
|
||||
#20125=*
|
||||
entry_cfg_node(#20125,#20001)
|
||||
#20126=@"loc,{#10000},1,1,1,0"
|
||||
locations_default(#20126,#10000,1,1,1,0)
|
||||
hasLocation(#20125,#20126)
|
||||
#20127=*
|
||||
exit_cfg_node(#20127,#20001)
|
||||
hasLocation(#20127,#20087)
|
||||
successor(#20119,#20123)
|
||||
successor(#20124,#20121)
|
||||
successor(#20123,#20124)
|
||||
successor(#20121,#20127)
|
||||
successor(#20113,#20117)
|
||||
successor(#20118,#20115)
|
||||
successor(#20117,#20118)
|
||||
successor(#20115,#20119)
|
||||
successor(#20107,#20111)
|
||||
successor(#20112,#20109)
|
||||
successor(#20111,#20112)
|
||||
successor(#20109,#20113)
|
||||
successor(#20101,#20105)
|
||||
successor(#20106,#20103)
|
||||
successor(#20105,#20106)
|
||||
successor(#20103,#20107)
|
||||
successor(#20095,#20099)
|
||||
successor(#20100,#20097)
|
||||
successor(#20099,#20100)
|
||||
successor(#20097,#20101)
|
||||
successor(#20093,#20094)
|
||||
successor(#20094,#20095)
|
||||
successor(#20091,#20092)
|
||||
successor(#20092,#20093)
|
||||
successor(#20125,#20091)
|
||||
numlines(#10000,8,7,7)
|
||||
filetype(#10000,"javascript")
|
||||
@@ -0,0 +1,15 @@
|
||||
#10000=@"/package.json;sourcefile"
|
||||
files(#10000,"/package.json","package","json",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=*
|
||||
json_errors(#20000,"Error: Unexpected token")
|
||||
#20001=@"loc,{#10000},1,1,1,1"
|
||||
locations_default(#20001,#10000,1,1,1,1)
|
||||
hasLocation(#20000,#20001)
|
||||
numlines(#10000,0,0,0)
|
||||
filetype(#10000,"json")
|
||||
@@ -0,0 +1,28 @@
|
||||
#10000=@"/tst.js;sourcefile"
|
||||
files(#10000,"/tst.js","tst","js",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"
|
||||
numlines(#20001,0,0,0)
|
||||
#20002=*
|
||||
tokeninfo(#20002,0,#20001,0,"")
|
||||
#20003=@"loc,{#10000},1,1,1,0"
|
||||
locations_default(#20003,#10000,1,1,1,0)
|
||||
hasLocation(#20002,#20003)
|
||||
toplevels(#20001,0)
|
||||
hasLocation(#20001,#20003)
|
||||
#20004=*
|
||||
entry_cfg_node(#20004,#20001)
|
||||
hasLocation(#20004,#20003)
|
||||
#20005=*
|
||||
exit_cfg_node(#20005,#20001)
|
||||
hasLocation(#20005,#20003)
|
||||
successor(#20004,#20005)
|
||||
numlines(#10000,0,0,0)
|
||||
filetype(#10000,"javascript")
|
||||
3
javascript/extractor/tests/ts/input/importNonStrings.ts
Normal file
3
javascript/extractor/tests/ts/input/importNonStrings.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
type X = import(3);
|
||||
type Y = import(`Foo`);
|
||||
type Z = import(Y);
|
||||
@@ -0,0 +1,252 @@
|
||||
#10000=@"/importNonStrings.ts;sourcefile"
|
||||
files(#10000,"/importNonStrings.ts","importNonStrings","ts",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,"type X = import(3);","
|
||||
")
|
||||
#20003=@"loc,{#10000},1,1,1,19"
|
||||
locations_default(#20003,#10000,1,1,1,19)
|
||||
hasLocation(#20002,#20003)
|
||||
#20004=*
|
||||
lines(#20004,#20001,"type Y = import(`Foo`);","
|
||||
")
|
||||
#20005=@"loc,{#10000},2,1,2,23"
|
||||
locations_default(#20005,#10000,2,1,2,23)
|
||||
hasLocation(#20004,#20005)
|
||||
#20006=*
|
||||
lines(#20006,#20001,"type Z = import(Y);","
|
||||
")
|
||||
#20007=@"loc,{#10000},3,1,3,19"
|
||||
locations_default(#20007,#10000,3,1,3,19)
|
||||
hasLocation(#20006,#20007)
|
||||
numlines(#20001,3,3,0)
|
||||
#20008=*
|
||||
tokeninfo(#20008,7,#20001,0,"type")
|
||||
#20009=@"loc,{#10000},1,1,1,4"
|
||||
locations_default(#20009,#10000,1,1,1,4)
|
||||
hasLocation(#20008,#20009)
|
||||
#20010=*
|
||||
tokeninfo(#20010,6,#20001,1,"X")
|
||||
#20011=@"loc,{#10000},1,6,1,6"
|
||||
locations_default(#20011,#10000,1,6,1,6)
|
||||
hasLocation(#20010,#20011)
|
||||
#20012=*
|
||||
tokeninfo(#20012,8,#20001,2,"=")
|
||||
#20013=@"loc,{#10000},1,8,1,8"
|
||||
locations_default(#20013,#10000,1,8,1,8)
|
||||
hasLocation(#20012,#20013)
|
||||
#20014=*
|
||||
tokeninfo(#20014,7,#20001,3,"import")
|
||||
#20015=@"loc,{#10000},1,10,1,15"
|
||||
locations_default(#20015,#10000,1,10,1,15)
|
||||
hasLocation(#20014,#20015)
|
||||
#20016=*
|
||||
tokeninfo(#20016,8,#20001,4,"(")
|
||||
#20017=@"loc,{#10000},1,16,1,16"
|
||||
locations_default(#20017,#10000,1,16,1,16)
|
||||
hasLocation(#20016,#20017)
|
||||
#20018=*
|
||||
tokeninfo(#20018,3,#20001,5,"3")
|
||||
#20019=@"loc,{#10000},1,17,1,17"
|
||||
locations_default(#20019,#10000,1,17,1,17)
|
||||
hasLocation(#20018,#20019)
|
||||
#20020=*
|
||||
tokeninfo(#20020,8,#20001,6,")")
|
||||
#20021=@"loc,{#10000},1,18,1,18"
|
||||
locations_default(#20021,#10000,1,18,1,18)
|
||||
hasLocation(#20020,#20021)
|
||||
#20022=*
|
||||
tokeninfo(#20022,8,#20001,7,";")
|
||||
#20023=@"loc,{#10000},1,19,1,19"
|
||||
locations_default(#20023,#10000,1,19,1,19)
|
||||
hasLocation(#20022,#20023)
|
||||
#20024=*
|
||||
tokeninfo(#20024,7,#20001,8,"type")
|
||||
#20025=@"loc,{#10000},2,1,2,4"
|
||||
locations_default(#20025,#10000,2,1,2,4)
|
||||
hasLocation(#20024,#20025)
|
||||
#20026=*
|
||||
tokeninfo(#20026,6,#20001,9,"Y")
|
||||
#20027=@"loc,{#10000},2,6,2,6"
|
||||
locations_default(#20027,#10000,2,6,2,6)
|
||||
hasLocation(#20026,#20027)
|
||||
#20028=*
|
||||
tokeninfo(#20028,8,#20001,10,"=")
|
||||
#20029=@"loc,{#10000},2,8,2,8"
|
||||
locations_default(#20029,#10000,2,8,2,8)
|
||||
hasLocation(#20028,#20029)
|
||||
#20030=*
|
||||
tokeninfo(#20030,7,#20001,11,"import")
|
||||
#20031=@"loc,{#10000},2,10,2,15"
|
||||
locations_default(#20031,#10000,2,10,2,15)
|
||||
hasLocation(#20030,#20031)
|
||||
#20032=*
|
||||
tokeninfo(#20032,8,#20001,12,"(")
|
||||
#20033=@"loc,{#10000},2,16,2,16"
|
||||
locations_default(#20033,#10000,2,16,2,16)
|
||||
hasLocation(#20032,#20033)
|
||||
#20034=*
|
||||
tokeninfo(#20034,4,#20001,13,"`Foo`")
|
||||
#20035=@"loc,{#10000},2,17,2,21"
|
||||
locations_default(#20035,#10000,2,17,2,21)
|
||||
hasLocation(#20034,#20035)
|
||||
#20036=*
|
||||
tokeninfo(#20036,8,#20001,14,")")
|
||||
#20037=@"loc,{#10000},2,22,2,22"
|
||||
locations_default(#20037,#10000,2,22,2,22)
|
||||
hasLocation(#20036,#20037)
|
||||
#20038=*
|
||||
tokeninfo(#20038,8,#20001,15,";")
|
||||
#20039=@"loc,{#10000},2,23,2,23"
|
||||
locations_default(#20039,#10000,2,23,2,23)
|
||||
hasLocation(#20038,#20039)
|
||||
#20040=*
|
||||
tokeninfo(#20040,7,#20001,16,"type")
|
||||
#20041=@"loc,{#10000},3,1,3,4"
|
||||
locations_default(#20041,#10000,3,1,3,4)
|
||||
hasLocation(#20040,#20041)
|
||||
#20042=*
|
||||
tokeninfo(#20042,6,#20001,17,"Z")
|
||||
#20043=@"loc,{#10000},3,6,3,6"
|
||||
locations_default(#20043,#10000,3,6,3,6)
|
||||
hasLocation(#20042,#20043)
|
||||
#20044=*
|
||||
tokeninfo(#20044,8,#20001,18,"=")
|
||||
#20045=@"loc,{#10000},3,8,3,8"
|
||||
locations_default(#20045,#10000,3,8,3,8)
|
||||
hasLocation(#20044,#20045)
|
||||
#20046=*
|
||||
tokeninfo(#20046,7,#20001,19,"import")
|
||||
#20047=@"loc,{#10000},3,10,3,15"
|
||||
locations_default(#20047,#10000,3,10,3,15)
|
||||
hasLocation(#20046,#20047)
|
||||
#20048=*
|
||||
tokeninfo(#20048,8,#20001,20,"(")
|
||||
#20049=@"loc,{#10000},3,16,3,16"
|
||||
locations_default(#20049,#10000,3,16,3,16)
|
||||
hasLocation(#20048,#20049)
|
||||
#20050=*
|
||||
tokeninfo(#20050,6,#20001,21,"Y")
|
||||
#20051=@"loc,{#10000},3,17,3,17"
|
||||
locations_default(#20051,#10000,3,17,3,17)
|
||||
hasLocation(#20050,#20051)
|
||||
#20052=*
|
||||
tokeninfo(#20052,8,#20001,22,")")
|
||||
#20053=@"loc,{#10000},3,18,3,18"
|
||||
locations_default(#20053,#10000,3,18,3,18)
|
||||
hasLocation(#20052,#20053)
|
||||
#20054=*
|
||||
tokeninfo(#20054,8,#20001,23,";")
|
||||
#20055=@"loc,{#10000},3,19,3,19"
|
||||
locations_default(#20055,#10000,3,19,3,19)
|
||||
hasLocation(#20054,#20055)
|
||||
#20056=*
|
||||
tokeninfo(#20056,0,#20001,24,"")
|
||||
#20057=@"loc,{#10000},4,1,4,0"
|
||||
locations_default(#20057,#10000,4,1,4,0)
|
||||
hasLocation(#20056,#20057)
|
||||
toplevels(#20001,0)
|
||||
#20058=@"loc,{#10000},1,1,4,0"
|
||||
locations_default(#20058,#10000,1,1,4,0)
|
||||
hasLocation(#20001,#20058)
|
||||
#20059=@"local_type_name;{X};{#20000}"
|
||||
local_type_names(#20059,"X",#20000)
|
||||
#20060=@"local_type_name;{Y};{#20000}"
|
||||
local_type_names(#20060,"Y",#20000)
|
||||
#20061=@"local_type_name;{Z};{#20000}"
|
||||
local_type_names(#20061,"Z",#20000)
|
||||
#20062=*
|
||||
stmts(#20062,35,#20001,0,"type X = import(3);")
|
||||
hasLocation(#20062,#20003)
|
||||
stmt_containers(#20062,#20001)
|
||||
#20063=*
|
||||
typeexprs(#20063,1,#20062,0,"X")
|
||||
hasLocation(#20063,#20011)
|
||||
enclosing_stmt(#20063,#20062)
|
||||
expr_containers(#20063,#20001)
|
||||
literals("X","X",#20063)
|
||||
typedecl(#20063,#20059)
|
||||
#20064=*
|
||||
typeexprs(#20064,30,#20062,1,"import(3)")
|
||||
#20065=@"loc,{#10000},1,10,1,18"
|
||||
locations_default(#20065,#10000,1,10,1,18)
|
||||
hasLocation(#20064,#20065)
|
||||
enclosing_stmt(#20064,#20062)
|
||||
expr_containers(#20064,#20001)
|
||||
#20066=*
|
||||
typeexprs(#20066,4,#20064,0,"3")
|
||||
hasLocation(#20066,#20019)
|
||||
enclosing_stmt(#20066,#20062)
|
||||
expr_containers(#20066,#20001)
|
||||
literals("3","3",#20066)
|
||||
#20067=*
|
||||
stmts(#20067,35,#20001,1,"type Y ... `Foo`);")
|
||||
hasLocation(#20067,#20005)
|
||||
stmt_containers(#20067,#20001)
|
||||
#20068=*
|
||||
typeexprs(#20068,1,#20067,0,"Y")
|
||||
hasLocation(#20068,#20027)
|
||||
enclosing_stmt(#20068,#20067)
|
||||
expr_containers(#20068,#20001)
|
||||
literals("Y","Y",#20068)
|
||||
typedecl(#20068,#20060)
|
||||
#20069=*
|
||||
typeexprs(#20069,30,#20067,1,"import(`Foo`)")
|
||||
#20070=@"loc,{#10000},2,10,2,22"
|
||||
locations_default(#20070,#10000,2,10,2,22)
|
||||
hasLocation(#20069,#20070)
|
||||
enclosing_stmt(#20069,#20067)
|
||||
expr_containers(#20069,#20001)
|
||||
#20071=*
|
||||
typeexprs(#20071,3,#20069,0,"`Foo`")
|
||||
hasLocation(#20071,#20035)
|
||||
enclosing_stmt(#20071,#20067)
|
||||
expr_containers(#20071,#20001)
|
||||
literals("Foo","`Foo`",#20071)
|
||||
#20072=*
|
||||
stmts(#20072,35,#20001,2,"type Z = import(Y);")
|
||||
hasLocation(#20072,#20007)
|
||||
stmt_containers(#20072,#20001)
|
||||
#20073=*
|
||||
typeexprs(#20073,1,#20072,0,"Z")
|
||||
hasLocation(#20073,#20043)
|
||||
enclosing_stmt(#20073,#20072)
|
||||
expr_containers(#20073,#20001)
|
||||
literals("Z","Z",#20073)
|
||||
typedecl(#20073,#20061)
|
||||
#20074=*
|
||||
typeexprs(#20074,30,#20072,1,"import(Y)")
|
||||
#20075=@"loc,{#10000},3,10,3,18"
|
||||
locations_default(#20075,#10000,3,10,3,18)
|
||||
hasLocation(#20074,#20075)
|
||||
enclosing_stmt(#20074,#20072)
|
||||
expr_containers(#20074,#20001)
|
||||
#20076=*
|
||||
typeexprs(#20076,0,#20074,0,"Y")
|
||||
hasLocation(#20076,#20051)
|
||||
enclosing_stmt(#20076,#20072)
|
||||
expr_containers(#20076,#20001)
|
||||
literals("Y","Y",#20076)
|
||||
typebind(#20076,#20060)
|
||||
#20077=*
|
||||
entry_cfg_node(#20077,#20001)
|
||||
#20078=@"loc,{#10000},1,1,1,0"
|
||||
locations_default(#20078,#10000,1,1,1,0)
|
||||
hasLocation(#20077,#20078)
|
||||
#20079=*
|
||||
exit_cfg_node(#20079,#20001)
|
||||
hasLocation(#20079,#20057)
|
||||
successor(#20072,#20079)
|
||||
successor(#20067,#20072)
|
||||
successor(#20062,#20067)
|
||||
successor(#20077,#20062)
|
||||
numlines(#10000,3,3,0)
|
||||
filetype(#10000,"typescript")
|
||||
@@ -31,7 +31,7 @@ predicate isStringSplitOrReplace(MethodCallExpr mce) {
|
||||
mce.getMethodName() = name and
|
||||
mce.getNumArgument() = arity
|
||||
|
|
||||
name = "replace" and arity = 2
|
||||
name = ["replace", "replaceAll"] and arity = 2
|
||||
or
|
||||
name = "split" and
|
||||
(arity = 1 or arity = 2)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @deprecated
|
||||
* @name External dependencies
|
||||
* @description Count the number of dependencies a JavaScript source file has on
|
||||
* NPM packages or framework libraries.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @deprecated
|
||||
* @name External dependency source links
|
||||
* @kind source-link
|
||||
* @metricType externalDependency
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @deprecated
|
||||
* @name Duplicated lines in files
|
||||
* @description The number of lines in a file (including code, comment and whitespace lines)
|
||||
* occurring in a block of lines that is duplicated at least once somewhere else.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @deprecated
|
||||
* @name Similar lines in files
|
||||
* @description The number of lines in a file (including code, comment and whitespace lines)
|
||||
* occurring in a block of lines that is similar to a block of lines seen
|
||||
|
||||
@@ -70,7 +70,7 @@ predicate regExpMatchesString(RegExpTerm t, string s) {
|
||||
|
||||
from MethodCallExpr repl, string s, string friendly
|
||||
where
|
||||
repl.getMethodName() = "replace" and
|
||||
repl.getMethodName() = ["replace", "replaceAll"] and
|
||||
matchesString(repl.getArgument(0), s) and
|
||||
repl.getArgument(1).getStringValue() = s and
|
||||
(if s = "" then friendly = "the empty string" else friendly = "'" + s + "'")
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @id js/incomplete-hostname-regexp
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-20
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -75,7 +75,7 @@ DataFlow::Node schemeCheck(DataFlow::Node nd, DangerousScheme scheme) {
|
||||
exists(DataFlow::MethodCallNode stringop |
|
||||
stringop.getMethodName().matches("trim%") or
|
||||
stringop.getMethodName().matches("to%Case") or
|
||||
stringop.getMethodName() = "replace"
|
||||
stringop.getMethodName() = ["replace", "replaceAll"]
|
||||
|
|
||||
result = schemeCheck(stringop, scheme) and
|
||||
nd = stringop.getReceiver()
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @id js/incomplete-url-substring-sanitization
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-20
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @id js/regex/missing-regexp-anchor
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-20
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* @id js/useless-regexp-character-escape
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-20
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -50,7 +50,7 @@ system's passwords.
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php/Path_traversal">Path Traversal</a>.</li>
|
||||
<li>OWASP: <a href="https://owasp.org/www-community/attacks/Path_Traversal">Path Traversal</a>.</li>
|
||||
<li>npm: <a href="https://www.npmjs.com/package/sanitize-filename">sanitize-filename</a> package.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -60,7 +60,7 @@ Snyk:
|
||||
</li>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/Path_traversal">Path Traversal</a>.
|
||||
<a href="https://owasp.org/www-community/attacks/Path_Traversal">Path Traversal</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-116
|
||||
* external/cwe/cwe-20
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* @tags security
|
||||
* external/cwe/cwe-079
|
||||
* external/cwe/cwe-116
|
||||
* external/cwe/cwe-20
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-116
|
||||
* external/cwe/cwe-20
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-116
|
||||
* external/cwe/cwe-20
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import javascript
|
||||
@@ -79,6 +79,7 @@ predicate allBackslashesEscaped(DataFlow::Node nd) {
|
||||
// flow through string methods
|
||||
exists(DataFlow::MethodCallNode mc, string m |
|
||||
m = "replace" or
|
||||
m = "replaceAll" or
|
||||
m = "slice" or
|
||||
m = "substr" or
|
||||
m = "substring" or
|
||||
|
||||
@@ -52,6 +52,6 @@ will not see the information:
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php/Information_Leak_(information_disclosure)">Information Leak</a>.</li>
|
||||
<li>OWASP: <a href="https://owasp.org/www-community/Improper_Error_Handling">Improper Error Handling</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -18,8 +18,8 @@ string cookieProperty() { result = "session" or result = "cookies" or result = "
|
||||
/** Gets a data flow node that flows to the base of an access to `cookies`, `session`, or `user`. */
|
||||
private DataFlow::SourceNode nodeLeadingToCookieAccess(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
exists(DataFlow::PropRead value |
|
||||
value = result.getAPropertyRead(cookieProperty()).getAPropertyRead() and
|
||||
exists(DataFlow::PropRef value |
|
||||
value = result.getAPropertyRead(cookieProperty()).getAPropertyReference() and
|
||||
// Ignore accesses to values that are part of a CSRF or captcha check
|
||||
not value.getPropertyName().regexpMatch("(?i).*(csrf|xsrf|captcha).*") and
|
||||
// Ignore calls like `req.session.save()`
|
||||
@@ -46,7 +46,12 @@ private DataFlow::SourceNode getARouteUsingCookies(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
isRouteHandlerUsingCookies(result)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = getARouteUsingCookies(t2).track(t2, t))
|
||||
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = getARouteUsingCookies(t2) |
|
||||
result = pred.track(t2, t)
|
||||
or
|
||||
t = t2 and
|
||||
HTTP::routeHandlerStep(pred, result)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a route handler that uses cookies. */
|
||||
|
||||
@@ -33,7 +33,7 @@ by <code>xpath</code>:
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php?title=Testing_for_XPath_Injection_(OTG-INPVAL-010)">Testing for XPath Injection</a>.</li>
|
||||
<li>OWASP: <a href="https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/09-Testing_for_XPath_Injection">Testing for XPath Injection</a>.</li>
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php/XPATH_Injection">XPath Injection</a>.</li>
|
||||
<li>npm: <a href="https://www.npmjs.com/package/xpath">xpath</a>.</li>
|
||||
</references>
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
- ide-contextual-queries/local-definitions
|
||||
- ide-contextual-queries/local-references
|
||||
- query: Comments/FCommentedOutCode.ql
|
||||
- query: Metrics/Dependencies/ExternalDependencies.ql
|
||||
- query: Metrics/Dependencies/ExternalDependenciesSourceLinks.ql
|
||||
- query: Metrics/FLinesOfCode.ql
|
||||
- query: Metrics/FLinesOfComment.ql
|
||||
- query: Metrics/FLinesOfDuplicatedCode.ql
|
||||
- query: Metrics/FLinesOfSimilarCode.ql
|
||||
- query: Metrics/FNumberOfTests.ql
|
||||
|
||||
@@ -31,7 +31,7 @@ Always verify the sender's identity of incoming messages.
|
||||
|
||||
<references>
|
||||
|
||||
<li><a href="https://cwe.mitre.org/data/definitions/20.html">CWE-20: Improper Input Validation</a></li>
|
||||
<li><a href="https://cwe.mitre.org/data/definitions/20.html">CWE-020: Improper Input Validation</a></li>
|
||||
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage">Window.postMessage()</a></li>
|
||||
<li><a href="https://portswigger.net/web-security/dom-based/web-message-manipulation">Web-message manipulation</a></li>
|
||||
<li><a href="https://labs.detectify.com/2016/12/08/the-pitfalls-of-postmessage/">The pitfalls of postMessage</a></li>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* @id js/missing-postmessageorigin-verification
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-20
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @deprecated
|
||||
* @name Duplicate function
|
||||
* @description There is another function that shares a lot of code with this function.
|
||||
* Extract the common parts to a shared utility function to improve maintainability.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @deprecated
|
||||
* @name Duplicate script
|
||||
* @description There is another script that shares a lot of code with this script. Consider combining the
|
||||
* two scripts to improve maintainability.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @deprecated
|
||||
* @name Similar function
|
||||
* @description There is another function that shares a lot of code with this function.
|
||||
* Extract the common parts to a shared utility function to improve maintainability.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @deprecated
|
||||
* @name Similar script
|
||||
* @description There is another script that shares a lot of code with this script.
|
||||
* Extract the common parts to a new script to improve maintainability..
|
||||
|
||||
15
javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql
Normal file
15
javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @name API-graph edges
|
||||
* @description The number of edges (other than points-to edges) in the API graph.
|
||||
* @kind metric
|
||||
* @metricType project
|
||||
* @metricAggregate sum
|
||||
* @tags meta
|
||||
* @id js/meta/api-graph-edges
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import meta.MetaMetrics
|
||||
|
||||
select projectRoot(),
|
||||
count(API::Node pred, string lbl, API::Node succ | succ = pred.getASuccessor(lbl))
|
||||
14
javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql
Normal file
14
javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name API-graph nodes
|
||||
* @description The number of nodes in the API graph.
|
||||
* @kind metric
|
||||
* @metricType project
|
||||
* @metricAggregate sum
|
||||
* @tags meta
|
||||
* @id js/meta/api-graph-nodes
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import meta.MetaMetrics
|
||||
|
||||
select projectRoot(), count(API::Node nd)
|
||||
14
javascript/ql/src/meta/ApiGraphs/ApiGraphPointsToEdges.ql
Normal file
14
javascript/ql/src/meta/ApiGraphs/ApiGraphPointsToEdges.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name API-graph points-to edges
|
||||
* @description The number of points-to edges in the API graph.
|
||||
* @kind metric
|
||||
* @metricType project
|
||||
* @metricAggregate sum
|
||||
* @tags meta
|
||||
* @id js/meta/api-graph-points-to-edges
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import meta.MetaMetrics
|
||||
|
||||
select projectRoot(), count(API::Node pred, API::Node succ | pred.refersTo(succ))
|
||||
15
javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql
Normal file
15
javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @name API-graph right-hand-side nodes
|
||||
* @description The number of data-flow nodes corresponding to a right-hand side of
|
||||
* a definition of an API-graph node.
|
||||
* @kind metric
|
||||
* @metricType project
|
||||
* @metricAggregate sum
|
||||
* @tags meta
|
||||
* @id js/meta/api-graph-rhs-nodes
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import meta.MetaMetrics
|
||||
|
||||
select projectRoot(), count(any(API::Node nd).getARhs())
|
||||
14
javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql
Normal file
14
javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name API-graph use nodes
|
||||
* @description The number of data-flow nodes corresponding to a use of an API-graph node.
|
||||
* @kind metric
|
||||
* @metricType project
|
||||
* @metricAggregate sum
|
||||
* @tags meta
|
||||
* @id js/meta/api-graph-use-nodes
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import meta.MetaMetrics
|
||||
|
||||
select projectRoot(), count(any(API::Node nd).getAUse())
|
||||
@@ -113,14 +113,14 @@ class AmdModuleDefinition extends CallExpr {
|
||||
/**
|
||||
* Gets the `i`th parameter of the factory function of this module.
|
||||
*/
|
||||
private SimpleParameter getFactoryParameter(int i) {
|
||||
private Parameter getFactoryParameter(int i) {
|
||||
getFactoryNodeInternal().asExpr().(Function).getParameter(i) = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter corresponding to the pseudo-dependency `require`.
|
||||
*/
|
||||
SimpleParameter getRequireParameter() {
|
||||
Parameter getRequireParameter() {
|
||||
result = getDependencyParameter("require")
|
||||
or
|
||||
// if no dependencies are listed, the first parameter is assumed to be `require`
|
||||
@@ -133,7 +133,7 @@ class AmdModuleDefinition extends CallExpr {
|
||||
/**
|
||||
* Gets the parameter corresponding to the pseudo-dependency `exports`.
|
||||
*/
|
||||
SimpleParameter getExportsParameter() {
|
||||
Parameter getExportsParameter() {
|
||||
result = getDependencyParameter("exports")
|
||||
or
|
||||
// if no dependencies are listed, the second parameter is assumed to be `exports`
|
||||
@@ -143,7 +143,7 @@ class AmdModuleDefinition extends CallExpr {
|
||||
/**
|
||||
* Gets the parameter corresponding to the pseudo-dependency `module`.
|
||||
*/
|
||||
SimpleParameter getModuleParameter() {
|
||||
Parameter getModuleParameter() {
|
||||
result = getDependencyParameter("module")
|
||||
or
|
||||
// if no dependencies are listed, the third parameter is assumed to be `module`
|
||||
|
||||
@@ -24,7 +24,10 @@ module API {
|
||||
* Gets a data-flow node corresponding to a use of the API component represented by this node.
|
||||
*
|
||||
* For example, `require('fs').readFileSync` is a use of the function `readFileSync` from the
|
||||
* `fs` module, and `require('fs').readFileSync(file)` is a use of the result of that function.
|
||||
* `fs` module, and `require('fs').readFileSync(file)` is a use of the return of that function.
|
||||
*
|
||||
* This includes indirect uses found via data flow, meaning that in
|
||||
* `f(obj.foo); function f(x) {};` both `obj.foo` and `x` are uses of the `foo` member from `obj`.
|
||||
*
|
||||
* As another example, in the assignment `exports.plusOne = (x) => x+1` the two references to
|
||||
* `x` are uses of the first parameter of `plusOne`.
|
||||
@@ -35,6 +38,34 @@ module API {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an immediate use of the API component represented by this node.
|
||||
*
|
||||
* For example, `require('fs').readFileSync` is a an immediate use of the `readFileSync` member
|
||||
* from the `fs` module.
|
||||
*
|
||||
* Unlike `getAUse()`, this predicate only gets the immediate references, not the indirect uses
|
||||
* found via data flow. This means that in `const x = fs.readFile` only `fs.readFile` is a reference
|
||||
* to the `readFile` member of `fs`, neither `x` nor any node that `x` flows to is a reference to
|
||||
* this API component.
|
||||
*/
|
||||
DataFlow::SourceNode getAnImmediateUse() { Impl::use(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a call to the function represented by this API component.
|
||||
*/
|
||||
DataFlow::CallNode getACall() { result = getReturn().getAnImmediateUse() }
|
||||
|
||||
/**
|
||||
* Gets a `new` call to the function represented by this API component.
|
||||
*/
|
||||
DataFlow::NewNode getAnInstantiation() { result = getInstance().getAnImmediateUse() }
|
||||
|
||||
/**
|
||||
* Gets an invocation (with our without `new`) to the function represented by this API component.
|
||||
*/
|
||||
DataFlow::InvokeNode getAnInvocation() { result = getACall() or result = getAnInstantiation() }
|
||||
|
||||
/**
|
||||
* Gets a data-flow node corresponding to the right-hand side of a definition of the API
|
||||
* component represented by this node.
|
||||
@@ -223,6 +254,9 @@ module API {
|
||||
) and
|
||||
length in [1 .. Impl::distanceFromRoot(this)]
|
||||
}
|
||||
|
||||
/** Gets the shortest distance from the root to this node in the API graph. */
|
||||
int getDepth() { result = Impl::distanceFromRoot(this) }
|
||||
}
|
||||
|
||||
/** The root node of an API graph. */
|
||||
@@ -267,6 +301,9 @@ module API {
|
||||
|
||||
/** Gets a data-flow node that defines this entry point. */
|
||||
abstract DataFlow::Node getARhs();
|
||||
|
||||
/** Gets an API-graph node for this entry point. */
|
||||
API::Node getNode() { result = root().getASuccessor(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -409,16 +446,9 @@ module API {
|
||||
rhs = f.getAReturn()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::SourceNode src, DataFlow::InvokeNode invk |
|
||||
use(base, src) and invk = trackUseNode(src).getAnInvocation()
|
||||
|
|
||||
exists(int i |
|
||||
lbl = Label::parameter(i) and
|
||||
rhs = invk.getArgument(i)
|
||||
)
|
||||
or
|
||||
lbl = Label::receiver() and
|
||||
rhs = invk.(DataFlow::CallNode).getReceiver()
|
||||
exists(int i |
|
||||
lbl = Label::parameter(i) and
|
||||
argumentPassing(base, i, rhs)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::SourceNode src, DataFlow::PropWrite pw |
|
||||
@@ -429,6 +459,30 @@ module API {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is passed as the `i`th argument to a use of `base`, either by means of a
|
||||
* full invocation, or in a partial function application.
|
||||
*
|
||||
* The receiver is considered to be argument -1.
|
||||
*/
|
||||
private predicate argumentPassing(TApiNode base, int i, DataFlow::Node arg) {
|
||||
exists(DataFlow::SourceNode use, DataFlow::SourceNode pred |
|
||||
use(base, use) and pred = trackUseNode(use)
|
||||
|
|
||||
arg = pred.getAnInvocation().getArgument(i)
|
||||
or
|
||||
arg = pred.getACall().getReceiver() and
|
||||
i = -1
|
||||
or
|
||||
exists(DataFlow::PartialInvokeNode pin, DataFlow::Node callback | pred.flowsTo(callback) |
|
||||
pin.isPartialArgument(callback, arg, i)
|
||||
or
|
||||
arg = pin.getBoundReceiver(callback) and
|
||||
i = -1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rhs` is the right-hand side of a definition of node `nd`.
|
||||
*/
|
||||
@@ -527,7 +581,11 @@ module API {
|
||||
ref = DataFlow::moduleImport(m)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ClassNode cls | nd = MkClassInstance(cls) | ref = cls.getAReceiverNode())
|
||||
exists(DataFlow::ClassNode cls | nd = MkClassInstance(cls) |
|
||||
ref = cls.getAReceiverNode()
|
||||
or
|
||||
ref = cls.(DataFlow::ClassNode::FunctionStyleClass).getAPrototypeReference()
|
||||
)
|
||||
or
|
||||
nd = MkUse(ref)
|
||||
or
|
||||
@@ -716,10 +774,14 @@ private module Label {
|
||||
bindingset[s]
|
||||
string parameterByStringIndex(string s) {
|
||||
result = "parameter " + s and
|
||||
s.toInt() >= 0
|
||||
s.toInt() >= -1
|
||||
}
|
||||
|
||||
/** Gets the `parameter` edge label for the `i`th parameter. */
|
||||
/**
|
||||
* Gets the `parameter` edge label for the `i`th parameter.
|
||||
*
|
||||
* The receiver is considered to be parameter -1.
|
||||
*/
|
||||
bindingset[i]
|
||||
string parameter(int i) { result = parameterByStringIndex(i.toString()) }
|
||||
|
||||
|
||||
@@ -24,13 +24,11 @@ module ArrayTaintTracking {
|
||||
predicate arrayFunctionTaintStep(DataFlow::Node pred, DataFlow::Node succ, DataFlow::CallNode call) {
|
||||
// `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
|
||||
// `elt` and `ary`; similar for `forEach`
|
||||
exists(string name, Function f, int i |
|
||||
(name = "map" or name = "forEach") and
|
||||
(i = 0 or i = 2) and
|
||||
exists(Function f |
|
||||
call.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = name and
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = ["map", "forEach"] and
|
||||
pred = call.getReceiver() and
|
||||
succ = DataFlow::parameterNode(f.getParameter(i))
|
||||
succ = DataFlow::parameterNode(f.getParameter([0, 2]))
|
||||
)
|
||||
or
|
||||
// `array.map` with tainted return value in callback
|
||||
@@ -47,41 +45,22 @@ module ArrayTaintTracking {
|
||||
succ = call
|
||||
or
|
||||
// `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name |
|
||||
name = "push" or
|
||||
name = "unshift"
|
||||
|
|
||||
pred = call.getAnArgument() and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
|
||||
)
|
||||
pred = call.getAnArgument() and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(["push", "unshift"]) = call
|
||||
or
|
||||
// `array.push(...e)`, `array.unshift(...e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name |
|
||||
name = "push" or
|
||||
name = "unshift"
|
||||
|
|
||||
pred = call.getASpreadArgument() and
|
||||
// Make sure we handle reflective calls
|
||||
succ = call.getReceiver().getALocalSource() and
|
||||
call.getCalleeName() = name
|
||||
)
|
||||
pred = call.getASpreadArgument() and
|
||||
// Make sure we handle reflective calls
|
||||
succ = call.getReceiver().getALocalSource() and
|
||||
call.getCalleeName() = ["push", "unshift"]
|
||||
or
|
||||
// `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name | name = "splice" |
|
||||
pred = call.getArgument(2) and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
|
||||
)
|
||||
pred = call.getArgument(2) and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall("splice") = call
|
||||
or
|
||||
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
|
||||
exists(string name |
|
||||
name = "pop" or
|
||||
name = "shift" or
|
||||
name = "slice" or
|
||||
name = "splice"
|
||||
|
|
||||
call.(DataFlow::MethodCallNode).calls(pred, name) and
|
||||
succ = call
|
||||
)
|
||||
call.(DataFlow::MethodCallNode).calls(pred, ["pop", "shift", "slice", "splice"]) and
|
||||
succ = call
|
||||
or
|
||||
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
|
||||
call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and
|
||||
|
||||
@@ -117,11 +117,11 @@ class ResolvedES2015PromiseDefinition extends ResolvedPromiseDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* An aggregated promise produced either by `Promise.all` or `Promise.race`.
|
||||
* An aggregated promise produced either by `Promise.all`, `Promise.race`, or `Promise.any`.
|
||||
*/
|
||||
class AggregateES2015PromiseDefinition extends PromiseCreationCall {
|
||||
AggregateES2015PromiseDefinition() {
|
||||
exists(string m | m = "all" or m = "race" |
|
||||
exists(string m | m = "all" or m = "race" or m = "any" |
|
||||
this = DataFlow::globalVarRef("Promise").getAMemberCall(m)
|
||||
)
|
||||
}
|
||||
@@ -440,6 +440,18 @@ predicate promiseTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred.getEnclosingExpr() = await.getOperand() and
|
||||
succ.getEnclosingExpr() = await
|
||||
)
|
||||
or
|
||||
exists(DataFlow::CallNode mapSeries |
|
||||
mapSeries = DataFlow::moduleMember("bluebird", "mapSeries").getACall()
|
||||
|
|
||||
// from `xs` to `x` in `require("bluebird").mapSeries(xs, (x) => {...})`.
|
||||
pred = mapSeries.getArgument(0) and
|
||||
succ = mapSeries.getABoundCallbackParameter(1, 0)
|
||||
or
|
||||
// from `y` to `require("bluebird").mapSeries(x, x => y)`.
|
||||
pred = mapSeries.getCallback(1).getAReturn() and
|
||||
succ = mapSeries
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -102,7 +102,7 @@ private class IteratorExceptionStep extends DataFlow::MethodCallNode, DataFlow::
|
||||
*/
|
||||
class StringReplaceCall extends DataFlow::MethodCallNode {
|
||||
StringReplaceCall() {
|
||||
getMethodName() = "replace" and
|
||||
getMethodName() = ["replace", "replaceAll"] and
|
||||
(getNumArgument() = 2 or getReceiver().mayHaveStringValue(_))
|
||||
}
|
||||
|
||||
@@ -128,9 +128,9 @@ class StringReplaceCall extends DataFlow::MethodCallNode {
|
||||
|
||||
/**
|
||||
* Holds if this is a global replacement, that is, the first argument is a regular expression
|
||||
* with the `g` flag.
|
||||
* with the `g` flag, or this is a call to `.replaceAll()`.
|
||||
*/
|
||||
predicate isGlobal() { getRegExp().isGlobal() }
|
||||
predicate isGlobal() { getRegExp().isGlobal() or getMethodName() = "replaceAll" }
|
||||
|
||||
/**
|
||||
* Holds if this call to `replace` replaces `old` with `new`.
|
||||
|
||||
@@ -427,6 +427,7 @@ module TaintTracking {
|
||||
name = "padStart" or
|
||||
name = "repeat" or
|
||||
name = "replace" or
|
||||
name = "replaceAll" or
|
||||
name = "slice" or
|
||||
name = "small" or
|
||||
name = "split" or
|
||||
@@ -452,7 +453,7 @@ module TaintTracking {
|
||||
exists(int i | pred.asExpr() = succ.getAstNode().(MethodCallExpr).getArgument(i) |
|
||||
name = "concat"
|
||||
or
|
||||
name = "replace" and i = 1
|
||||
name = ["replace", "replaceAll"] and i = 1
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -707,7 +708,8 @@ module TaintTracking {
|
||||
*/
|
||||
private ControlFlowNode getACaptureSetter(DataFlow::Node input) {
|
||||
exists(DataFlow::MethodCallNode call | result = call.asExpr() |
|
||||
call.getMethodName() = ["search", "replace", "match"] and input = call.getReceiver()
|
||||
call.getMethodName() = ["search", "replace", "replaceAll", "match"] and
|
||||
input = call.getReceiver()
|
||||
or
|
||||
call.getMethodName() = ["test", "exec"] and input = call.getArgument(0)
|
||||
)
|
||||
|
||||
@@ -819,27 +819,27 @@ class LinkFunction extends Function {
|
||||
/**
|
||||
* Gets the scope parameter of this function.
|
||||
*/
|
||||
SimpleParameter getScopeParameter() { result = getParameter(0) }
|
||||
Parameter getScopeParameter() { result = getParameter(0) }
|
||||
|
||||
/**
|
||||
* Gets the element parameter of this function (contains a jqLite-wrapped DOM element).
|
||||
*/
|
||||
SimpleParameter getElementParameter() { result = getParameter(1) }
|
||||
Parameter getElementParameter() { result = getParameter(1) }
|
||||
|
||||
/**
|
||||
* Gets the attributes parameter of this function.
|
||||
*/
|
||||
SimpleParameter getAttributesParameter() { result = getParameter(2) }
|
||||
Parameter getAttributesParameter() { result = getParameter(2) }
|
||||
|
||||
/**
|
||||
* Gets the controller parameter of this function.
|
||||
*/
|
||||
SimpleParameter getControllerParameter() { result = getParameter(3) }
|
||||
Parameter getControllerParameter() { result = getParameter(3) }
|
||||
|
||||
/**
|
||||
* Gets the transclude-function parameter of this function.
|
||||
*/
|
||||
SimpleParameter getTranscludeFnParameter() { result = getParameter(4) }
|
||||
Parameter getTranscludeFnParameter() { result = getParameter(4) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -868,7 +868,7 @@ class AngularScope extends TAngularScope {
|
||||
*/
|
||||
Expr getAnAccess() {
|
||||
exists(CustomDirective d | this = d.getAScope() |
|
||||
exists(SimpleParameter p |
|
||||
exists(Parameter p |
|
||||
p = d.getController().getDependencyParameter("$scope") or
|
||||
p = d.getALinkFunction().getParameter(0)
|
||||
|
|
||||
@@ -884,7 +884,7 @@ class AngularScope extends TAngularScope {
|
||||
d.hasIsolateScope() and result = d.getMember("scope").asExpr()
|
||||
)
|
||||
or
|
||||
exists(DirectiveController c, DOM::ElementDefinition elem, SimpleParameter p |
|
||||
exists(DirectiveController c, DOM::ElementDefinition elem, Parameter p |
|
||||
c.boundTo(elem) and
|
||||
this.mayApplyTo(elem) and
|
||||
p = c.getFactoryFunction().getDependencyParameter("$scope") and
|
||||
|
||||
@@ -41,7 +41,7 @@ abstract class DependencyInjection extends DataFlow::ValueNode {
|
||||
*/
|
||||
abstract class InjectableFunction extends DataFlow::ValueNode {
|
||||
/** Gets the parameter corresponding to dependency `name`. */
|
||||
abstract SimpleParameter getDependencyParameter(string name);
|
||||
abstract Parameter getDependencyParameter(string name);
|
||||
|
||||
/**
|
||||
* Gets the `i`th dependency declaration, which is also named `name`.
|
||||
@@ -67,7 +67,7 @@ abstract class InjectableFunction extends DataFlow::ValueNode {
|
||||
/**
|
||||
* Gets a service corresponding to the dependency-injected `parameter`.
|
||||
*/
|
||||
ServiceReference getAResolvedDependency(SimpleParameter parameter) {
|
||||
ServiceReference getAResolvedDependency(Parameter parameter) {
|
||||
exists(string name, InjectableFunctionServiceRequest request |
|
||||
this = request.getAnInjectedFunction() and
|
||||
parameter = getDependencyParameter(name) and
|
||||
@@ -79,7 +79,7 @@ abstract class InjectableFunction extends DataFlow::ValueNode {
|
||||
* Gets a Custom service corresponding to the dependency-injected `parameter`.
|
||||
* (this is a convenience variant of `getAResolvedDependency`)
|
||||
*/
|
||||
DataFlow::Node getCustomServiceDependency(SimpleParameter parameter) {
|
||||
DataFlow::Node getCustomServiceDependency(Parameter parameter) {
|
||||
exists(CustomServiceDefinition custom |
|
||||
custom.getServiceReference() = getAResolvedDependency(parameter) and
|
||||
result = custom.getAService()
|
||||
@@ -99,11 +99,11 @@ private class FunctionWithImplicitDependencyAnnotation extends InjectableFunctio
|
||||
not exists(getAPropertyDependencyInjection(astNode))
|
||||
}
|
||||
|
||||
override SimpleParameter getDependencyParameter(string name) {
|
||||
override Parameter getDependencyParameter(string name) {
|
||||
result = astNode.getParameterByName(name)
|
||||
}
|
||||
|
||||
override SimpleParameter getDependencyDeclaration(int i, string name) {
|
||||
override Parameter getDependencyDeclaration(int i, string name) {
|
||||
result.getName() = name and
|
||||
result = astNode.getParameter(i)
|
||||
}
|
||||
@@ -139,7 +139,7 @@ private class FunctionWithInjectProperty extends InjectableFunction {
|
||||
)
|
||||
}
|
||||
|
||||
override SimpleParameter getDependencyParameter(string name) {
|
||||
override Parameter getDependencyParameter(string name) {
|
||||
exists(int i | exists(getDependencyDeclaration(i, name)) | result = astNode.getParameter(i))
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ private class FunctionWithExplicitDependencyAnnotation extends InjectableFunctio
|
||||
function.flowsToExpr(astNode.getElement(astNode.getSize() - 1))
|
||||
}
|
||||
|
||||
override SimpleParameter getDependencyParameter(string name) {
|
||||
override Parameter getDependencyParameter(string name) {
|
||||
exists(int i | astNode.getElement(i).mayHaveStringValue(name) |
|
||||
result = asFunction().getParameter(i)
|
||||
)
|
||||
|
||||
@@ -479,7 +479,7 @@ abstract class ServiceRequest extends Expr {
|
||||
/**
|
||||
* Gets the parameter of this request into which `service` is injected.
|
||||
*/
|
||||
abstract SimpleParameter getDependencyParameter(ServiceReference service);
|
||||
abstract Parameter getDependencyParameter(ServiceReference service);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -488,7 +488,7 @@ abstract class ServiceRequest extends Expr {
|
||||
private class LinkFunctionWithScopeInjection extends ServiceRequest {
|
||||
LinkFunctionWithScopeInjection() { this instanceof LinkFunction }
|
||||
|
||||
override SimpleParameter getDependencyParameter(ServiceReference service) {
|
||||
override Parameter getDependencyParameter(ServiceReference service) {
|
||||
service instanceof ScopeServiceReference and
|
||||
result = this.(LinkFunction).getScopeParameter()
|
||||
}
|
||||
@@ -521,7 +521,7 @@ class InjectableFunctionServiceRequest extends ServiceRequest {
|
||||
result.isInjectable()
|
||||
}
|
||||
|
||||
override SimpleParameter getDependencyParameter(ServiceReference service) {
|
||||
override Parameter getDependencyParameter(ServiceReference service) {
|
||||
service = injectedFunction.getAResolvedDependency(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,6 +327,85 @@ module ClientRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes for modelling the url request library `needle`.
|
||||
*/
|
||||
private module Needle {
|
||||
/**
|
||||
* A model of a URL request made using `require("needle")(...)`.
|
||||
*/
|
||||
class PromisedNeedleRequest extends ClientRequest::Range {
|
||||
DataFlow::Node url;
|
||||
|
||||
PromisedNeedleRequest() { this = DataFlow::moduleImport("needle").getACall() }
|
||||
|
||||
override DataFlow::Node getUrl() { result = getArgument(1) }
|
||||
|
||||
override DataFlow::Node getHost() { none() }
|
||||
|
||||
override DataFlow::Node getADataNode() {
|
||||
result = getOptionArgument([2, 3], "headers")
|
||||
or
|
||||
result = getArgument(2)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResponseDataNode(string responseType, boolean promise) {
|
||||
responseType = "fetch.response" and
|
||||
promise = true and
|
||||
result = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a URL request made using `require("needle")[method](...)`.
|
||||
* E.g. `needle.get("http://example.org", (err, resp, body) => {})`.
|
||||
*
|
||||
* As opposed to the calls modeled in `PromisedNeedleRequest` these calls do not return promises.
|
||||
* Instead they take an optional callback as their last argument.
|
||||
*/
|
||||
class NeedleMethodRequest extends ClientRequest::Range {
|
||||
boolean hasData;
|
||||
|
||||
NeedleMethodRequest() {
|
||||
exists(string method |
|
||||
method = ["get", "head"] and hasData = false
|
||||
or
|
||||
method = ["post", "put", "patch", "delete"] and hasData = true
|
||||
or
|
||||
method = "request" and hasData = [true, false]
|
||||
|
|
||||
this = DataFlow::moduleMember("needle", method).getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrl() { result = getArgument(0) }
|
||||
|
||||
override DataFlow::Node getHost() { none() }
|
||||
|
||||
override DataFlow::Node getADataNode() {
|
||||
hasData = true and
|
||||
(
|
||||
result = getArgument(1)
|
||||
or
|
||||
result = getOptionArgument(2, "headers")
|
||||
)
|
||||
or
|
||||
hasData = false and
|
||||
result = getOptionArgument(1, "headers")
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResponseDataNode(string responseType, boolean promise) {
|
||||
promise = false and
|
||||
result = this.getABoundCallbackParameter(this.getNumArgument() - 1, 1) and
|
||||
responseType = "fetch.response"
|
||||
or
|
||||
promise = false and
|
||||
result = this.getABoundCallbackParameter(this.getNumArgument() - 1, 2) and
|
||||
responseType = "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a URL request made using the `got` library.
|
||||
*/
|
||||
@@ -389,6 +468,53 @@ module ClientRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instantiation `socket` of `require("net").Socket` type tracked using `t`.
|
||||
*/
|
||||
private DataFlow::SourceNode netSocketInstantiation(
|
||||
DataFlow::TypeTracker t, DataFlow::NewNode socket
|
||||
) {
|
||||
t.start() and
|
||||
socket = DataFlow::moduleMember("net", "Socket").getAnInstantiation() and
|
||||
result = socket
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = netSocketInstantiation(t2, socket).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a request made using `(new require("net").Socket()).connect(args);`.
|
||||
*/
|
||||
class NetSocketRequest extends ClientRequest::Range {
|
||||
DataFlow::NewNode socket;
|
||||
|
||||
NetSocketRequest() {
|
||||
this = netSocketInstantiation(DataFlow::TypeTracker::end(), socket).getAMethodCall("connect")
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrl() {
|
||||
result = getArgument([0, 1]) // there are multiple overrides of `connect`, and the URL can be in the first or second argument.
|
||||
}
|
||||
|
||||
override DataFlow::Node getHost() { result = getOptionArgument(0, "host") }
|
||||
|
||||
override DataFlow::Node getAResponseDataNode(string responseType, boolean promise) {
|
||||
responseType = "text" and
|
||||
promise = false and
|
||||
exists(DataFlow::CallNode call |
|
||||
call = netSocketInstantiation(DataFlow::TypeTracker::end(), socket).getAMemberCall("on") and
|
||||
call.getArgument(0).mayHaveStringValue("data") and
|
||||
result = call.getABoundCallbackParameter(1, 0)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getADataNode() {
|
||||
exists(DataFlow::CallNode call |
|
||||
call = netSocketInstantiation(DataFlow::TypeTracker::end(), socket).getAMemberCall("write") and
|
||||
result = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a URL request made using the `superagent` library.
|
||||
*/
|
||||
|
||||
@@ -30,17 +30,17 @@ module Connect {
|
||||
*
|
||||
* `kind` is one of: "error", "request", "response", "next".
|
||||
*/
|
||||
abstract SimpleParameter getRouteHandlerParameter(string kind);
|
||||
abstract Parameter getRouteHandlerParameter(string kind);
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the request object.
|
||||
*/
|
||||
SimpleParameter getRequestParameter() { result = getRouteHandlerParameter("request") }
|
||||
Parameter getRequestParameter() { result = getRouteHandlerParameter("request") }
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the response object.
|
||||
*/
|
||||
SimpleParameter getResponseParameter() { result = getRouteHandlerParameter("response") }
|
||||
Parameter getResponseParameter() { result = getRouteHandlerParameter("response") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,7 +51,7 @@ module Connect {
|
||||
|
||||
StandardRouteHandler() { this = any(RouteSetup setup).getARouteHandler() }
|
||||
|
||||
override SimpleParameter getRouteHandlerParameter(string kind) {
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
result = getRouteHandlerParameter(astNode, kind)
|
||||
}
|
||||
}
|
||||
@@ -180,7 +180,7 @@ module Connect {
|
||||
HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
TrackedRouteHandlerCandidateWithSetup() { this = any(RouteSetup s).getARouteHandler() }
|
||||
|
||||
override SimpleParameter getRouteHandlerParameter(string kind) {
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
result = getRouteHandlerParameter(astNode, kind)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,9 +125,7 @@ module Express {
|
||||
exists(DataFlow::TypeBackTracker t2, DataFlow::SourceNode succ | succ = getARouteHandler(t2) |
|
||||
result = succ.backtrack(t2, t)
|
||||
or
|
||||
exists(HTTP::RouteHandlerCandidateContainer container |
|
||||
result = container.getRouteHandler(succ)
|
||||
) and
|
||||
HTTP::routeHandlerStep(result, succ) and
|
||||
t = t2
|
||||
)
|
||||
}
|
||||
@@ -197,7 +195,7 @@ module Express {
|
||||
|
||||
PassportRouteHandler() { this = any(PassportRouteSetup setup).getARouteHandler() }
|
||||
|
||||
override SimpleParameter getRouteHandlerParameter(string kind) {
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
kind = "request" and
|
||||
result = astNode.getParameter(0)
|
||||
}
|
||||
@@ -331,17 +329,17 @@ module Express {
|
||||
*
|
||||
* `kind` is one of: "error", "request", "response", "next", or "parameter".
|
||||
*/
|
||||
abstract SimpleParameter getRouteHandlerParameter(string kind);
|
||||
abstract Parameter getRouteHandlerParameter(string kind);
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the request object.
|
||||
*/
|
||||
SimpleParameter getRequestParameter() { result = getRouteHandlerParameter("request") }
|
||||
Parameter getRequestParameter() { result = getRouteHandlerParameter("request") }
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the response object.
|
||||
*/
|
||||
SimpleParameter getResponseParameter() { result = getRouteHandlerParameter("response") }
|
||||
Parameter getResponseParameter() { result = getRouteHandlerParameter("response") }
|
||||
|
||||
/**
|
||||
* Gets a request body access of this handler.
|
||||
@@ -359,7 +357,7 @@ module Express {
|
||||
|
||||
StandardRouteHandler() { this = routeSetup.getARouteHandler() }
|
||||
|
||||
override SimpleParameter getRouteHandlerParameter(string kind) {
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
if routeSetup.isParameterHandler()
|
||||
then result = getRouteParameterHandlerParameter(astNode, kind)
|
||||
else result = getRouteHandlerParameter(astNode, kind)
|
||||
@@ -507,6 +505,10 @@ module Express {
|
||||
// `req.cookies`
|
||||
kind = "cookie" and
|
||||
this = request.getAPropertyRead("cookies")
|
||||
or
|
||||
// `req.files`, treated the same as `req.body`.
|
||||
kind = "body" and
|
||||
this = request.getAPropertyRead("files")
|
||||
)
|
||||
or
|
||||
kind = "body" and
|
||||
@@ -888,7 +890,7 @@ module Express {
|
||||
|
||||
TrackedRouteHandlerCandidateWithSetup() { this = routeSetup.getARouteHandler() }
|
||||
|
||||
override SimpleParameter getRouteHandlerParameter(string kind) {
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
if routeSetup.isParameterHandler()
|
||||
then result = getRouteParameterHandlerParameter(astNode, kind)
|
||||
else result = getRouteHandlerParameter(astNode, kind)
|
||||
|
||||
@@ -223,7 +223,7 @@ module Firebase {
|
||||
|
||||
RouteHandler() { this = any(RouteSetup setup).getARouteHandler() }
|
||||
|
||||
override SimpleParameter getRouteHandlerParameter(string kind) {
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
kind = "request" and result = astNode.getParameter(0)
|
||||
or
|
||||
kind = "response" and result = astNode.getParameter(1)
|
||||
|
||||
@@ -233,6 +233,74 @@ module HTTP {
|
||||
ResponseExpr getAResponseExpr() { result.getRouteHandler() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` decorates the function `pred`.
|
||||
* This means that `call` returns a function that forwards its arguments to `pred`.
|
||||
* Only holds when the decorator looks like it is decorating a route-handler.
|
||||
*
|
||||
* Below is a code example relating `call`, `decoratee`, `outer`, `inner`.
|
||||
* ```
|
||||
* function outer(method) {
|
||||
* return function inner(req, res) {
|
||||
* return method.call(this, req, res);
|
||||
* };
|
||||
* }
|
||||
* var route = outer(function decoratee(req, res) { // <- call
|
||||
* res.end("foo");
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
private predicate isDecoratedCall(DataFlow::CallNode call, DataFlow::FunctionNode decoratee) {
|
||||
// indirect route-handler `result` is given to function `outer`, which returns function `inner` which calls the function `pred`.
|
||||
exists(int i, DataFlow::FunctionNode outer, HTTP::RouteHandlerCandidate inner |
|
||||
inner = outer.getAReturn().getALocalSource() and
|
||||
decoratee = call.getArgument(i).getALocalSource() and
|
||||
outer.getFunction() = call.getACallee() and
|
||||
hasForwardingHandlerParameter(i, outer, inner)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th parameter of `outer` has a call that `inner` forwards its parameters to.
|
||||
*/
|
||||
private predicate hasForwardingHandlerParameter(
|
||||
int i, DataFlow::FunctionNode outer, HTTP::RouteHandlerCandidate inner
|
||||
) {
|
||||
isAForwardingRouteHandlerCall(outer.getParameter(i), inner)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` looks like a route-handler and a call to `callee` inside `f` forwards all of the parameters from `f` to that call.
|
||||
*/
|
||||
private predicate isAForwardingRouteHandlerCall(
|
||||
DataFlow::SourceNode callee, HTTP::RouteHandlerCandidate f
|
||||
) {
|
||||
exists(DataFlow::CallNode call | call = callee.getACall() |
|
||||
forall(int arg | arg = [0 .. f.getNumParameter() - 1] |
|
||||
f.getParameter(arg).flowsTo(call.getArgument(arg))
|
||||
) and
|
||||
call.getContainer() = f.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a step from `pred` to `succ` for a RouteHandler - beyond the usual steps defined by TypeTracking.
|
||||
*/
|
||||
predicate routeHandlerStep(DataFlow::SourceNode pred, DataFlow::SourceNode succ) {
|
||||
isDecoratedCall(succ, pred)
|
||||
or
|
||||
// A forwarding call
|
||||
isAForwardingRouteHandlerCall(pred, succ)
|
||||
or
|
||||
// a container containing route-handlers.
|
||||
exists(HTTP::RouteHandlerCandidateContainer container | pred = container.getRouteHandler(succ))
|
||||
or
|
||||
// (function (req, res) {}).bind(this);
|
||||
exists(DataFlow::PartialInvokeNode call |
|
||||
succ = call.getBoundFunction(any(DataFlow::Node n | pred.flowsTo(n)), 0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that sets up a route on a server.
|
||||
*/
|
||||
@@ -402,6 +470,23 @@ module HTTP {
|
||||
*/
|
||||
abstract Expr getServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter containing data received by a NodeJS HTTP server.
|
||||
* E.g. `chunk` in: `http.createServer().on('request', (req, res) => req.on("data", (chunk) => ...))`.
|
||||
*/
|
||||
private class ServerRequestDataEvent extends RemoteFlowSource, DataFlow::ParameterNode {
|
||||
RequestSource req;
|
||||
|
||||
ServerRequestDataEvent() {
|
||||
exists(DataFlow::MethodCallNode mcn | mcn = req.ref().getAMethodCall(EventEmitter::on()) |
|
||||
mcn.getArgument(0).mayHaveStringValue("data") and
|
||||
this = mcn.getABoundCallbackParameter(1, 0)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "NodeJS HTTP server data event" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -555,7 +640,9 @@ module HTTP {
|
||||
create.getArgument(0).asExpr() instanceof NullLiteral
|
||||
)
|
||||
) and
|
||||
exists(RouteHandlerCandidate candidate | candidate.flowsTo(getAPropertyWrite().getRhs()))
|
||||
exists(RouteHandlerCandidate candidate |
|
||||
getAPossiblyDecoratedHandler(candidate).flowsTo(getAPropertyWrite().getRhs())
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getRouteHandler(DataFlow::SourceNode access) {
|
||||
@@ -563,7 +650,7 @@ module HTTP {
|
||||
exists(DataFlow::PropWrite write, DataFlow::PropRead read |
|
||||
access = read and
|
||||
ref(this).getAPropertyRead() = read and
|
||||
result.flowsTo(write.getRhs()) and
|
||||
getAPossiblyDecoratedHandler(result).flowsTo(write.getRhs()) and
|
||||
write = this.getAPropertyWrite()
|
||||
|
|
||||
write.getPropertyName() = read.getPropertyName()
|
||||
@@ -571,10 +658,33 @@ module HTTP {
|
||||
exists(EnumeratedPropName prop | access = prop.getASourceProp())
|
||||
or
|
||||
read = DataFlow::lvalueNode(any(ForOfStmt stmt).getLValue())
|
||||
or
|
||||
// for forwarding calls to an element where the key is determined by the request.
|
||||
getRequestParameterRead().flowsToExpr(read.getPropertyNameExpr())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a (chained) property-read/method-call on the request parameter of the route-handler `f`.
|
||||
*/
|
||||
private DataFlow::SourceNode getRequestParameterRead() {
|
||||
result = any(RouteHandlerCandidate f).getParameter(0)
|
||||
or
|
||||
result = getRequestParameterRead().getAPropertyRead()
|
||||
or
|
||||
result = getRequestParameterRead().getAMethodCall()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that is either `candidate`, or a call that decorates `candidate`.
|
||||
*/
|
||||
DataFlow::SourceNode getAPossiblyDecoratedHandler(RouteHandlerCandidate candidate) {
|
||||
result = candidate
|
||||
or
|
||||
isDecoratedCall(result, candidate)
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection that contains one or more route potential handlers.
|
||||
*/
|
||||
@@ -592,13 +702,14 @@ module HTTP {
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getRouteHandler(DataFlow::SourceNode access) {
|
||||
result instanceof RouteHandlerCandidate and
|
||||
exists(
|
||||
DataFlow::Node input, TypeTrackingPseudoProperty key, CollectionFlowStep store,
|
||||
CollectionFlowStep load, DataFlow::Node storeTo, DataFlow::Node loadFrom
|
||||
|
|
||||
this.flowsTo(storeTo) and
|
||||
store.store(input, storeTo, key) and
|
||||
result.(RouteHandlerCandidate).flowsTo(input) and
|
||||
getAPossiblyDecoratedHandler(result).flowsTo(input) and
|
||||
ref(this).flowsTo(loadFrom) and
|
||||
load.load(loadFrom, access, key)
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ module Hapi {
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the request object.
|
||||
*/
|
||||
SimpleParameter getRequestParameter() { result = function.getParameter(0) }
|
||||
Parameter getRequestParameter() { result = function.getParameter(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,7 +47,7 @@ module Koa {
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the context object.
|
||||
*/
|
||||
SimpleParameter getContextParameter() { result = function.getParameter(0) }
|
||||
Parameter getContextParameter() { result = function.getParameter(0) }
|
||||
|
||||
/**
|
||||
* Gets an expression that contains the "context" object of
|
||||
|
||||
@@ -394,6 +394,52 @@ module LodashUnderscore {
|
||||
succ = this.getExceptionalReturn()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a taint-step involving a (non-function) underscore method from `pred` to `succ`.
|
||||
*/
|
||||
private predicate underscoreTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(string name, DataFlow::CallNode call |
|
||||
call = any(Member member | member.getName() = name).getACall()
|
||||
|
|
||||
name =
|
||||
["find", "filter", "findWhere", "where", "reject", "pluck", "max", "min", "sortBy",
|
||||
"shuffle", "sample", "toArray", "partition", "compact", "first", "initial", "last",
|
||||
"rest", "flatten", "without", "difference", "uniq", "unique", "unzip", "transpose",
|
||||
"object", "chunk", "values", "mapObject", "pick", "omit", "defaults", "clone", "tap",
|
||||
"identity"] and
|
||||
pred = call.getArgument(0) and
|
||||
succ = call
|
||||
or
|
||||
name = ["union", "zip"] and
|
||||
pred = call.getAnArgument() and
|
||||
succ = call
|
||||
or
|
||||
name =
|
||||
["each", "map", "every", "some", "max", "min", "sortBy", "partition", "mapObject", "tap"] and
|
||||
pred = call.getArgument(0) and
|
||||
succ = call.getABoundCallbackParameter(1, 0)
|
||||
or
|
||||
name = ["reduce", "reduceRight"] and
|
||||
pred = call.getArgument(0) and
|
||||
succ = call.getABoundCallbackParameter(1, 1)
|
||||
or
|
||||
name = ["map", "reduce", "reduceRight"] and
|
||||
pred = call.getCallback(1).getAReturn() and
|
||||
succ = call
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A model for taint-steps involving (non-function) underscore methods.
|
||||
*/
|
||||
private class UnderscoreTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
UnderscoreTaintStep() { underscoreTaintStep(this, _) }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
underscoreTaintStep(pred, succ) and pred = this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,94 +13,39 @@ module NoSQL {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a `$where` property of an object that flows to `n`.
|
||||
* Gets a value that has been assigned to the "$where" property of an object that flows to `queryArg`.
|
||||
*/
|
||||
private DataFlow::Node getADollarWherePropertyValue(DataFlow::Node n) {
|
||||
result = n.getALocalSource().getAPropertyWrite("$where").getRhs()
|
||||
private DataFlow::Node getADollarWhereProperty(API::Node queryArg) {
|
||||
result = queryArg.getMember("$where").getARhs()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the MongoDB library.
|
||||
*/
|
||||
private module MongoDB {
|
||||
/**
|
||||
* Gets an import of MongoDB.
|
||||
*/
|
||||
DataFlow::ModuleImportNode mongodb() { result.getPath() = "mongodb" }
|
||||
|
||||
/**
|
||||
* Gets an access to `mongodb.MongoClient`.
|
||||
*/
|
||||
private DataFlow::SourceNode getAMongoClient(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
(
|
||||
result = mongodb().getAPropertyRead("MongoClient")
|
||||
or
|
||||
exists(DataFlow::ParameterNode p |
|
||||
p = result and
|
||||
p = getAMongoDbCallback().getParameter(1) and
|
||||
not p.getName().toLowerCase() = "db" // mongodb v2 provides a `Db` here
|
||||
)
|
||||
)
|
||||
private API::Node getAMongoClient() {
|
||||
result = API::moduleImport("mongodb").getMember("MongoClient")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = getAMongoClient(t2).track(t2, t))
|
||||
result = getAMongoDbCallback().getParameter(1) and
|
||||
not result.getAnImmediateUse().(DataFlow::ParameterNode).getName() = "db" // mongodb v2 provides a `Db` here
|
||||
}
|
||||
|
||||
/** Gets an API-graph node that refers to a `connect` callback. */
|
||||
private API::Node getAMongoDbCallback() {
|
||||
result = getAMongoClient().getMember("connect").getLastParameter()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to `mongodb.MongoClient`.
|
||||
* Gets an API-graph node that may refer to a MongoDB database connection.
|
||||
*/
|
||||
DataFlow::SourceNode getAMongoClient() { result = getAMongoClient(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a data flow node that leads to a `connect` callback. */
|
||||
private DataFlow::SourceNode getAMongoDbCallback(DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = getAMongoClient().getAMemberCall("connect").getLastArgument().getALocalSource()
|
||||
private API::Node getAMongoDb() {
|
||||
result = getAMongoClient().getMember("db").getReturn()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getAMongoDbCallback(t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node that leads to a `connect` callback. */
|
||||
private DataFlow::FunctionNode getAMongoDbCallback() {
|
||||
result = getAMongoDbCallback(DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that may refer to a MongoDB database connection.
|
||||
*/
|
||||
private DataFlow::SourceNode getAMongoDb(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
(
|
||||
exists(DataFlow::ParameterNode p |
|
||||
p = result and
|
||||
p = getAMongoDbCallback().getParameter(1) and
|
||||
not p.getName().toLowerCase() = "client" // mongodb v3 provides a `Mongoclient` here
|
||||
)
|
||||
or
|
||||
result = getAMongoClient().getAMethodCall("db")
|
||||
)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = getAMongoDb(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that may refer to a MongoDB database connection.
|
||||
*/
|
||||
DataFlow::SourceNode getAMongoDb() { result = getAMongoDb(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* A data flow node that may hold a MongoDB collection.
|
||||
*/
|
||||
abstract class Collection extends DataFlow::SourceNode { }
|
||||
|
||||
/**
|
||||
* A collection resulting from calling `Db.collection(...)`.
|
||||
*/
|
||||
private class CollectionFromDb extends Collection {
|
||||
CollectionFromDb() {
|
||||
this = getAMongoDb().getAMethodCall("collection")
|
||||
or
|
||||
this = getAMongoDb().getAMethodCall("collection").getCallback(1).getParameter(0)
|
||||
}
|
||||
result = getAMongoDbCallback().getParameter(1) and
|
||||
not result.getAnImmediateUse().(DataFlow::ParameterNode).getName() = "client" // mongodb v3 provides a `Mongoclient` here
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,32 +54,55 @@ private module MongoDB {
|
||||
* Note that this also covers `mongoose` models since they are subtypes
|
||||
* of `mongodb.Collection`.
|
||||
*/
|
||||
private class CollectionFromType extends Collection {
|
||||
CollectionFromType() { hasUnderlyingType("mongodb", "Collection") }
|
||||
private class TypedMongoCollection extends API::EntryPoint {
|
||||
TypedMongoCollection() { this = "TypedMongoCollection" }
|
||||
|
||||
override DataFlow::SourceNode getAUse() { result.hasUnderlyingType("mongodb", "Collection") }
|
||||
|
||||
override DataFlow::Node getARhs() { none() }
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a MongoDB collection. */
|
||||
private DataFlow::SourceNode getACollection(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof Collection
|
||||
private API::Node getACollection() {
|
||||
// A collection resulting from calling `Db.collection(...)`.
|
||||
exists(API::Node collection | collection = getAMongoDb().getMember("collection").getReturn() |
|
||||
result = collection
|
||||
or
|
||||
result = collection.getParameter(1).getParameter(0)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = getACollection(t2).track(t2, t))
|
||||
result = any(TypedMongoCollection c).getNode()
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a MongoDB collection. */
|
||||
DataFlow::SourceNode getACollection() { result = getACollection(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** A call to a MongoDB query method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::CallNode {
|
||||
int queryArgIdx;
|
||||
API::Node callee;
|
||||
|
||||
QueryCall() {
|
||||
exists(string m | this = getACollection().getAMethodCall(m) |
|
||||
CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
|
||||
)
|
||||
exists(string method |
|
||||
CollectionMethodSignatures::interpretsArgumentAsQuery(method, queryArgIdx) and
|
||||
callee = getACollection().getMember(method)
|
||||
) and
|
||||
this = callee.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(queryArgIdx) }
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(callee.getParameter(queryArgIdx))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a MongoDB query.
|
||||
*/
|
||||
class Query extends NoSQL::Query {
|
||||
QueryCall qc;
|
||||
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,17 +162,6 @@ private module MongoDB {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a MongoDB query.
|
||||
*/
|
||||
class Query extends NoSQL::Query {
|
||||
Query() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWherePropertyValue(this.flow())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,81 +171,84 @@ private module Mongoose {
|
||||
/**
|
||||
* Gets an import of Mongoose.
|
||||
*/
|
||||
DataFlow::ModuleImportNode getAMongooseInstance() { result.getPath() = "mongoose" }
|
||||
API::Node getAMongooseInstance() { result = API::moduleImport("mongoose") }
|
||||
|
||||
/**
|
||||
* Gets a call to `mongoose.createConnection`.
|
||||
* Gets a reference to `mongoose.createConnection`.
|
||||
*/
|
||||
DataFlow::CallNode createConnection() {
|
||||
result = getAMongooseInstance().getAMemberCall("createConnection")
|
||||
}
|
||||
API::Node createConnection() { result = getAMongooseInstance().getMember("createConnection") }
|
||||
|
||||
/**
|
||||
* A Mongoose function invocation.
|
||||
* A Mongoose function.
|
||||
*/
|
||||
private class InvokeNode extends DataFlow::InvokeNode {
|
||||
private class MongooseFunction extends API::Node {
|
||||
/**
|
||||
* Holds if this invocation returns an object of type `Query`.
|
||||
* Gets the API-graph node for the result from this function (if the function returns a `Query`).
|
||||
*/
|
||||
abstract predicate returnsQuery();
|
||||
abstract API::Node getQueryReturn();
|
||||
|
||||
/**
|
||||
* Holds if this invocation returns a `Query` that evaluates to one or
|
||||
* Holds if this function returns a `Query` that evaluates to one or
|
||||
* more Documents (`asArray` is false if it evaluates to a single
|
||||
* Document).
|
||||
*/
|
||||
abstract predicate returnsDocumentQuery(boolean asArray);
|
||||
|
||||
/**
|
||||
* Holds if this invocation interprets `arg` as a query.
|
||||
* Gets an argument that this function interprets as a query.
|
||||
*/
|
||||
abstract predicate interpretsArgumentAsQuery(DataFlow::Node arg);
|
||||
abstract API::Node getQueryArgument();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the Mongoose Model class
|
||||
*/
|
||||
module Model {
|
||||
private class ModelInvokeNode extends InvokeNode, DataFlow::MethodCallNode {
|
||||
ModelInvokeNode() { this = ref().getAMethodCall() }
|
||||
private class ModelFunction extends MongooseFunction {
|
||||
string methodName;
|
||||
|
||||
override predicate returnsQuery() { MethodSignatures::returnsQuery(getMethodName()) }
|
||||
ModelFunction() { this = getModelObject().getMember(methodName) }
|
||||
|
||||
override API::Node getQueryReturn() {
|
||||
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
|
||||
}
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) {
|
||||
MethodSignatures::returnsDocumentQuery(getMethodName(), asArray)
|
||||
MethodSignatures::returnsDocumentQuery(methodName, asArray)
|
||||
}
|
||||
|
||||
override predicate interpretsArgumentAsQuery(DataFlow::Node arg) {
|
||||
override API::Node getQueryArgument() {
|
||||
exists(int n |
|
||||
MethodSignatures::interpretsArgumentAsQuery(this.getMethodName(), n) and
|
||||
arg = this.getArgument(n)
|
||||
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
|
||||
result = this.getParameter(n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose Model object.
|
||||
* A Mongoose collection based on the type `mongoose.Model`.
|
||||
*/
|
||||
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
|
||||
(
|
||||
result = getAMongooseInstance().getAMemberCall("model")
|
||||
or
|
||||
exists(DataFlow::SourceNode conn | conn = createConnection() |
|
||||
result = conn.getAMemberCall("model") or
|
||||
result = conn.getAPropertyRead("models").getAPropertyRead()
|
||||
)
|
||||
or
|
||||
result.hasUnderlyingType("mongoose", "Model")
|
||||
) and
|
||||
t.start()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
|
||||
private class TypedMongooseModel extends API::EntryPoint {
|
||||
TypedMongooseModel() { this = "TypedMongooseModel" }
|
||||
|
||||
override DataFlow::SourceNode getAUse() { result.hasUnderlyingType("mongoose", "Model") }
|
||||
|
||||
override DataFlow::Node getARhs() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose model object.
|
||||
* Gets a API-graph node referring to a Mongoose Model object.
|
||||
*/
|
||||
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
|
||||
private API::Node getModelObject() {
|
||||
result = getAMongooseInstance().getMember("model").getReturn()
|
||||
or
|
||||
exists(API::Node conn | conn = createConnection().getReturn() |
|
||||
result = conn.getMember("model").getReturn() or
|
||||
result = conn.getMember("models").getAMember()
|
||||
)
|
||||
or
|
||||
result = any(TypedMongooseModel c).getNode()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides signatures for the Model methods.
|
||||
@@ -350,58 +310,62 @@ private module Mongoose {
|
||||
* Provides classes modeling the Mongoose Query class
|
||||
*/
|
||||
module Query {
|
||||
private class QueryInvokeNode extends InvokeNode, DataFlow::MethodCallNode {
|
||||
QueryInvokeNode() { this = ref().getAMethodCall() }
|
||||
private class QueryFunction extends MongooseFunction {
|
||||
string methodName;
|
||||
|
||||
override predicate returnsQuery() { MethodSignatures::returnsQuery(getMethodName()) }
|
||||
QueryFunction() { this = getAMongooseQuery().getMember(methodName) }
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) {
|
||||
MethodSignatures::returnsDocumentQuery(getMethodName(), asArray)
|
||||
override API::Node getQueryReturn() {
|
||||
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
|
||||
}
|
||||
|
||||
override predicate interpretsArgumentAsQuery(DataFlow::Node arg) {
|
||||
override predicate returnsDocumentQuery(boolean asArray) {
|
||||
MethodSignatures::returnsDocumentQuery(methodName, asArray)
|
||||
}
|
||||
|
||||
override API::Node getQueryArgument() {
|
||||
exists(int n |
|
||||
MethodSignatures::interpretsArgumentAsQuery(this.getMethodName(), n) and
|
||||
arg = this.getArgument(n)
|
||||
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
|
||||
result = this.getParameter(n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class NewQueryInvokeNode extends InvokeNode {
|
||||
NewQueryInvokeNode() {
|
||||
this = getAMongooseInstance().getAPropertyRead("Query").getAnInstantiation()
|
||||
}
|
||||
private class NewQueryFunction extends MongooseFunction {
|
||||
NewQueryFunction() { this = getAMongooseInstance().getMember("Query") }
|
||||
|
||||
override predicate returnsQuery() { any() }
|
||||
override API::Node getQueryReturn() { result = this.getInstance() }
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) { none() }
|
||||
|
||||
override predicate interpretsArgumentAsQuery(DataFlow::Node arg) { arg = this.getArgument(2) }
|
||||
override API::Node getQueryArgument() { result = this.getParameter(2) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Mongoose query.
|
||||
*/
|
||||
private class TypedMongooseQuery extends API::EntryPoint {
|
||||
TypedMongooseQuery() { this = "TypedMongooseQuery" }
|
||||
|
||||
override DataFlow::SourceNode getAUse() { result.hasUnderlyingType("mongoose", "Query") }
|
||||
|
||||
override DataFlow::Node getARhs() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose query object.
|
||||
*/
|
||||
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
|
||||
(
|
||||
result.(InvokeNode).returnsQuery() or
|
||||
result.hasUnderlyingType("mongoose", "Query")
|
||||
) and
|
||||
t.start()
|
||||
API::Node getAMongooseQuery() {
|
||||
result = any(MongooseFunction f).getQueryReturn()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode succ | succ = ref(t2) |
|
||||
result = succ.track(t2, t)
|
||||
or
|
||||
result = succ.getAMethodCall(any(string name | MethodSignatures::returnsQuery(name))) and
|
||||
t = t2.continue()
|
||||
)
|
||||
result = any(TypedMongooseQuery c).getNode()
|
||||
or
|
||||
result =
|
||||
getAMongooseQuery()
|
||||
.getMember(any(string name | MethodSignatures::returnsQuery(name)))
|
||||
.getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose query object.
|
||||
*/
|
||||
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* Provides signatures for the Query methods.
|
||||
*/
|
||||
@@ -541,19 +505,23 @@ private module Mongoose {
|
||||
* Provides classes modeling the Mongoose Document class
|
||||
*/
|
||||
module Document {
|
||||
private class DocumentInvokeNode extends InvokeNode, DataFlow::MethodCallNode {
|
||||
DocumentInvokeNode() { this = ref().getAMethodCall() }
|
||||
private class DocumentFunction extends MongooseFunction {
|
||||
string methodName;
|
||||
|
||||
override predicate returnsQuery() { MethodSignatures::returnsQuery(getMethodName()) }
|
||||
DocumentFunction() { this = getAMongooseDocument().getMember(methodName) }
|
||||
|
||||
override predicate returnsDocumentQuery(boolean asArray) {
|
||||
MethodSignatures::returnsDocumentQuery(getMethodName(), asArray)
|
||||
override API::Node getQueryReturn() {
|
||||
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
|
||||
}
|
||||
|
||||
override predicate interpretsArgumentAsQuery(DataFlow::Node arg) {
|
||||
override predicate returnsDocumentQuery(boolean asArray) {
|
||||
MethodSignatures::returnsDocumentQuery(methodName, asArray)
|
||||
}
|
||||
|
||||
override API::Node getQueryArgument() {
|
||||
exists(int n |
|
||||
MethodSignatures::interpretsArgumentAsQuery(this.getMethodName(), n) and
|
||||
arg = this.getArgument(n)
|
||||
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
|
||||
result = this.getParameter(n)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -561,67 +529,59 @@ private module Mongoose {
|
||||
/**
|
||||
* A Mongoose Document that is retrieved from the backing database.
|
||||
*/
|
||||
class RetrievedDocument extends DataFlow::SourceNode {
|
||||
class RetrievedDocument extends API::Node {
|
||||
RetrievedDocument() {
|
||||
exists(boolean asArray, DataFlow::ParameterNode param |
|
||||
exists(InvokeNode call |
|
||||
call.returnsDocumentQuery(asArray) and
|
||||
param = call.getCallback(call.getNumArgument() - 1).getParameter(1)
|
||||
exists(boolean asArray, API::Node param |
|
||||
exists(MongooseFunction func |
|
||||
func.returnsDocumentQuery(asArray) and
|
||||
param = func.getLastParameter().getParameter(1)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
DataFlow::SourceNode base, DataFlow::MethodCallNode call, string executor,
|
||||
int paramIndex
|
||||
|
|
||||
executor = "then" and paramIndex = 0
|
||||
exists(API::Node f |
|
||||
f = Query::getAMongooseQuery().getMember("then") and
|
||||
param = f.getParameter(0).getParameter(0)
|
||||
or
|
||||
executor = "exec" and paramIndex = 1
|
||||
f = Query::getAMongooseQuery().getMember("exec") and
|
||||
param = f.getParameter(0).getParameter(1)
|
||||
|
|
||||
base = Query::ref() and
|
||||
call = base.getAMethodCall(executor) and
|
||||
param = call.getCallback(0).getParameter(paramIndex) and
|
||||
exists(DataFlow::MethodCallNode pred |
|
||||
// limitation: look at the previous method call
|
||||
// limitation: look at the previous method call
|
||||
Query::MethodSignatures::returnsDocumentQuery(pred.getMethodName(), asArray) and
|
||||
pred.getAMethodCall() = call
|
||||
pred.getAMethodCall() = f.getACall()
|
||||
)
|
||||
)
|
||||
|
|
||||
asArray = false and this = param
|
||||
or
|
||||
asArray = true and
|
||||
exists(DataFlow::PropRead access |
|
||||
// limitation: look for direct accesses
|
||||
access = param.getAPropertyRead() and
|
||||
not exists(access.getPropertyName()) and
|
||||
this = access
|
||||
)
|
||||
// limitation: look for direct accesses
|
||||
this = param.getUnknownMember()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose Document object.
|
||||
* A Mongoose document.
|
||||
*/
|
||||
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
|
||||
(
|
||||
result instanceof RetrievedDocument or
|
||||
result.hasUnderlyingType("mongoose", "Document")
|
||||
) and
|
||||
t.start()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode succ | succ = ref(t2) |
|
||||
result = succ.track(t2, t)
|
||||
or
|
||||
result = succ.getAMethodCall(any(string name | MethodSignatures::returnsDocument(name))) and
|
||||
t = t2.continue()
|
||||
)
|
||||
private class TypedMongooseDocument extends API::EntryPoint {
|
||||
TypedMongooseDocument() { this = "TypedMongooseDocument" }
|
||||
|
||||
override DataFlow::SourceNode getAUse() { result.hasUnderlyingType("mongoose", "Document") }
|
||||
|
||||
override DataFlow::Node getARhs() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose Document object.
|
||||
*/
|
||||
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
|
||||
private API::Node getAMongooseDocument() {
|
||||
result instanceof RetrievedDocument or
|
||||
result = any(TypedMongooseDocument c).getNode() or
|
||||
result =
|
||||
getAMongooseDocument()
|
||||
.getMember(any(string name | MethodSignatures::returnsDocument(name)))
|
||||
.getReturn()
|
||||
}
|
||||
|
||||
private module MethodSignatures {
|
||||
/**
|
||||
@@ -679,7 +639,9 @@ private module Mongoose {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(string prop | this = createConnection().getOptionArgument(3, prop).asExpr() |
|
||||
exists(string prop |
|
||||
this = createConnection().getParameter(3).getMember(prop).getARhs().asExpr()
|
||||
|
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
prop = "pass" and kind = "password"
|
||||
@@ -693,38 +655,38 @@ private module Mongoose {
|
||||
* An expression that is interpreted as a (part of a) MongoDB query.
|
||||
*/
|
||||
class MongoDBQueryPart extends NoSQL::Query {
|
||||
MongoDBQueryPart() { any(InvokeNode call).interpretsArgumentAsQuery(this.flow()) }
|
||||
MongooseFunction f;
|
||||
|
||||
MongoDBQueryPart() { this = f.getQueryArgument().getARhs().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWherePropertyValue(this.flow())
|
||||
result = getADollarWhereProperty(f.getQueryArgument())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An evaluation of a MongoDB query.
|
||||
*/
|
||||
class ShorthandQueryEvaluation extends DatabaseAccess {
|
||||
InvokeNode invk;
|
||||
class ShorthandQueryEvaluation extends DatabaseAccess, DataFlow::InvokeNode {
|
||||
MongooseFunction f;
|
||||
|
||||
ShorthandQueryEvaluation() {
|
||||
this = invk and
|
||||
this = f.getACall() and
|
||||
// shorthand for execution: provide a callback
|
||||
invk.returnsQuery() and
|
||||
exists(invk.getCallback(invk.getNumArgument() - 1))
|
||||
exists(f.getQueryReturn()) and
|
||||
exists(this.getCallback(this.getNumArgument() - 1))
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
// NB: the complete information is not easily accessible for deeply chained calls
|
||||
invk.interpretsArgumentAsQuery(result)
|
||||
f.getQueryArgument().getARhs() = result
|
||||
}
|
||||
}
|
||||
|
||||
class ExplicitQueryEvaluation extends DatabaseAccess {
|
||||
ExplicitQueryEvaluation() {
|
||||
// explicit execution using a Query method call
|
||||
exists(string executor | executor = "exec" or executor = "then" or executor = "catch" |
|
||||
Query::ref().getAMethodCall(executor) = this
|
||||
)
|
||||
Query::getAMongooseQuery().getMember(["exec", "then", "catch"]).getACall() = this
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
@@ -738,26 +700,6 @@ private module Mongoose {
|
||||
* Provides classes modeling the Minimongo library.
|
||||
*/
|
||||
private module Minimongo {
|
||||
/**
|
||||
* Gets an expression that may refer to a Minimongo database.
|
||||
*/
|
||||
private DataFlow::SourceNode getADb(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
// new (require('minimongo')[DBKINDNAME])()
|
||||
result = DataFlow::moduleImport("minimongo").getAPropertyRead().getAnInvocation()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = getADb(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a Minimongo collection. */
|
||||
private DataFlow::SourceNode getACollection(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
// db[COLLECTIONNAME]
|
||||
result = getADb(DataFlow::TypeTracker::end()).getAPropertyRead()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = getACollection(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides signatures for the Collection methods.
|
||||
*/
|
||||
@@ -774,25 +716,32 @@ private module Minimongo {
|
||||
/** A call to a Minimongo query method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
int queryArgIdx;
|
||||
API::Node callee;
|
||||
|
||||
QueryCall() {
|
||||
exists(string m | this = getACollection(DataFlow::TypeTracker::end()).getAMethodCall(m) |
|
||||
exists(string m |
|
||||
callee = API::moduleImport("minimongo").getAMember().getReturn().getAMember().getMember(m) and
|
||||
this = callee.getACall() and
|
||||
CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(queryArgIdx) }
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(callee.getParameter(queryArgIdx))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a Minimongo query.
|
||||
*/
|
||||
class Query extends NoSQL::Query {
|
||||
Query() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
|
||||
QueryCall qc;
|
||||
|
||||
override DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWherePropertyValue(this.flow())
|
||||
}
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -800,49 +749,35 @@ private module Minimongo {
|
||||
* Provides classes modeling the MarsDB library.
|
||||
*/
|
||||
private module MarsDB {
|
||||
/**
|
||||
* Gets an expression that may refer to a MarsDB database.
|
||||
*/
|
||||
private DataFlow::SourceNode getADb(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
// Collection = require('marsdb')
|
||||
result = DataFlow::moduleImport("marsdb")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = getADb(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to a MarsDB collection. */
|
||||
private DataFlow::SourceNode getACollection(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
// new Collection(...)
|
||||
result =
|
||||
getADb(DataFlow::TypeTracker::end()).getAPropertyRead("Collection").getAnInstantiation()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = getACollection(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** A call to a MarsDB query method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
int queryArgIdx;
|
||||
API::Node callee;
|
||||
|
||||
QueryCall() {
|
||||
exists(string m | this = getACollection(DataFlow::TypeTracker::end()).getAMethodCall(m) |
|
||||
exists(string m |
|
||||
callee = API::moduleImport("marsdb").getMember("Collection").getInstance().getMember(m) and
|
||||
this = callee.getACall() and
|
||||
// implements parts of the Minimongo interface
|
||||
Minimongo::CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(queryArgIdx) }
|
||||
|
||||
DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWhereProperty(callee.getParameter(queryArgIdx))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is interpreted as a MarsDB query.
|
||||
*/
|
||||
class Query extends NoSQL::Query {
|
||||
Query() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
|
||||
QueryCall qc;
|
||||
|
||||
override DataFlow::Node getACodeOperator() {
|
||||
result = getADollarWherePropertyValue(this.flow())
|
||||
}
|
||||
Query() { this = qc.getAQueryArgument().asExpr() }
|
||||
|
||||
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,12 +91,12 @@ module NodeJSLib {
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the request object.
|
||||
*/
|
||||
SimpleParameter getRequestParameter() { result = getFunction().getParameter(0) }
|
||||
Parameter getRequestParameter() { result = getFunction().getParameter(0) }
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the response object.
|
||||
*/
|
||||
SimpleParameter getResponseParameter() { result = getFunction().getParameter(1) }
|
||||
Parameter getResponseParameter() { result = getFunction().getParameter(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,7 +228,12 @@ module NodeJSLib {
|
||||
t.start() and
|
||||
result = handler.flow().getALocalSource()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getARouteHandler(t2).backtrack(t2, t))
|
||||
exists(DataFlow::TypeBackTracker t2, DataFlow::SourceNode succ | succ = getARouteHandler(t2) |
|
||||
result = succ.backtrack(t2, t)
|
||||
or
|
||||
t = t2 and
|
||||
HTTP::routeHandlerStep(result, succ)
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getServer() { result = server }
|
||||
@@ -735,16 +740,8 @@ module NodeJSLib {
|
||||
astNode.getParameter(0).getName() = request and
|
||||
astNode.getParameter(1).getName() = response
|
||||
|
|
||||
not (
|
||||
// heuristic: not a class method (Node.js invokes this with a function call)
|
||||
astNode = any(MethodDefinition def).getBody()
|
||||
or
|
||||
// heuristic: does not return anything (Node.js will not use the return value)
|
||||
exists(astNode.getAReturnStmt().getExpr())
|
||||
or
|
||||
// heuristic: is not invoked (Node.js invokes this at a call site we cannot reason precisely about)
|
||||
exists(DataFlow::InvokeNode cs | cs.getACallee() = astNode)
|
||||
)
|
||||
// heuristic: not a class method (Node.js invokes this with a function call)
|
||||
not astNode = any(MethodDefinition def).getBody()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1082,8 +1079,10 @@ module NodeJSLib {
|
||||
/**
|
||||
* An instance of net.createServer(), which creates a new TCP/IPC server.
|
||||
*/
|
||||
private class NodeJSNetServer extends DataFlow::SourceNode {
|
||||
NodeJSNetServer() { this = DataFlow::moduleMember("net", "createServer").getAnInvocation() }
|
||||
class NodeJSNetServer extends DataFlow::InvokeNode {
|
||||
NodeJSNetServer() {
|
||||
this = DataFlow::moduleMember(["net", "tls"], "createServer").getAnInvocation()
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
|
||||
t.start() and result = this
|
||||
@@ -1110,6 +1109,8 @@ module NodeJSLib {
|
||||
|
|
||||
this = call.getCallback(1).getParameter(0)
|
||||
)
|
||||
or
|
||||
this = server.getCallback([0, 1]).getParameter(0)
|
||||
}
|
||||
|
||||
DataFlow::SourceNode ref() { result = EventEmitter::trackEventEmitter(this) }
|
||||
|
||||
@@ -30,12 +30,12 @@ module Restify {
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the request object.
|
||||
*/
|
||||
SimpleParameter getRequestParameter() { result = function.getParameter(0) }
|
||||
Parameter getRequestParameter() { result = function.getParameter(0) }
|
||||
|
||||
/**
|
||||
* Gets the parameter of the route handler that contains the response object.
|
||||
*/
|
||||
SimpleParameter getResponseParameter() { result = function.getParameter(1) }
|
||||
Parameter getResponseParameter() { result = function.getParameter(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,24 +31,27 @@ private module MySql {
|
||||
/** Gets the package name `mysql` or `mysql2`. */
|
||||
API::Node mysql() { result = API::moduleImport(["mysql", "mysql2"]) }
|
||||
|
||||
/** Gets a call to `mysql.createConnection`. */
|
||||
API::Node createConnection() { result = mysql().getMember("createConnection").getReturn() }
|
||||
/** Gets a reference to `mysql.createConnection`. */
|
||||
API::Node createConnection() { result = mysql().getMember("createConnection") }
|
||||
|
||||
/** Gets a call to `mysql.createPool`. */
|
||||
API::Node createPool() { result = mysql().getMember("createPool").getReturn() }
|
||||
/** Gets a reference to `mysql.createPool`. */
|
||||
API::Node createPool() { result = mysql().getMember("createPool") }
|
||||
|
||||
/** Gets a node that contains a MySQL pool created using `mysql.createPool()`. */
|
||||
API::Node pool() { result = createPool().getReturn() }
|
||||
|
||||
/** Gets a data flow node that contains a freshly created MySQL connection instance. */
|
||||
API::Node connection() {
|
||||
result = createConnection()
|
||||
result = createConnection().getReturn()
|
||||
or
|
||||
result = createPool().getMember("getConnection").getParameter(0).getParameter(1)
|
||||
result = pool().getMember("getConnection").getParameter(0).getParameter(1)
|
||||
}
|
||||
|
||||
/** A call to the MySql `query` method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() {
|
||||
exists(API::Node recv | recv = createPool() or recv = connection() |
|
||||
this = recv.getMember("query").getReturn().getAUse()
|
||||
exists(API::Node recv | recv = pool() or recv = connection() |
|
||||
this = recv.getMember("query").getACall()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -63,12 +66,7 @@ private module MySql {
|
||||
/** A call to the `escape` or `escapeId` method that performs SQL sanitization. */
|
||||
class EscapingSanitizer extends SQL::SqlSanitizer, MethodCallExpr {
|
||||
EscapingSanitizer() {
|
||||
this =
|
||||
[mysql(), createPool(), connection()]
|
||||
.getMember(["escape", "escapeId"])
|
||||
.getReturn()
|
||||
.getAUse()
|
||||
.asExpr() and
|
||||
this = [mysql(), pool(), connection()].getMember(["escape", "escapeId"]).getACall().asExpr() and
|
||||
input = this.getArgument(0) and
|
||||
output = this
|
||||
}
|
||||
@@ -79,9 +77,9 @@ private module MySql {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(API::Node call, string prop |
|
||||
call in [createConnection(), createPool()] and
|
||||
call.getAUse().asExpr().(CallExpr).hasOptionArgument(0, prop, this) and
|
||||
exists(API::Node callee, string prop |
|
||||
callee in [createConnection(), createPool()] and
|
||||
this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and
|
||||
(
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
@@ -98,29 +96,32 @@ private module MySql {
|
||||
* Provides classes modelling the `pg` package.
|
||||
*/
|
||||
private module Postgres {
|
||||
/** Gets an expression of the form `new require('pg').Client()`. */
|
||||
API::Node newClient() { result = API::moduleImport("pg").getMember("Client").getInstance() }
|
||||
/** Gets a reference to the `Client` constructor in the `pg` package, for example `require('pg').Client`. */
|
||||
API::Node newClient() { result = API::moduleImport("pg").getMember("Client") }
|
||||
|
||||
/** Gets a data flow node that holds a freshly created Postgres client instance. */
|
||||
/** Gets a freshly created Postgres client instance. */
|
||||
API::Node client() {
|
||||
result = newClient()
|
||||
result = newClient().getInstance()
|
||||
or
|
||||
// pool.connect(function(err, client) { ... })
|
||||
result = newPool().getMember("connect").getParameter(0).getParameter(1)
|
||||
result = pool().getMember("connect").getParameter(0).getParameter(1)
|
||||
}
|
||||
|
||||
/** Gets a constructor that when invoked constructs a new connection pool. */
|
||||
API::Node newPool() {
|
||||
// new require('pg').Pool()
|
||||
result = API::moduleImport("pg").getMember("Pool")
|
||||
or
|
||||
// new require('pg-pool')
|
||||
result = API::moduleImport("pg-pool")
|
||||
}
|
||||
|
||||
/** Gets an expression that constructs a new connection pool. */
|
||||
API::Node newPool() {
|
||||
// new require('pg').Pool()
|
||||
result = API::moduleImport("pg").getMember("Pool").getInstance()
|
||||
or
|
||||
// new require('pg-pool')
|
||||
result = API::moduleImport("pg-pool").getInstance()
|
||||
}
|
||||
API::Node pool() { result = newPool().getInstance() }
|
||||
|
||||
/** A call to the Postgres `query` method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = [client(), newPool()].getMember("query").getReturn().getAUse() }
|
||||
QueryCall() { this = [client(), pool()].getMember("query").getACall() }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
|
||||
}
|
||||
@@ -135,9 +136,8 @@ private module Postgres {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(DataFlow::InvokeNode call, string prop |
|
||||
call = [client(), newPool()].getAUse() and
|
||||
this = call.getOptionArgument(0, prop).asExpr() and
|
||||
exists(string prop |
|
||||
this = [newClient(), newPool()].getParameter(0).getMember(prop).getARhs().asExpr() and
|
||||
(
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
@@ -178,7 +178,7 @@ private module Sqlite {
|
||||
meth = "prepare" or
|
||||
meth = "run"
|
||||
|
|
||||
this = newDb().getMember(meth).getReturn().getAUse()
|
||||
this = newDb().getMember(meth).getACall()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ private module MsSql {
|
||||
|
||||
/** A call to a MsSql query method. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = request().getMember(["query", "batch"]).getReturn().getAUse() }
|
||||
QueryCall() { this = request().getMember(["query", "batch"]).getACall() }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
|
||||
}
|
||||
@@ -250,13 +250,13 @@ private module MsSql {
|
||||
string kind;
|
||||
|
||||
Credentials() {
|
||||
exists(DataFlow::InvokeNode call, string prop |
|
||||
exists(API::Node callee, string prop |
|
||||
(
|
||||
call = mssql().getMember("connect").getReturn().getAUse()
|
||||
callee = mssql().getMember("connect")
|
||||
or
|
||||
call = mssql().getMember("ConnectionPool").getInstance().getAUse()
|
||||
callee = mssql().getMember("ConnectionPool")
|
||||
) and
|
||||
this = call.getOptionArgument(0, prop).asExpr() and
|
||||
this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and
|
||||
(
|
||||
prop = "user" and kind = "user name"
|
||||
or
|
||||
@@ -281,7 +281,7 @@ private module Sequelize {
|
||||
|
||||
/** A call to `Sequelize.query`. */
|
||||
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
|
||||
QueryCall() { this = newSequelize().getMember("query").getReturn().getAUse() }
|
||||
QueryCall() { this = newSequelize().getMember("query").getACall() }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
|
||||
}
|
||||
@@ -300,7 +300,7 @@ private module Sequelize {
|
||||
|
||||
Credentials() {
|
||||
exists(NewExpr ne, string prop |
|
||||
ne = newSequelize().getAUse().asExpr() and
|
||||
ne = sequelize().getAnInstantiation().asExpr() and
|
||||
(
|
||||
this = ne.getArgument(1) and prop = "username"
|
||||
or
|
||||
@@ -378,8 +378,7 @@ private module Spanner {
|
||||
*/
|
||||
class DatabaseRunCall extends SqlExecution {
|
||||
DatabaseRunCall() {
|
||||
this =
|
||||
database().getMember(["run", "runPartitionedUpdate", "runStream"]).getReturn().getAUse()
|
||||
this = database().getMember(["run", "runPartitionedUpdate", "runStream"]).getACall()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,7 +387,7 @@ private module Spanner {
|
||||
*/
|
||||
class TransactionRunCall extends SqlExecution {
|
||||
TransactionRunCall() {
|
||||
this = transaction().getMember(["run", "runStream", "runUpdate"]).getReturn().getAUse()
|
||||
this = transaction().getMember(["run", "runStream", "runUpdate"]).getACall()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,8 +396,7 @@ private module Spanner {
|
||||
*/
|
||||
class ExecuteSqlCall extends SqlExecution {
|
||||
ExecuteSqlCall() {
|
||||
this =
|
||||
v1SpannerClient().getMember(["executeSql", "executeStreamingSql"]).getReturn().getAUse()
|
||||
this = v1SpannerClient().getMember(["executeSql", "executeStreamingSql"]).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
|
||||
@@ -29,7 +29,7 @@ private class PromotedExpressCandidate extends Express::RouteHandler,
|
||||
HTTP::Servers::StandardRouteHandler {
|
||||
PromotedExpressCandidate() { this instanceof ConnectExpressShared::RouteHandlerCandidate }
|
||||
|
||||
override SimpleParameter getRouteHandlerParameter(string kind) {
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
result = ConnectExpressShared::getRouteHandlerParameter(getAstNode(), kind)
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ private class PromotedConnectCandidate extends Connect::RouteHandler,
|
||||
HTTP::Servers::StandardRouteHandler {
|
||||
PromotedConnectCandidate() { this instanceof ConnectExpressShared::RouteHandlerCandidate }
|
||||
|
||||
override SimpleParameter getRouteHandlerParameter(string kind) {
|
||||
override Parameter getRouteHandlerParameter(string kind) {
|
||||
result = ConnectExpressShared::getRouteHandlerParameter(getAstNode(), kind)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,7 @@ abstract class HeuristicAdditionalTaintStep extends DataFlow::ValueNode { }
|
||||
* A call to `tainted.replace(x, y)` that preserves taint.
|
||||
*/
|
||||
private class HeuristicStringManipulationTaintStep extends HeuristicAdditionalTaintStep,
|
||||
TaintTracking::AdditionalTaintStep, DataFlow::MethodCallNode {
|
||||
HeuristicStringManipulationTaintStep() { getMethodName() = "replace" }
|
||||
|
||||
TaintTracking::AdditionalTaintStep, StringReplaceCall {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred = getReceiver() and succ = this
|
||||
}
|
||||
|
||||
@@ -34,15 +34,11 @@ module CleartextLogging {
|
||||
/**
|
||||
* A call to `.replace()` that seems to mask sensitive information.
|
||||
*/
|
||||
class MaskingReplacer extends Barrier, DataFlow::MethodCallNode {
|
||||
class MaskingReplacer extends Barrier, StringReplaceCall {
|
||||
MaskingReplacer() {
|
||||
this.getCalleeName() = "replace" and
|
||||
exists(RegExpLiteral reg |
|
||||
reg = this.getArgument(0).getALocalSource().asExpr() and
|
||||
reg.isGlobal() and
|
||||
any(RegExpDot term).getLiteral() = reg
|
||||
) and
|
||||
exists(this.getArgument(1).getStringValue())
|
||||
this.isGlobal() and
|
||||
exists(this.getRawReplacement().getStringValue()) and
|
||||
any(RegExpDot term).getLiteral() = getRegExp().asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ module ClientSideUrlRedirect {
|
||||
or
|
||||
exists(MethodCallExpr mce |
|
||||
queryAccess.asExpr() = mce and
|
||||
mce = any(RegExpLiteral re).flow().(DataFlow::SourceNode).getAMethodCall("exec").asExpr() and
|
||||
mce = any(DataFlow::RegExpCreationNode re).getAMethodCall("exec").asExpr() and
|
||||
nd.asExpr() = mce.getArgument(0)
|
||||
)
|
||||
}
|
||||
@@ -132,6 +132,15 @@ module ClientSideUrlRedirect {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to `importScripts(..)` - which is used inside `WebWorker`s to import new scripts - viewed as a `ScriptUrlSink`.
|
||||
*/
|
||||
class ImportScriptsSink extends ScriptUrlSink {
|
||||
ImportScriptsSink() {
|
||||
this = DataFlow::globalVarRef("importScripts").getACall().getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A script or iframe `src` attribute, viewed as a `ScriptUrlSink`.
|
||||
*/
|
||||
|
||||
@@ -199,6 +199,11 @@ class PostMessageEventHandler extends Function {
|
||||
addEventListener.getArgument(0).mayHaveStringValue("message") and
|
||||
addEventListener.getArgument(1).getABoundFunctionValue(paramIndex).getFunction() = this
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node rhs |
|
||||
rhs = DataFlow::globalObjectRef().getAPropertyWrite("onmessage").getRhs() and
|
||||
rhs.getABoundFunctionValue(paramIndex).getFunction() = this
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@ private DataFlow::Node commandArgument(SystemCommandExecution sys, DataFlow::Typ
|
||||
t.start() and
|
||||
result = sys.getACommandArgument()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | t = t2.smallstep(result, commandArgument(sys, t2)))
|
||||
exists(DataFlow::TypeBackTracker t2 | t2 = t.smallstep(result, commandArgument(sys, t2)))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +63,26 @@ private DataFlow::SourceNode argumentList(SystemCommandExecution sys) {
|
||||
result = argumentList(sys, DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node whose value ends up being interpreted as an element of the argument list
|
||||
* `args` after a flow summarised by `t`.
|
||||
*/
|
||||
private DataFlow::Node argumentListElement(DataFlow::SourceNode args, DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
args = argumentList(_) and
|
||||
result = args.getAPropertyWrite().getRhs()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | t2 = t.smallstep(result, argumentListElement(args, t2)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node whose value ends up being interpreted as an element of the argument list
|
||||
* `args`.
|
||||
*/
|
||||
private DataFlow::Node argumentListElement(DataFlow::SourceNode args) {
|
||||
result = argumentListElement(args, DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `source` contributes to the arguments of an indirect command execution `sys`.
|
||||
*
|
||||
@@ -86,12 +106,12 @@ predicate isIndirectCommandArgument(DataFlow::Node source, SystemCommandExecutio
|
||||
exists(DataFlow::ArrayCreationNode args, DataFlow::Node shell, string dashC |
|
||||
shellCmd(shell.asExpr(), dashC) and
|
||||
shell = commandArgument(sys) and
|
||||
args.getAPropertyWrite().getRhs().mayHaveStringValue(dashC) and
|
||||
args = argumentList(sys) and
|
||||
(
|
||||
source = argumentList(sys)
|
||||
or
|
||||
source = argumentList(sys).getAPropertyWrite().getRhs()
|
||||
argumentListElement(args).mayHaveStringValue(dashC) and
|
||||
exists(DataFlow::SourceNode argsSource | argsSource = argumentList(sys) |
|
||||
if exists(argsSource.getAPropertyWrite())
|
||||
then source = argsSource.getAPropertyWrite().getRhs()
|
||||
else source = argsSource
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -218,12 +218,12 @@ module TaintedPath {
|
||||
output = this
|
||||
or
|
||||
// non-global replace or replace of something other than /\.\./g, /[/]/g, or /[\.]/g.
|
||||
this.getCalleeName() = "replace" and
|
||||
this instanceof StringReplaceCall and
|
||||
input = getReceiver() and
|
||||
output = this and
|
||||
not exists(RegExpLiteral literal, RegExpTerm term |
|
||||
getArgument(0).getALocalSource().asExpr() = literal and
|
||||
literal.isGlobal() and
|
||||
this.(StringReplaceCall).getRegExp().asExpr() = literal and
|
||||
this.(StringReplaceCall).isGlobal() and
|
||||
literal.getRoot() = term
|
||||
|
|
||||
term.getAMatchedString() = "/" or
|
||||
@@ -247,16 +247,15 @@ module TaintedPath {
|
||||
/**
|
||||
* A call that removes all instances of "../" in the prefix of the string.
|
||||
*/
|
||||
class DotDotSlashPrefixRemovingReplace extends DataFlow::CallNode {
|
||||
class DotDotSlashPrefixRemovingReplace extends StringReplaceCall {
|
||||
DataFlow::Node input;
|
||||
DataFlow::Node output;
|
||||
|
||||
DotDotSlashPrefixRemovingReplace() {
|
||||
this.getCalleeName() = "replace" and
|
||||
input = getReceiver() and
|
||||
output = this and
|
||||
exists(RegExpLiteral literal, RegExpTerm term |
|
||||
getArgument(0).getALocalSource().asExpr() = literal and
|
||||
getRegExp().asExpr() = literal and
|
||||
(term instanceof RegExpStar or term instanceof RegExpPlus) and
|
||||
term.getChild(0) = getADotDotSlashMatcher()
|
||||
|
|
||||
@@ -298,17 +297,16 @@ module TaintedPath {
|
||||
/**
|
||||
* A call that removes all "." or ".." from a path, without also removing all forward slashes.
|
||||
*/
|
||||
class DotRemovingReplaceCall extends DataFlow::CallNode {
|
||||
class DotRemovingReplaceCall extends StringReplaceCall {
|
||||
DataFlow::Node input;
|
||||
DataFlow::Node output;
|
||||
|
||||
DotRemovingReplaceCall() {
|
||||
this.getCalleeName() = "replace" and
|
||||
input = getReceiver() and
|
||||
output = this and
|
||||
isGlobal() and
|
||||
exists(RegExpLiteral literal, RegExpTerm term |
|
||||
getArgument(0).getALocalSource().asExpr() = literal and
|
||||
literal.isGlobal() and
|
||||
getRegExp().asExpr() = literal and
|
||||
literal.getRoot() = term and
|
||||
not term.getAMatchedString() = "/"
|
||||
|
|
||||
|
||||
@@ -39,10 +39,9 @@ module UnsafeJQueryPlugin {
|
||||
StringConcatenation::taintStep(pred, succ, _, any(int i | i >= 1))
|
||||
or
|
||||
// prefixing through a poor-mans templating system:
|
||||
exists(DataFlow::MethodCallNode replace |
|
||||
exists(StringReplaceCall replace |
|
||||
replace = succ and
|
||||
pred = replace.getArgument(1) and
|
||||
replace.getMethodName() = "replace"
|
||||
pred = replace.getRawReplacement()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -62,10 +61,27 @@ module UnsafeJQueryPlugin {
|
||||
* With this taint-step we regain that `foo.bar` is tainted, because `PropertyPresenceSanitizer` could remove it.
|
||||
*/
|
||||
private predicate aliasPropertyPresenceStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
exists(PropertyPresenceSanitizer sanitizer, DataFlow::PropRead read | read = src |
|
||||
read = sanitizer.getPropRead() and
|
||||
sink = AccessPath::getAnAliasedSourceNode(read) and
|
||||
read.getBasicBlock().(ReachableBasicBlock).strictlyDominates(sink.getBasicBlock())
|
||||
exists(ReachableBasicBlock srcBB, ReachableBasicBlock sinkBB |
|
||||
aliasPropertyPresenceStepHelper(src, sink, srcBB, sinkBB) and
|
||||
srcBB.strictlyDominates(sinkBB)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a taint-step from `src` to `sink`, and `srcBB` is the basicblock for `src` and `sinkBB` is the basicblock for `sink`.
|
||||
*
|
||||
* This predicate is outlined to get a better join-order.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate aliasPropertyPresenceStepHelper(
|
||||
DataFlow::PropRead src, DataFlow::Node sink, ReachableBasicBlock srcBB,
|
||||
ReachableBasicBlock sinkBB
|
||||
) {
|
||||
exists(PropertyPresenceSanitizer sanitizer |
|
||||
src = sanitizer.getPropRead() and
|
||||
sink = AccessPath::getAnAliasedSourceNode(src) and
|
||||
srcBB = src.getBasicBlock() and
|
||||
sinkBB = sink.getBasicBlock()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,15 +195,25 @@ module UnsafeJQueryPlugin {
|
||||
*/
|
||||
predicate isLikelyIntentionalHtmlSink(DataFlow::Node sink) {
|
||||
exists(
|
||||
JQuery::JQueryPluginMethod plugin, DataFlow::PropWrite defaultDef, string default,
|
||||
JQuery::JQueryPluginMethod plugin, DataFlow::PropWrite defaultDef,
|
||||
DataFlow::PropRead finalRead
|
||||
|
|
||||
hasDefaultOption(plugin, defaultDef) and
|
||||
defaultDef.getPropertyName() = finalRead.getPropertyName() and
|
||||
defaultDef.getRhs().mayHaveStringValue(default) and
|
||||
default.regexpMatch("\\s*<.*") and
|
||||
defaultDef = getALikelyHTMLWrite(finalRead.getPropertyName()) and
|
||||
finalRead.flowsTo(sink) and
|
||||
sink.getTopLevel() = plugin.getTopLevel()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property-write that writes a HTML-like constant string to `prop`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private DataFlow::PropWrite getALikelyHTMLWrite(string prop) {
|
||||
exists(string default |
|
||||
result.getRhs().mayHaveStringValue(default) and
|
||||
default.regexpMatch("\\s*<.*") and
|
||||
result.getPropertyName() = prop
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,11 +33,10 @@ module Shared {
|
||||
* The XSS queries do not attempt to reason about correctness or completeness of sanitizers,
|
||||
* so any such replacement stops taint propagation.
|
||||
*/
|
||||
class MetacharEscapeSanitizer extends Sanitizer, DataFlow::MethodCallNode {
|
||||
class MetacharEscapeSanitizer extends Sanitizer, StringReplaceCall {
|
||||
MetacharEscapeSanitizer() {
|
||||
getMethodName() = "replace" and
|
||||
exists(RegExpConstant c |
|
||||
c.getLiteral() = getArgument(0).getALocalSource().asExpr() and
|
||||
c.getLiteral() = getRegExp().asExpr() and
|
||||
c.getValue().regexpMatch("['\"&<>]")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ module PolynomialReDoS {
|
||||
name = "split" or
|
||||
name = "matchAll" or
|
||||
name = "replace" or
|
||||
name = "replaceAll" or
|
||||
name = "search"
|
||||
)
|
||||
or
|
||||
|
||||
@@ -20,4 +20,8 @@ MyOtherStream.prototype.write = function (data) { /* use (instance (member MyOth
|
||||
return this;
|
||||
};
|
||||
|
||||
MyOtherStream.prototype.instanceProp = 1; /* def (member instanceProp (instance (member MyOtherStream (member exports (module classes))))) */
|
||||
|
||||
MyOtherStream.classProp = 1; /* def (member classProp (member MyOtherStream (member exports (module classes)))) */
|
||||
|
||||
module.exports.MyOtherStream = MyOtherStream;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import ApiGraphs.VerifyAssertions
|
||||
8
javascript/ql/test/ApiGraphs/partial-invoke/index.js
Normal file
8
javascript/ql/test/ApiGraphs/partial-invoke/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const cp = require('child_process');
|
||||
|
||||
module.exports = function () {
|
||||
return cp.spawn.bind(
|
||||
cp, // def (parameter -1 (member spawn (member exports (module child_process))))
|
||||
"cat" // def (parameter 0 (member spawn (member exports (module child_process))))
|
||||
);
|
||||
};
|
||||
3
javascript/ql/test/ApiGraphs/partial-invoke/package.json
Normal file
3
javascript/ql/test/ApiGraphs/partial-invoke/package.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "partial-invoke"
|
||||
}
|
||||
@@ -6,5 +6,6 @@
|
||||
| tst2.js:1:1:3:3 | <toplevel> | tst2.js:1:1:3:2 | define( ... 42;\\n}) |
|
||||
| tst3.js:1:1:3:3 | <toplevel> | tst3.js:1:1:3:2 | define( ... 42;\\n}) |
|
||||
| tst4.js:1:1:11:3 | <toplevel> | tst4.js:1:1:11:2 | define( ... };\\n}) |
|
||||
| tst5.js:1:1:6:3 | <toplevel> | tst5.js:1:1:6:2 | define( ... };\\n}) |
|
||||
| tst.js:1:1:6:3 | <toplevel> | tst.js:1:1:6:2 | define( ... };\\n}) |
|
||||
| umd.js:1:1:14:4 | <toplevel> | umd.js:4:9:4:43 | define( ... actory) |
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
getDependencyParameter
|
||||
| tst2.js:1:1:3:2 | define( ... 42;\\n}) | exports | tst2.js:1:30:1:36 | exports |
|
||||
| tst4.js:1:1:11:2 | define( ... };\\n}) | a.js | tst4.js:6:20:6:20 | a |
|
||||
| tst4.js:1:1:11:2 | define( ... };\\n}) | foo | tst4.js:6:23:6:23 | b |
|
||||
| tst4.js:1:1:11:2 | define( ... };\\n}) | nested/a | tst4.js:6:26:6:32 | exports |
|
||||
| tst5.js:1:1:6:2 | define( ... };\\n}) | ./a | tst5.js:1:37:1:37 | a |
|
||||
| tst5.js:1:1:6:2 | define( ... };\\n}) | ./dir/b | tst5.js:1:40:1:44 | {bar} |
|
||||
| tst.js:1:1:6:2 | define( ... };\\n}) | ./a | tst.js:1:37:1:37 | a |
|
||||
| tst.js:1:1:6:2 | define( ... };\\n}) | ./dir/b | tst.js:1:40:1:40 | b |
|
||||
| umd.js:4:9:4:43 | define( ... actory) | ./a | umd.js:9:19:9:19 | a |
|
||||
| umd.js:4:9:4:43 | define( ... actory) | ./dir/b | umd.js:9:22:9:22 | b |
|
||||
#select
|
||||
| a.js:1:1:3:2 | define( ... 2 };\\n}) | a.js:1:8:3:1 | functio ... 42 };\\n} |
|
||||
| dir/b.js:1:1:3:2 | define( ... : 42\\n}) | dir/b.js:1:8:3:1 | {\\n bar: 42\\n} |
|
||||
| lib/a.js:1:1:3:2 | define( ... 2 };\\n}) | lib/a.js:1:8:3:1 | functio ... 42 };\\n} |
|
||||
@@ -6,6 +18,7 @@
|
||||
| tst2.js:1:1:3:2 | define( ... 42;\\n}) | tst2.js:1:21:3:1 | functio ... = 42;\\n} |
|
||||
| tst3.js:1:1:3:2 | define( ... 42;\\n}) | tst3.js:1:8:3:1 | functio ... = 42;\\n} |
|
||||
| tst4.js:1:1:11:2 | define( ... };\\n}) | tst4.js:6:11:11:1 | functio ... };\\n} |
|
||||
| tst5.js:1:1:6:2 | define( ... };\\n}) | tst5.js:1:28:6:1 | functio ... };\\n} |
|
||||
| tst.js:1:1:6:2 | define( ... };\\n}) | tst.js:1:28:6:1 | functio ... };\\n} |
|
||||
| umd.js:4:9:4:43 | define( ... actory) | umd.js:1:18:1:24 | factory |
|
||||
| umd.js:4:9:4:43 | define( ... actory) | umd.js:9:9:14:1 | functio ... };\\n} |
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import javascript
|
||||
|
||||
query Parameter getDependencyParameter(AmdModuleDefinition mod, string name) {
|
||||
result = mod.getDependencyParameter(name)
|
||||
}
|
||||
|
||||
from AmdModuleDefinition d
|
||||
select d, d.getFactoryNode()
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
| tst4.js:1:1:11:2 | define( ... };\\n}) | tst4.js:3:9:3:13 | 'foo' |
|
||||
| tst4.js:1:1:11:2 | define( ... };\\n}) | tst4.js:4:9:4:18 | 'nested/a' |
|
||||
| tst4.js:1:1:11:2 | define( ... };\\n}) | tst4.js:5:9:5:20 | 'lib/foo.js' |
|
||||
| tst5.js:1:1:6:2 | define( ... };\\n}) | tst5.js:1:9:1:13 | './a' |
|
||||
| tst5.js:1:1:6:2 | define( ... };\\n}) | tst5.js:1:16:1:24 | './dir/b' |
|
||||
| tst.js:1:1:6:2 | define( ... };\\n}) | tst.js:1:9:1:13 | './a' |
|
||||
| tst.js:1:1:6:2 | define( ... };\\n}) | tst.js:1:16:1:24 | './dir/b' |
|
||||
| umd.js:4:9:4:43 | define( ... actory) | umd.js:4:17:4:21 | './a' |
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
| tst3.js:1:1:3:3 | <toplevel> | foo |
|
||||
| tst4.js:1:1:11:3 | <toplevel> | bar |
|
||||
| tst4.js:1:1:11:3 | <toplevel> | foo |
|
||||
| tst5.js:1:1:6:3 | <toplevel> | bar |
|
||||
| tst5.js:1:1:6:3 | <toplevel> | foo |
|
||||
| tst.js:1:1:6:3 | <toplevel> | bar |
|
||||
| tst.js:1:1:6:3 | <toplevel> | foo |
|
||||
| umd.js:1:1:14:4 | <toplevel> | bar |
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
| lib/foo.js:1:1:3:2 | define( ... : 23\\n}) | lib/foo.js:1:8:3:1 | {\\n foo: 23\\n} | lib/foo.js:1:8:3:1 | {\\n foo: 23\\n} |
|
||||
| lib/nested/a.js:1:1:3:2 | define( ... 2 };\\n}) | lib/nested/a.js:2:12:2:22 | { foo: 42 } | lib/nested/a.js:2:12:2:22 | { foo: 42 } |
|
||||
| tst4.js:1:1:11:2 | define( ... };\\n}) | tst4.js:7:12:10:5 | {\\n ... r\\n } | tst4.js:7:12:10:5 | {\\n ... r\\n } |
|
||||
| tst5.js:1:1:6:2 | define( ... };\\n}) | tst5.js:2:12:5:5 | {\\n ... r\\n } | tst5.js:2:12:5:5 | {\\n ... r\\n } |
|
||||
| tst.js:1:1:6:2 | define( ... };\\n}) | tst.js:2:12:5:5 | {\\n ... r\\n } | tst.js:2:12:5:5 | {\\n ... r\\n } |
|
||||
| umd.js:4:9:4:43 | define( ... actory) | umd.js:1:18:1:24 | factory | umd.js:1:18:1:24 | factory |
|
||||
| umd.js:4:9:4:43 | define( ... actory) | umd.js:1:18:1:24 | factory | umd.js:9:9:14:1 | functio ... };\\n} |
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
| tst4.js:1:1:11:3 | <toplevel> | tst4.js:3:9:3:13 | 'foo' | lib/foo.js:1:1:4:0 | <toplevel> |
|
||||
| tst4.js:1:1:11:3 | <toplevel> | tst4.js:4:9:4:18 | 'nested/a' | lib/nested/a.js:1:1:3:3 | <toplevel> |
|
||||
| tst4.js:1:1:11:3 | <toplevel> | tst4.js:5:9:5:20 | 'lib/foo.js' | lib/foo.js:1:1:4:0 | <toplevel> |
|
||||
| tst5.js:1:1:6:3 | <toplevel> | tst5.js:1:9:1:13 | './a' | a.js:1:1:3:3 | <toplevel> |
|
||||
| tst5.js:1:1:6:3 | <toplevel> | tst5.js:1:16:1:24 | './dir/b' | dir/b.js:1:1:3:3 | <toplevel> |
|
||||
| tst.js:1:1:6:3 | <toplevel> | tst.js:1:9:1:13 | './a' | a.js:1:1:3:3 | <toplevel> |
|
||||
| tst.js:1:1:6:3 | <toplevel> | tst.js:1:16:1:24 | './dir/b' | dir/b.js:1:1:3:3 | <toplevel> |
|
||||
| umd.js:1:1:14:4 | <toplevel> | umd.js:4:17:4:21 | './a' | a.js:1:1:3:3 | <toplevel> |
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
| tst3.js:1:1:3:3 | <toplevel> | foo | tst3.js:2:43:2:44 | 42 |
|
||||
| tst4.js:1:1:11:3 | <toplevel> | bar | tst4.js:9:14:9:18 | b.bar |
|
||||
| tst4.js:1:1:11:3 | <toplevel> | foo | tst4.js:8:14:8:18 | a.foo |
|
||||
| tst5.js:1:1:6:3 | <toplevel> | bar | tst5.js:4:14:4:16 | bar |
|
||||
| tst5.js:1:1:6:3 | <toplevel> | foo | tst5.js:3:14:3:18 | a.foo |
|
||||
| tst.js:1:1:6:3 | <toplevel> | bar | tst.js:4:14:4:18 | b.bar |
|
||||
| tst.js:1:1:6:3 | <toplevel> | foo | tst.js:3:14:3:18 | a.foo |
|
||||
| umd.js:1:1:14:4 | <toplevel> | bar | umd.js:11:14:11:18 | a.foo |
|
||||
|
||||
6
javascript/ql/test/library-tests/AMD/tst5.js
Normal file
6
javascript/ql/test/library-tests/AMD/tst5.js
Normal file
@@ -0,0 +1,6 @@
|
||||
define(['./a', './dir/b'], function(a, {bar}, exports) {
|
||||
return {
|
||||
foo: a.foo,
|
||||
bar: bar
|
||||
};
|
||||
});
|
||||
@@ -86,3 +86,11 @@
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:61:16:61:18 | o.r |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:68:16:68:22 | inner() |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:80:16:80:22 | outer() |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:87:16:87:43 | source1 ... /g, "") |
|
||||
| tst.js:2:17:2:22 | "src1" | tst.js:88:16:88:46 | "foo".r ... ource1) |
|
||||
| underscore.js:2:17:2:22 | "src1" | underscore.js:3:15:3:28 | _.max(source1) |
|
||||
| underscore.js:5:17:5:22 | "src2" | underscore.js:6:15:6:34 | _.union([], source2) |
|
||||
| underscore.js:5:17:5:22 | "src2" | underscore.js:7:15:7:32 | _.zip(source2, []) |
|
||||
| underscore.js:9:17:9:22 | "src3" | underscore.js:11:17:11:17 | x |
|
||||
| underscore.js:14:17:14:22 | "src4" | underscore.js:16:17:16:17 | e |
|
||||
| underscore.js:19:17:19:22 | "src5" | underscore.js:20:15:20:44 | _.map([ ... ource5) |
|
||||
|
||||
@@ -83,4 +83,7 @@
|
||||
|
||||
o.notTracked = source1;
|
||||
var sink22 = o.notTracked;
|
||||
|
||||
var sink23 = source1.replaceAll(/f/g, "");
|
||||
var sink24 = "foo".replaceAll(/f/g, source1);
|
||||
})();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user