Merge branch 'main' into CVE760-reexport

This commit is contained in:
Erik Krogh Kristensen
2020-10-06 12:28:44 +02:00
657 changed files with 29823 additions and 7357 deletions

View File

@@ -12,7 +12,6 @@
"watch": "tsc -p . -w --sourceMap"
},
"devDependencies": {
"@types/node": "12.7.11",
"tslint": "^5.9.1"
"@types/node": "12.7.11"
}
}

View File

@@ -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

View File

@@ -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: // '^'

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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");

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);

View 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;

View 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

View File

@@ -0,0 +1,3 @@
{
"experimental": true
}

View 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")

View 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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -0,0 +1,3 @@
type X = import(3);
type Y = import(`Foo`);
type Z = import(Y);

View File

@@ -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")

View File

@@ -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)

View File

@@ -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.

View File

@@ -1,4 +1,5 @@
/**
* @deprecated
* @name External dependency source links
* @kind source-link
* @metricType externalDependency

View File

@@ -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.

View File

@@ -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

View File

@@ -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 + "'")

View File

@@ -7,7 +7,7 @@
* @id js/incomplete-hostname-regexp
* @tags correctness
* security
* external/cwe/cwe-20
* external/cwe/cwe-020
*/
import javascript

View File

@@ -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()

View File

@@ -7,7 +7,7 @@
* @id js/incomplete-url-substring-sanitization
* @tags correctness
* security
* external/cwe/cwe-20
* external/cwe/cwe-020
*/
import javascript

View File

@@ -7,7 +7,7 @@
* @id js/regex/missing-regexp-anchor
* @tags correctness
* security
* external/cwe/cwe-20
* external/cwe/cwe-020
*/
import javascript

View File

@@ -9,7 +9,7 @@
* @id js/useless-regexp-character-escape
* @tags correctness
* security
* external/cwe/cwe-20
* external/cwe/cwe-020
*/
import javascript

View File

@@ -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>

View File

@@ -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>

View File

@@ -10,7 +10,7 @@
* @tags correctness
* security
* external/cwe/cwe-116
* external/cwe/cwe-20
* external/cwe/cwe-020
*/
import javascript

View File

@@ -10,7 +10,7 @@
* @tags security
* external/cwe/cwe-079
* external/cwe/cwe-116
* external/cwe/cwe-20
* external/cwe/cwe-020
*/
import javascript

View File

@@ -8,7 +8,7 @@
* @tags correctness
* security
* external/cwe/cwe-116
* external/cwe/cwe-20
* external/cwe/cwe-020
*/
import javascript

View File

@@ -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

View File

@@ -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>

View File

@@ -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. */

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -8,7 +8,7 @@
* @id js/missing-postmessageorigin-verification
* @tags correctness
* security
* external/cwe/cwe-20
* external/cwe/cwe-020
*/
import javascript

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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..

View 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))

View 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)

View 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))

View 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())

View 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())

View File

@@ -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`

View File

@@ -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()) }

View File

@@ -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

View File

@@ -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
)
}
/**

View File

@@ -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`.

View File

@@ -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)
)

View File

@@ -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

View File

@@ -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)
)

View File

@@ -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)
}
}

View File

@@ -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.
*/

View File

@@ -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)
}
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)
)

View File

@@ -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) }
}
/**

View File

@@ -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

View File

@@ -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
}
}
}
/**

View File

@@ -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() }
}
}

View File

@@ -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) }

View File

@@ -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) }
}
/**

View File

@@ -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() {

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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()
}
}

View File

@@ -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`.
*/

View File

@@ -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
)
}
/**

View File

@@ -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
)
)
}

View File

@@ -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() = "/"
|

View File

@@ -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()
)
}
}

View File

@@ -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
)
}
}

View File

@@ -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("['\"&<>]")
)
}

View File

@@ -51,6 +51,7 @@ module PolynomialReDoS {
name = "split" or
name = "matchAll" or
name = "replace" or
name = "replaceAll" or
name = "search"
)
or

View File

@@ -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;

View File

@@ -0,0 +1 @@
import ApiGraphs.VerifyAssertions

View 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))))
);
};

View File

@@ -0,0 +1,3 @@
{
"name": "partial-invoke"
}

View File

@@ -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) |

View File

@@ -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} |

View File

@@ -1,4 +1,8 @@
import javascript
query Parameter getDependencyParameter(AmdModuleDefinition mod, string name) {
result = mod.getDependencyParameter(name)
}
from AmdModuleDefinition d
select d, d.getFactoryNode()

View File

@@ -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' |

View File

@@ -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 |

View File

@@ -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} |

View File

@@ -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> |

View File

@@ -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 |

View File

@@ -0,0 +1,6 @@
define(['./a', './dir/b'], function(a, {bar}, exports) {
return {
foo: a.foo,
bar: bar
};
});

View File

@@ -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) |

View File

@@ -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