From 7d7b8c5d2df2af9eb13084b7dc562a48f40dea53 Mon Sep 17 00:00:00 2001 From: amammad Date: Wed, 13 Sep 2023 19:32:19 +1000 Subject: [PATCH 1/7] v1 --- javascript/ql/lib/javascript.qll | 1 + .../semmle/javascript/frameworks/Execa.qll | 249 ++++++++++++++++++ .../semmle/javascript/frameworks/ShellJS.qll | 23 +- .../frameworks/Execa/Execa.expected | 64 +++++ .../library-tests/frameworks/Execa/Execa.ql | 12 + .../library-tests/frameworks/Execa/tst.js | 49 ++++ .../frameworks/Shelljs/ShellJS.expected | 5 + .../library-tests/frameworks/Shelljs/tst.js | 3 + 8 files changed, 404 insertions(+), 2 deletions(-) create mode 100644 javascript/ql/lib/semmle/javascript/frameworks/Execa.qll create mode 100644 javascript/ql/test/library-tests/frameworks/Execa/Execa.expected create mode 100644 javascript/ql/test/library-tests/frameworks/Execa/Execa.ql create mode 100644 javascript/ql/test/library-tests/frameworks/Execa/tst.js diff --git a/javascript/ql/lib/javascript.qll b/javascript/ql/lib/javascript.qll index 07fb759bd655..238bd870a905 100644 --- a/javascript/ql/lib/javascript.qll +++ b/javascript/ql/lib/javascript.qll @@ -123,6 +123,7 @@ import semmle.javascript.frameworks.Request import semmle.javascript.frameworks.RxJS import semmle.javascript.frameworks.ServerLess import semmle.javascript.frameworks.ShellJS +import semmle.javascript.frameworks.Execa import semmle.javascript.frameworks.Snapdragon import semmle.javascript.frameworks.SystemCommandExecutors import semmle.javascript.frameworks.SQL diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll new file mode 100644 index 000000000000..39397ca383d6 --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll @@ -0,0 +1,249 @@ +/** + * Models the `execa` library in terms of `FileSystemAccess` and `SystemCommandExecution`. + */ + +import javascript +import semmle.javascript.security.dataflow.RequestForgeryCustomizations +import semmle.javascript.security.dataflow.UrlConcatenation + +// I don't know where should i put following class +private class DynamicImport extends SystemCommandExecution, DataFlow::ExprNode { + DynamicImport() { this = any(DynamicImportExpr e).getAChildExpr().flow() } + + override DataFlow::Node getACommandArgument() { result = this } + + override predicate isShellInterpreted(DataFlow::Node arg) { none() } + + override predicate isSync() { none() } + + override DataFlow::Node getOptionsArg() { none() } +} + +module Execa { + // https://github.com/sindresorhus/execa + private class ExecaRead extends FileSystemReadAccess, DataFlow::Node { + API::Node execaNode; + + ExecaRead() { + ( + execaNode = API::moduleImport("execa").getMember("$").getParameter(0) + or + execaNode = + API::moduleImport("execa") + .getMember(["execa", "execaCommand", "execaCommandSync", "execaSync"]) + .getParameter([0, 1, 2]) + ) and + this = execaNode.asSink() + } + + // data is the output of a command so IDK how it can be implemented + override DataFlow::Node getADataNode() { none() } + + override DataFlow::Node getAPathArgument() { + result = execaNode.getMember("inputFile").asSink() + } + } + + class ExecaCall extends API::CallNode { + string name; + + ExecaCall() { + this = API::moduleImport("execa").getMember("execa").getACall() and + name = "execa" + or + this = API::moduleImport("execa").getMember("execaSync").getACall() and + name = "execaSync" + } + + /** Gets the name of the exported function, such as `rm` in `shelljs.rm()`. */ + string getName() { result = name } + } + + private class ExecaExec extends SystemCommandExecution, ExecaCall { + ExecaExec() { name = ["execa", "execaSync"] } + + override DataFlow::Node getACommandArgument() { result = this.getArgument(0) } + + override predicate isShellInterpreted(DataFlow::Node arg) { + // if shell: true then first and second args are sinks + // options can be third argument + arg = [this.getArgument(0), this.getParameter(1).getUnknownMember().asSink()] and + isExecaShellEnable(this.getParameter(2)) + or + // options can be second argument + arg = this.getArgument(0) and + isExecaShellEnable(this.getParameter(1)) + } + + predicate isArgumentInjectable(DataFlow::Node arg) { + // arguments can be vulnerable if the arguemnts belongs to a command that is vulnerable to command execution through argument itself + // like `-oProxyCommand=touch file` by ssh command + // function execa(file: string,arguments?: readonly string[],options?: Options): ExecaChildProcess; + arg = this.getParameter(1).getUnknownMember().asSink() and + argumentIsInjectable(this.getParameter(0).asSink().getStringValue()) + } + + override predicate isSync() { name = "execaSync" } + + override DataFlow::Node getOptionsArg() { + result = this.getLastArgument() and result.asExpr() instanceof ObjectExpr + } + } + + class ExecaScriptExpr extends DataFlow::ExprNode { + string name; + + ExecaScriptExpr() { + this.asExpr() = + [ + API::moduleImport("execa").getMember("$"), + API::moduleImport("execa").getMember("$").getReturn() + ].getAValueReachableFromSource().asExpr() and + name = "ASync" + or + this.asExpr() = + [ + API::moduleImport("execa").getMember("$").getMember("sync"), + API::moduleImport("execa").getMember("$").getMember("sync").getReturn() + ].getAValueReachableFromSource().asExpr() and + name = "Sync" + } + + /** Gets the name of the exported function, such as `rm` in `shelljs.rm()`. */ + string getName() { result = name } + } + + private class ExecaScriptEec extends SystemCommandExecution, ExecaScriptExpr { + ExecaScriptEec() { name = ["Sync", "ASync"] } + + override DataFlow::Node getACommandArgument() { + result.asExpr() = templateLiteralChildAsSink(this.asExpr()).getAChildExpr+() + } + + override predicate isShellInterpreted(DataFlow::Node arg) { + // $({shell: true})`${sink} ${sink} .. ${sink}` + // ISSUE: $`cmd args` I can't reach the tag function argument easyily + exists(TemplateLiteral tmpL | templateLiteralChildAsSink(this.asExpr()) = tmpL | + arg.asExpr() = tmpL.getAChildExpr+() and + isExecaShellEnableWithExpr(this.asExpr().(CallExpr).getArgument(0)) + ) + } + + predicate isArgumentInjectable(DataFlow::Node arg) { + // first arg only can execute one command so it is not vulnerable when we can inject something into it + // but if the commmand is an executable like ssh then their arguments can be injected + // and execute a command (a payload like `-oProxyCommand=touch file`) + // $({shell: true})`${Can Not Be sink} ${sink} .. ${sink}` + exists(TemplateLiteral tmpL | templateLiteralChildAsSink(this.asExpr()) = tmpL | + arg.asExpr() = tmpL.getAChildExpr+() and + not arg.asExpr() = tmpL.getChildExpr(0) and + argumentIsInjectable(tmpL.getChildExpr(0).getStringValue()) + ) + } + + override predicate isSync() { name = "Sync" } + + override DataFlow::Node getOptionsArg() { + result = this.asExpr().getAChildExpr*().flow() and result.asExpr() instanceof ObjectExpr + } + } + + class ExecaCommandCall extends API::CallNode { + string name; + + ExecaCommandCall() { + this = API::moduleImport("execa").getMember("execaCommandSync").getACall() and + name = "execaCommandSync" + or + this = API::moduleImport("execa").getMember("execaCommand").getACall() and + name = "execaCommand" + } + + /** Gets the name of the exported function, such as `rm` in `shelljs.rm()`. */ + string getName() { result = name } + } + + private class ExecaCommandExec extends SystemCommandExecution, ExecaCommandCall { + ExecaCommandExec() { name = ["execaCommand", "execaCommandSync"] } + + override DataFlow::Node getACommandArgument() { result = this.getArgument(0) } + + override predicate isShellInterpreted(DataFlow::Node arg) { + // execaCommandSync(sink1 + sink2, {shell: true}) + arg.asExpr() = this.getArgument(0).asExpr().getAChildExpr+() and + isExecaShellEnable(this.getParameter(1)) + or + // there is only one argument that is constructed in previous nodes, + // it makes sanitizing really hard to select whether it is vulnerable to argument injection or not + arg = this.getParameter(0).asSink() and + not exists(this.getArgument(0).asExpr().getChildExpr(1)) + } + + predicate isArgumentInjectable(DataFlow::Node arg) { + // first arg is the command so we should check whether it is begening with a command that vulnerable to argument command injection or not + // execaCommand("echo " + sink); + // execaCommand(`echo ${sink}`); + argumentIsInjectable(this.getParameter(0).asSink().asExpr().getChildExpr(0).getStringValue()) and + arg.asExpr() = this.getParameter(0).asSink().asExpr().getAChildExpr+() and + not arg.asExpr() = this.getArgument(0).asExpr().getChildExpr(0) + } + + override predicate isSync() { name = "execaCommandSync" } + + override DataFlow::Node getOptionsArg() { + result = this.getLastArgument() and result.asExpr() instanceof ObjectExpr + } + } + + // parent = left`result` + TemplateLiteral templateLiteralChildAsSink(Expr left) { + exists(TaggedTemplateExpr parent | + parent.getTemplate() = result and + left = parent.getChildExpr(0) + ) + } + + class CommandsVulnerableToArgumentInjection extends string { + CommandsVulnerableToArgumentInjection() { + // Thanks to https://sonarsource.github.io/argument-injection-vectors/#+command + this = + [ + "chrome", "git-blame", "git-clone", "git-fetch", "git-grep", "git-ls-remote", "hg", + "psql", "qt5", "ssh", "tar", "zip", "aria2c", "tcpdump", "sysctl", "split", "sed", + "pidstat", "php", "nohup", "crontab", "crontab", "crontab", "crontab", "sh", "zsh", + "bash", "cmd.exe", "cmd" + ] + } + } + + // Check whether a command is vulnerable to argument injection or not + bindingset[cmd] + predicate argumentIsInjectable(string cmd) { + // "cmd args" or "cmd" + exists(CommandsVulnerableToArgumentInjection c | + // full/relative path to command like `/usr/bin/cmd` or `somedir/cmd` + cmd.matches("%/" + c) + or + // command like `cmd args` + cmd.matches(c + " %") + or + // only the command `cmd` + cmd = c + ) + } + + // Check whether Execa has shell enabled options or not, get Parameter responsible for opstions + predicate isExecaShellEnable(API::Node n) { + n.getMember("shell").asSink().asExpr().(BooleanLiteral).getValue() = "true" and + exists(n.getMember("shell")) + } + + // Check whether Execa has shell enabled options or not, get Parameter responsible for opstions + predicate isExecaShellEnableWithExpr(Expr n) { + exists(ObjectExpr o, Property p | o = n.getAChildExpr*() | + o.getAChild() = p and + p.getAChild().(Label).getName() = "shell" and + p.getAChild().(Literal).getValue() = "true" + ) + } +} diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll b/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll index 33fbef3cf3e7..86b637c3f5cc 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll @@ -1,15 +1,31 @@ /** * Models the `shelljs` library in terms of `FileSystemAccess` and `SystemCommandExecution`. + * + * https://www.npmjs.com/package/shelljs */ import javascript module ShellJS { + API::Node shellJSMember() { + result = API::moduleImport("shelljs") + or + result = + shellJSMember() + .getMember([ + "exec", "cd", "cp", "touch", "chmod", "pushd", "find", "ls", "ln", "mkdir", "mv", + "rm", "cat", "head", "sort", "tail", "uniq", "grep", "sed", "to", "toEnd", "echo" + ]) + .getReturn() + } + + API::Node test() { result = API::moduleImport("shelljs").getASuccessor*() } + /** * Gets an import of the `shelljs` or `async-shelljs` module. */ DataFlow::SourceNode shelljs() { - result = DataFlow::moduleImport("shelljs") or + result = shellJSMember().asSource() or result = DataFlow::moduleImport("async-shelljs") } @@ -39,7 +55,10 @@ module ShellJS { /** The `shelljs.exec` library modeled as a `shelljs` member. */ private class ShellJsExec extends Range { - ShellJsExec() { this = DataFlow::moduleImport("shelljs.exec") } + ShellJsExec() { + this = DataFlow::moduleImport("shelljs.exec") or + this = shellJSMember().getMember("exec").asSource() + } override string getName() { result = "exec" } } diff --git a/javascript/ql/test/library-tests/frameworks/Execa/Execa.expected b/javascript/ql/test/library-tests/frameworks/Execa/Execa.expected new file mode 100644 index 000000000000..7c27ecc42153 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Execa/Execa.expected @@ -0,0 +1,64 @@ +test_FileSystemAccess +| tst.js:18:9:18:23 | { shell: true } | +| tst.js:20:9:20:24 | { shell: false } | +| tst.js:24:13:24:22 | 'aCommand' | +| tst.js:24:25:24:36 | ['example1'] | +| tst.js:26:13:26:18 | 'echo' | +| tst.js:26:21:26:32 | ['example1'] | +| tst.js:28:13:28:47 | 'echo e ... ple 11' | +| tst.js:28:50:28:64 | { shell: true } | +| tst.js:29:13:29:29 | 'echo example 10' | +| tst.js:29:32:29:52 | ['; ech ... le 11'] | +| tst.js:29:55:29:69 | { shell: true } | +| tst.js:32:11:32:16 | 'echo' | +| tst.js:32:19:32:35 | ['example5 sync'] | +| tst.js:34:20:34:42 | "echo " ... gument" | +| tst.js:35:20:35:52 | `echo $ ... ndSync` | +| tst.js:37:18:37:20 | arg | +| tst.js:39:18:39:39 | "echo 1 ... echo 2" | +| tst.js:39:42:39:56 | { shell: true } | +| tst.js:45:9:45:29 | { input ... sink' } | +| tst.js:46:13:46:17 | 'cat' | +| tst.js:46:20:46:40 | { input ... sink' } | +| tst.js:47:13:47:18 | 'echo' | +| tst.js:47:21:47:32 | ['example2'] | +| tst.js:48:13:48:18 | 'echo' | +| tst.js:48:21:48:32 | ['example3'] | +| tst.js:49:13:49:18 | 'echo' | +| tst.js:49:21:49:32 | ['example4'] | +| tst.js:49:35:49:47 | { all: true } | +test_MissingFileSystemAccess +test_SystemCommandExecution +| tst.js:1:71:1:71 | $ | +| tst.js:4:7:4:7 | $ | +| tst.js:5:7:5:7 | $ | +| tst.js:6:1:6:1 | $ | +| tst.js:6:1:6:6 | $.sync | +| tst.js:10:7:10:7 | $ | +| tst.js:12:7:12:7 | $ | +| tst.js:13:1:13:1 | $ | +| tst.js:13:1:13:6 | $.sync | +| tst.js:15:1:15:1 | $ | +| tst.js:15:1:15:6 | $.sync | +| tst.js:16:7:16:7 | $ | +| tst.js:18:7:18:7 | $ | +| tst.js:18:7:18:24 | $({ shell: true }) | +| tst.js:20:7:20:7 | $ | +| tst.js:20:7:20:25 | $({ shell: false }) | +| tst.js:24:7:24:37 | execa(' ... ple1']) | +| tst.js:26:7:26:33 | execa(' ... ple1']) | +| tst.js:28:7:28:65 | execa(' ... true }) | +| tst.js:29:7:29:70 | execa(' ... true }) | +| tst.js:32:1:32:36 | execaSy ... sync']) | +| tst.js:34:7:34:43 | execaCo ... ument") | +| tst.js:35:7:35:53 | execaCo ... dSync`) | +| tst.js:37:1:37:21 | execaCo ... nc(arg) | +| tst.js:39:1:39:57 | execaCo ... true }) | +| tst.js:43:7:43:7 | $ | +| tst.js:45:7:45:7 | $ | +| tst.js:45:7:45:30 | $({ inp ... ink' }) | +| tst.js:46:7:46:41 | execa(' ... ink' }) | +| tst.js:47:7:47:33 | execa(' ... ple2']) | +| tst.js:48:7:48:33 | execa(' ... ple3']) | +| tst.js:49:7:49:48 | execa(' ... true }) | +test_FileNameSource diff --git a/javascript/ql/test/library-tests/frameworks/Execa/Execa.ql b/javascript/ql/test/library-tests/frameworks/Execa/Execa.ql new file mode 100644 index 000000000000..42858633cd3a --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Execa/Execa.ql @@ -0,0 +1,12 @@ +import javascript + +query predicate test_FileSystemAccess(FileSystemAccess access) { any() } + +query predicate test_MissingFileSystemAccess(VarAccess var) { + var.getName().matches("file%") and + not exists(FileSystemAccess access | access.getAPathArgument().asExpr() = var) +} + +query predicate test_SystemCommandExecution(SystemCommandExecution exec) { any() } + +query predicate test_FileNameSource(FileNameSource exec) { any() } diff --git a/javascript/ql/test/library-tests/frameworks/Execa/tst.js b/javascript/ql/test/library-tests/frameworks/Execa/tst.js new file mode 100644 index 000000000000..6b2e59b57d61 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Execa/tst.js @@ -0,0 +1,49 @@ +import { execa, execaSync, execaCommand, execaCommandSync, execaNode, $ } from 'execa'; + +// Node.js scripts +await $`echo example1`.pipeStderr(`tmp`); +await $`echo ${"example2"}`.pipeStderr(`tmp`); +$.sync`echo example2 sync` +// Multiple arguments +const args = ["arg:" + arg, 'example3', '&', 'rainbows!']; +// GOOD +await $`${arg} sth`; +// GOOD only one command can be executed +await $`${arg}`; +$.sync`${arg}` +// BAD argument injection +$.sync`echo ${args} ${args}` +await $`echo ${["-a", "-lps"]}` +// if shell: true then all inputs except first are dangerous +await $({ shell: true })`echo example6 ${";echo example6 > tmpdir/example6"}` +// GOOD +await $({ shell: false })`echo example6 ${";echo example6 > tmpdir/example6"}` + +// execa +// GOOD +await execa('aCommand', ['example1']); +// BAD argument injection +await execa('echo', ['example1']); +// BAD shell is enable +await execa('echo example 10 ; echo example 11', { shell: true }); +await execa('echo example 10', ['; echo example 11'], { shell: true }); + +// BAD argument injection +execaSync('echo', ['example5 sync']); +// BAD argument injection +await execaCommand("echo " + "badArgument"); +await execaCommand(`echo ${"arg1"} execaCommandSync`); +// bad totally controllable argument +execaCommandSync(arg); +// BAD shell is enable +execaCommandSync("echo 1 " + "; echo 2", { shell: true }); + +// FileSystemAccess +// Piping stdout to a file +await $`echo example8`.pipeStdout('tmp') +// Piping stdin from a file +await $({ inputFile: 'sink' })`cat` +await execa('cat', { inputFile: 'sink' }); +await execa('echo', ['example2']).pipeStdout('tmpdir/sink.txt'); +await execa('echo', ['example3']).pipeStderr('tmpdir/sink.txt'); +await execa('echo', ['example4'], { all: true }).pipeAll('tmpdir/sink.txt'); diff --git a/javascript/ql/test/library-tests/frameworks/Shelljs/ShellJS.expected b/javascript/ql/test/library-tests/frameworks/Shelljs/ShellJS.expected index 4c8f45af1c01..003ae3f442f8 100644 --- a/javascript/ql/test/library-tests/frameworks/Shelljs/ShellJS.expected +++ b/javascript/ql/test/library-tests/frameworks/Shelljs/ShellJS.expected @@ -52,9 +52,14 @@ test_FileSystemAccess | tst.js:56:1:56:18 | shelljs.uniq(file) | | tst.js:57:1:57:26 | shelljs ... file2) | | tst.js:58:1:58:32 | shelljs ... file2) | +| tst.js:60:1:60:17 | shelljs.cat(file) | +| tst.js:60:1:60:41 | shelljs ... cement) | +| tst.js:61:1:61:17 | shelljs.cat(file) | test_MissingFileSystemAccess test_SystemCommandExecution | tst.js:14:1:14:27 | shelljs ... ts, cb) | +| tst.js:60:1:60:51 | shelljs ... ec(cmd) | +| tst.js:61:1:61:27 | shelljs ... ec(cmd) | test_FileNameSource | tst.js:15:1:15:26 | shelljs ... file2) | | tst.js:24:1:24:16 | shelljs.ls(file) | diff --git a/javascript/ql/test/library-tests/frameworks/Shelljs/tst.js b/javascript/ql/test/library-tests/frameworks/Shelljs/tst.js index feb7c7affa52..9e7905c98477 100644 --- a/javascript/ql/test/library-tests/frameworks/Shelljs/tst.js +++ b/javascript/ql/test/library-tests/frameworks/Shelljs/tst.js @@ -56,3 +56,6 @@ shelljs.touch(file1, file2); shelljs.uniq(file); shelljs.uniq(file1, file2); shelljs.uniq(opts, file1, file2); + +shelljs.cat(file).sed(regex, replacement).exec(cmd); +shelljs.cat(file).exec(cmd); From 2be8daa38b6e27f68ccf3d9fbbee4031dd074880 Mon Sep 17 00:00:00 2001 From: amammad Date: Sun, 17 Sep 2023 00:47:34 +1000 Subject: [PATCH 2/7] fix grammar mistake --- javascript/ql/lib/semmle/javascript/frameworks/Execa.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll index 39397ca383d6..788cd39dc2e1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll @@ -6,6 +6,7 @@ import javascript import semmle.javascript.security.dataflow.RequestForgeryCustomizations import semmle.javascript.security.dataflow.UrlConcatenation +//TODO: comments // I don't know where should i put following class private class DynamicImport extends SystemCommandExecution, DataFlow::ExprNode { DynamicImport() { this = any(DynamicImportExpr e).getAChildExpr().flow() } @@ -180,7 +181,7 @@ module Execa { } predicate isArgumentInjectable(DataFlow::Node arg) { - // first arg is the command so we should check whether it is begening with a command that vulnerable to argument command injection or not + // first arg is the command so we should check whether it is beginning with a command that is vulnerable to argument command injection or not // execaCommand("echo " + sink); // execaCommand(`echo ${sink}`); argumentIsInjectable(this.getParameter(0).asSink().asExpr().getChildExpr(0).getStringValue()) and From 35679f02dde4746f89876d7306f73a8e77edd68e Mon Sep 17 00:00:00 2001 From: amammad Date: Tue, 19 Sep 2023 01:41:54 +1000 Subject: [PATCH 3/7] match new sinks to SystemCommandExecution --- .../semmle/javascript/frameworks/Execa.qll | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll index 788cd39dc2e1..e1b9af284067 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll @@ -20,8 +20,10 @@ private class DynamicImport extends SystemCommandExecution, DataFlow::ExprNode { override DataFlow::Node getOptionsArg() { none() } } +/** + * Provide model for [Execa](https://github.com/sindresorhus/execa) package + */ module Execa { - // https://github.com/sindresorhus/execa private class ExecaRead extends FileSystemReadAccess, DataFlow::Node { API::Node execaNode; @@ -77,7 +79,7 @@ module Execa { } predicate isArgumentInjectable(DataFlow::Node arg) { - // arguments can be vulnerable if the arguemnts belongs to a command that is vulnerable to command execution through argument itself + // arguments can be vulnerable if the arguments belongs to a command that is vulnerable to command execution through argument itself // like `-oProxyCommand=touch file` by ssh command // function execa(file: string,arguments?: readonly string[],options?: Options): ExecaChildProcess; arg = this.getParameter(1).getUnknownMember().asSink() and @@ -123,22 +125,18 @@ module Execa { override predicate isShellInterpreted(DataFlow::Node arg) { // $({shell: true})`${sink} ${sink} .. ${sink}` - // ISSUE: $`cmd args` I can't reach the tag function argument easyily + // ISSUE: $`cmd args` I can't reach the tag function argument easily exists(TemplateLiteral tmpL | templateLiteralChildAsSink(this.asExpr()) = tmpL | arg.asExpr() = tmpL.getAChildExpr+() and isExecaShellEnableWithExpr(this.asExpr().(CallExpr).getArgument(0)) ) } - predicate isArgumentInjectable(DataFlow::Node arg) { - // first arg only can execute one command so it is not vulnerable when we can inject something into it - // but if the commmand is an executable like ssh then their arguments can be injected - // and execute a command (a payload like `-oProxyCommand=touch file`) - // $({shell: true})`${Can Not Be sink} ${sink} .. ${sink}` + override DataFlow::Node getArgumentList() { + // $`${Can Not Be sink} ${sink} .. ${sink}` exists(TemplateLiteral tmpL | templateLiteralChildAsSink(this.asExpr()) = tmpL | - arg.asExpr() = tmpL.getAChildExpr+() and - not arg.asExpr() = tmpL.getChildExpr(0) and - argumentIsInjectable(tmpL.getChildExpr(0).getStringValue()) + result.asExpr() = tmpL.getAChildExpr+() and + not result.asExpr() = tmpL.getChildExpr(0) ) } @@ -169,6 +167,13 @@ module Execa { override DataFlow::Node getACommandArgument() { result = this.getArgument(0) } + override DataFlow::Node getArgumentList() { + // execaCommand("echo " + sink); + // execaCommand(`echo ${sink}`); + result.asExpr() = this.getParameter(0).asSink().asExpr().getAChildExpr+() and + not result.asExpr() = this.getArgument(0).asExpr().getChildExpr(0) + } + override predicate isShellInterpreted(DataFlow::Node arg) { // execaCommandSync(sink1 + sink2, {shell: true}) arg.asExpr() = this.getArgument(0).asExpr().getAChildExpr+() and @@ -180,15 +185,6 @@ module Execa { not exists(this.getArgument(0).asExpr().getChildExpr(1)) } - predicate isArgumentInjectable(DataFlow::Node arg) { - // first arg is the command so we should check whether it is beginning with a command that is vulnerable to argument command injection or not - // execaCommand("echo " + sink); - // execaCommand(`echo ${sink}`); - argumentIsInjectable(this.getParameter(0).asSink().asExpr().getChildExpr(0).getStringValue()) and - arg.asExpr() = this.getParameter(0).asSink().asExpr().getAChildExpr+() and - not arg.asExpr() = this.getArgument(0).asExpr().getChildExpr(0) - } - override predicate isSync() { name = "execaCommandSync" } override DataFlow::Node getOptionsArg() { From dc322279ade05995eca0669e4f1ed7425de6fc33 Mon Sep 17 00:00:00 2001 From: amammad Date: Tue, 19 Sep 2023 06:00:56 +1000 Subject: [PATCH 4/7] add comments --- .../semmle/javascript/frameworks/Execa.qll | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll index e1b9af284067..5d770d1eed72 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll @@ -6,9 +6,10 @@ import javascript import semmle.javascript.security.dataflow.RequestForgeryCustomizations import semmle.javascript.security.dataflow.UrlConcatenation -//TODO: comments -// I don't know where should i put following class -private class DynamicImport extends SystemCommandExecution, DataFlow::ExprNode { +/** + * The dynamic import expression input can be a `data:` URL which loads any module from that data + */ +class DynamicImport extends SystemCommandExecution, DataFlow::ExprNode { DynamicImport() { this = any(DynamicImportExpr e).getAChildExpr().flow() } override DataFlow::Node getACommandArgument() { result = this } @@ -24,7 +25,10 @@ private class DynamicImport extends SystemCommandExecution, DataFlow::ExprNode { * Provide model for [Execa](https://github.com/sindresorhus/execa) package */ module Execa { - private class ExecaRead extends FileSystemReadAccess, DataFlow::Node { + /** + * The Execa input file option + */ + class ExecaRead extends FileSystemReadAccess, DataFlow::Node { API::Node execaNode; ExecaRead() { @@ -47,6 +51,9 @@ module Execa { } } + /** + * A call to `execa.execa` or `execa.execaSync` + */ class ExecaCall extends API::CallNode { string name; @@ -62,7 +69,10 @@ module Execa { string getName() { result = name } } - private class ExecaExec extends SystemCommandExecution, ExecaCall { + /** + * The system command execution nodes for `execa.execa` or `execa.execaSync` functions + */ + class ExecaExec extends SystemCommandExecution, ExecaCall { ExecaExec() { name = ["execa", "execaSync"] } override DataFlow::Node getACommandArgument() { result = this.getArgument(0) } @@ -79,9 +89,7 @@ module Execa { } predicate isArgumentInjectable(DataFlow::Node arg) { - // arguments can be vulnerable if the arguments belongs to a command that is vulnerable to command execution through argument itself - // like `-oProxyCommand=touch file` by ssh command - // function execa(file: string,arguments?: readonly string[],options?: Options): ExecaChildProcess; + // execa(file: string,arguments?: readonly string[],options?: Options) arg = this.getParameter(1).getUnknownMember().asSink() and argumentIsInjectable(this.getParameter(0).asSink().getStringValue()) } @@ -93,7 +101,10 @@ module Execa { } } - class ExecaScriptExpr extends DataFlow::ExprNode { + /** + * A call to `execa.$` or `execa.$.sync` tag functions + */ + private class ExecaScriptExpr extends DataFlow::ExprNode { string name; ExecaScriptExpr() { @@ -116,7 +127,10 @@ module Execa { string getName() { result = name } } - private class ExecaScriptEec extends SystemCommandExecution, ExecaScriptExpr { + /** + * The system command execution nodes for `execa.$` or `execa.$.sync` tag functions + */ + class ExecaScriptEec extends SystemCommandExecution, ExecaScriptExpr { ExecaScriptEec() { name = ["Sync", "ASync"] } override DataFlow::Node getACommandArgument() { @@ -147,7 +161,10 @@ module Execa { } } - class ExecaCommandCall extends API::CallNode { + /** + * A call to `execa.execaCommandSync` or `execa.execaCommand` + */ + private class ExecaCommandCall extends API::CallNode { string name; ExecaCommandCall() { @@ -162,7 +179,10 @@ module Execa { string getName() { result = name } } - private class ExecaCommandExec extends SystemCommandExecution, ExecaCommandCall { + /** + * The system command execution nodes for `execa.execaCommand` or `execa.execaCommandSync` functions + */ + class ExecaCommandExec extends SystemCommandExecution, ExecaCommandCall { ExecaCommandExec() { name = ["execaCommand", "execaCommandSync"] } override DataFlow::Node getACommandArgument() { result = this.getArgument(0) } @@ -193,14 +213,14 @@ module Execa { } // parent = left`result` - TemplateLiteral templateLiteralChildAsSink(Expr left) { + private TemplateLiteral templateLiteralChildAsSink(Expr left) { exists(TaggedTemplateExpr parent | parent.getTemplate() = result and left = parent.getChildExpr(0) ) } - class CommandsVulnerableToArgumentInjection extends string { + private class CommandsVulnerableToArgumentInjection extends string { CommandsVulnerableToArgumentInjection() { // Thanks to https://sonarsource.github.io/argument-injection-vectors/#+command this = @@ -215,10 +235,10 @@ module Execa { // Check whether a command is vulnerable to argument injection or not bindingset[cmd] - predicate argumentIsInjectable(string cmd) { + private predicate argumentIsInjectable(string cmd) { // "cmd args" or "cmd" exists(CommandsVulnerableToArgumentInjection c | - // full/relative path to command like `/usr/bin/cmd` or `somedir/cmd` + // full/relative path to command like `/usr/bin/cmd` or `someDir/cmd` cmd.matches("%/" + c) or // command like `cmd args` @@ -229,14 +249,14 @@ module Execa { ) } - // Check whether Execa has shell enabled options or not, get Parameter responsible for opstions - predicate isExecaShellEnable(API::Node n) { + // Check whether Execa has shell enabled options or not, get Parameter responsible for options + private predicate isExecaShellEnable(API::Node n) { n.getMember("shell").asSink().asExpr().(BooleanLiteral).getValue() = "true" and exists(n.getMember("shell")) } - // Check whether Execa has shell enabled options or not, get Parameter responsible for opstions - predicate isExecaShellEnableWithExpr(Expr n) { + // Check whether Execa has shell enabled options or not, get Parameter responsible for options + private predicate isExecaShellEnableWithExpr(Expr n) { exists(ObjectExpr o, Property p | o = n.getAChildExpr*() | o.getAChild() = p and p.getAChild().(Label).getName() = "shell" and From 9657491d331f50606c27c872114eb359e9541c24 Mon Sep 17 00:00:00 2001 From: amammad Date: Thu, 21 Sep 2023 06:02:17 +1000 Subject: [PATCH 5/7] V1 --- .../semmle/javascript/frameworks/Execa.qll | 64 +++++++------------ .../frameworks/Execa/Execa.expected | 12 ++-- .../library-tests/frameworks/Execa/tst.js | 12 ++-- 3 files changed, 38 insertions(+), 50 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll index 5d770d1eed72..24574ac32853 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll @@ -88,12 +88,6 @@ module Execa { isExecaShellEnable(this.getParameter(1)) } - predicate isArgumentInjectable(DataFlow::Node arg) { - // execa(file: string,arguments?: readonly string[],options?: Options) - arg = this.getParameter(1).getUnknownMember().asSink() and - argumentIsInjectable(this.getParameter(0).asSink().getStringValue()) - } - override predicate isSync() { name = "execaSync" } override DataFlow::Node getOptionsArg() { @@ -134,7 +128,7 @@ module Execa { ExecaScriptEec() { name = ["Sync", "ASync"] } override DataFlow::Node getACommandArgument() { - result.asExpr() = templateLiteralChildAsSink(this.asExpr()).getAChildExpr+() + result.asExpr() = templateLiteralChildAsSink(this.asExpr()).getChildExpr(0) } override predicate isShellInterpreted(DataFlow::Node arg) { @@ -179,13 +173,32 @@ module Execa { string getName() { result = name } } + /** + * The system command execution nodes for `execa.execaCommand` or `execa.execaCommandSync` functions + */ + class ExecaCommandExec2 extends SystemCommandExecution, DataFlow::CallNode { + ExecaCommandExec2() { this = API::moduleImport("execa").getMember("execaCommand").getACall() } + + override DataFlow::Node getACommandArgument() { result = this.getArgument(0) } + + override DataFlow::Node getArgumentList() { result = this.getArgument(0) } + + override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getArgument(0) } + + override predicate isSync() { none() } + + override DataFlow::Node getOptionsArg() { result = this } + } + /** * The system command execution nodes for `execa.execaCommand` or `execa.execaCommandSync` functions */ class ExecaCommandExec extends SystemCommandExecution, ExecaCommandCall { ExecaCommandExec() { name = ["execaCommand", "execaCommandSync"] } - override DataFlow::Node getACommandArgument() { result = this.getArgument(0) } + override DataFlow::Node getACommandArgument() { + result = this.(DataFlow::CallNode).getArgument(0) + } override DataFlow::Node getArgumentList() { // execaCommand("echo " + sink); @@ -212,7 +225,7 @@ module Execa { } } - // parent = left`result` + // Holds if left parameter is the the left child of a template literal and returns the template literal private TemplateLiteral templateLiteralChildAsSink(Expr left) { exists(TaggedTemplateExpr parent | parent.getTemplate() = result and @@ -220,42 +233,13 @@ module Execa { ) } - private class CommandsVulnerableToArgumentInjection extends string { - CommandsVulnerableToArgumentInjection() { - // Thanks to https://sonarsource.github.io/argument-injection-vectors/#+command - this = - [ - "chrome", "git-blame", "git-clone", "git-fetch", "git-grep", "git-ls-remote", "hg", - "psql", "qt5", "ssh", "tar", "zip", "aria2c", "tcpdump", "sysctl", "split", "sed", - "pidstat", "php", "nohup", "crontab", "crontab", "crontab", "crontab", "sh", "zsh", - "bash", "cmd.exe", "cmd" - ] - } - } - - // Check whether a command is vulnerable to argument injection or not - bindingset[cmd] - private predicate argumentIsInjectable(string cmd) { - // "cmd args" or "cmd" - exists(CommandsVulnerableToArgumentInjection c | - // full/relative path to command like `/usr/bin/cmd` or `someDir/cmd` - cmd.matches("%/" + c) - or - // command like `cmd args` - cmd.matches(c + " %") - or - // only the command `cmd` - cmd = c - ) - } - - // Check whether Execa has shell enabled options or not, get Parameter responsible for options + // Holds whether Execa has shell enabled options or not, get Parameter responsible for options private predicate isExecaShellEnable(API::Node n) { n.getMember("shell").asSink().asExpr().(BooleanLiteral).getValue() = "true" and exists(n.getMember("shell")) } - // Check whether Execa has shell enabled options or not, get Parameter responsible for options + // Holds whether Execa has shell enabled options or not, get Parameter responsible for options private predicate isExecaShellEnableWithExpr(Expr n) { exists(ObjectExpr o, Property p | o = n.getAChildExpr*() | o.getAChild() = p and diff --git a/javascript/ql/test/library-tests/frameworks/Execa/Execa.expected b/javascript/ql/test/library-tests/frameworks/Execa/Execa.expected index 7c27ecc42153..a99d033b6efc 100644 --- a/javascript/ql/test/library-tests/frameworks/Execa/Execa.expected +++ b/javascript/ql/test/library-tests/frameworks/Execa/Execa.expected @@ -17,9 +17,9 @@ test_FileSystemAccess | tst.js:37:18:37:20 | arg | | tst.js:39:18:39:39 | "echo 1 ... echo 2" | | tst.js:39:42:39:56 | { shell: true } | -| tst.js:45:9:45:29 | { input ... sink' } | +| tst.js:45:9:45:27 | { inputFile: file } | | tst.js:46:13:46:17 | 'cat' | -| tst.js:46:20:46:40 | { input ... sink' } | +| tst.js:46:20:46:38 | { inputFile: file } | | tst.js:47:13:47:18 | 'echo' | | tst.js:47:21:47:32 | ['example2'] | | tst.js:48:13:48:18 | 'echo' | @@ -28,6 +28,10 @@ test_FileSystemAccess | tst.js:49:21:49:32 | ['example4'] | | tst.js:49:35:49:47 | { all: true } | test_MissingFileSystemAccess +| tst.js:43:35:43:38 | file | +| tst.js:47:46:47:49 | file | +| tst.js:48:46:48:49 | file | +| tst.js:49:58:49:61 | file | test_SystemCommandExecution | tst.js:1:71:1:71 | $ | | tst.js:4:7:4:7 | $ | @@ -56,8 +60,8 @@ test_SystemCommandExecution | tst.js:39:1:39:57 | execaCo ... true }) | | tst.js:43:7:43:7 | $ | | tst.js:45:7:45:7 | $ | -| tst.js:45:7:45:30 | $({ inp ... ink' }) | -| tst.js:46:7:46:41 | execa(' ... ink' }) | +| tst.js:45:7:45:28 | $({ inp ... file }) | +| tst.js:46:7:46:39 | execa(' ... file }) | | tst.js:47:7:47:33 | execa(' ... ple2']) | | tst.js:48:7:48:33 | execa(' ... ple3']) | | tst.js:49:7:49:48 | execa(' ... true }) | diff --git a/javascript/ql/test/library-tests/frameworks/Execa/tst.js b/javascript/ql/test/library-tests/frameworks/Execa/tst.js index 6b2e59b57d61..0e657d421a24 100644 --- a/javascript/ql/test/library-tests/frameworks/Execa/tst.js +++ b/javascript/ql/test/library-tests/frameworks/Execa/tst.js @@ -40,10 +40,10 @@ execaCommandSync("echo 1 " + "; echo 2", { shell: true }); // FileSystemAccess // Piping stdout to a file -await $`echo example8`.pipeStdout('tmp') +await $`echo example8`.pipeStdout(file) // Piping stdin from a file -await $({ inputFile: 'sink' })`cat` -await execa('cat', { inputFile: 'sink' }); -await execa('echo', ['example2']).pipeStdout('tmpdir/sink.txt'); -await execa('echo', ['example3']).pipeStderr('tmpdir/sink.txt'); -await execa('echo', ['example4'], { all: true }).pipeAll('tmpdir/sink.txt'); +await $({ inputFile: file })`cat` +await execa('cat', { inputFile: file }); +await execa('echo', ['example2']).pipeStdout(file); +await execa('echo', ['example3']).pipeStderr(file); +await execa('echo', ['example4'], { all: true }).pipeAll(file); From 90a14bf38a2c5962c45e381ed5969b29c57c3070 Mon Sep 17 00:00:00 2001 From: amammad Date: Fri, 22 Sep 2023 19:04:40 +1000 Subject: [PATCH 6/7] fix qldoc --- .../semmle/javascript/frameworks/Execa.qll | 20 ++----------------- .../javascript/frameworks/NodeJSLib.qll | 15 ++++++++++++++ .../semmle/javascript/frameworks/ShellJS.qll | 2 -- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll index 24574ac32853..fe654a287f5c 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Execa.qll @@ -6,21 +6,6 @@ import javascript import semmle.javascript.security.dataflow.RequestForgeryCustomizations import semmle.javascript.security.dataflow.UrlConcatenation -/** - * The dynamic import expression input can be a `data:` URL which loads any module from that data - */ -class DynamicImport extends SystemCommandExecution, DataFlow::ExprNode { - DynamicImport() { this = any(DynamicImportExpr e).getAChildExpr().flow() } - - override DataFlow::Node getACommandArgument() { result = this } - - override predicate isShellInterpreted(DataFlow::Node arg) { none() } - - override predicate isSync() { none() } - - override DataFlow::Node getOptionsArg() { none() } -} - /** * Provide model for [Execa](https://github.com/sindresorhus/execa) package */ @@ -225,7 +210,7 @@ module Execa { } } - // Holds if left parameter is the the left child of a template literal and returns the template literal + // Holds if left parameter is the left child of a template literal and returns the template literal private TemplateLiteral templateLiteralChildAsSink(Expr left) { exists(TaggedTemplateExpr parent | parent.getTemplate() = result and @@ -235,8 +220,7 @@ module Execa { // Holds whether Execa has shell enabled options or not, get Parameter responsible for options private predicate isExecaShellEnable(API::Node n) { - n.getMember("shell").asSink().asExpr().(BooleanLiteral).getValue() = "true" and - exists(n.getMember("shell")) + n.getMember("shell").asSink().asExpr().(BooleanLiteral).getValue() = "true" } // Holds whether Execa has shell enabled options or not, get Parameter responsible for options diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll index 258a583e1ca0..9996c4fe2261 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll @@ -760,6 +760,21 @@ module NodeJSLib { } } + /** + * The dynamic import expression input can be a `data:` URL which loads any module from that data + */ + class DynamicImport extends SystemCommandExecution, DataFlow::ExprNode { + DynamicImport() { this = any(DynamicImportExpr e).getAChildExpr().flow() } + + override DataFlow::Node getACommandArgument() { result = this } + + override predicate isShellInterpreted(DataFlow::Node arg) { arg = this } + + override predicate isSync() { none() } + + override DataFlow::Node getOptionsArg() { none() } + } + /** * A call to a method from module `child_process`. */ diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll b/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll index 86b637c3f5cc..0d68fda01ab8 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll @@ -19,8 +19,6 @@ module ShellJS { .getReturn() } - API::Node test() { result = API::moduleImport("shelljs").getASuccessor*() } - /** * Gets an import of the `shelljs` or `async-shelljs` module. */ From 97cd620f23e6ff588553ebd35d3217bd61e6effd Mon Sep 17 00:00:00 2001 From: amammad Date: Fri, 22 Sep 2023 19:16:31 +1000 Subject: [PATCH 7/7] add dynamic import test --- .../frameworks/NodeJSLib/exec.js | 3 + .../frameworks/NodeJSLib/tests.expected | 483 +++++++++--------- 2 files changed, 245 insertions(+), 241 deletions(-) diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/exec.js b/javascript/ql/test/library-tests/frameworks/NodeJSLib/exec.js index d22f0bb760d8..8a3bf9d6da47 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/exec.js +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/exec.js @@ -5,3 +5,6 @@ cp.execFileSync("sh", ["-c", "node --version"]); cp.fork("foo", ["arg"]); cp.spawn("echo", ["Hi"], cb); cp.spawnSync("echo", ["Hi", "there"]); + +// dynamic import +await import('data:text/javascript,console.log("hello!");') \ No newline at end of file diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected b/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected index 78c2c35309ff..8336e6f5a064 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected @@ -1,45 +1,50 @@ -test_isCreateServer -| createServer.js:2:1:2:42 | https.c ... es) {}) | -| createServer.js:3:1:3:45 | https.c ... es) {}) | -| createServer.js:4:1:4:47 | require ... => {}) | -| createServer.js:31:17:31:58 | http.cr ... dler()) | -| src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | -| src/http.js:12:1:16:2 | http.cr ... r");\\n}) | -| src/http.js:57:1:57:31 | http.cr ... dler()) | -| src/http.js:60:1:60:33 | createS ... res){}) | -| src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | -| src/http.js:70:1:70:36 | http.cr ... dler()) | -| src/http.js:72:1:76:2 | http.cr ... })\\n}) | -| src/https.js:4:14:10:2 | https.c ... foo;\\n}) | -| src/https.js:12:1:16:2 | https.c ... r");\\n}) | -| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | -| src/indirect.js:34:14:34:58 | http.cr ... dler()) | -test_RequestInputAccess -| src/http.js:6:26:6:32 | req.url | url | src/http.js:4:32:10:1 | functio ... .foo;\\n} | -| src/http.js:8:3:8:20 | req.headers.cookie | cookie | src/http.js:4:32:10:1 | functio ... .foo;\\n} | -| src/http.js:9:3:9:17 | req.headers.foo | header | src/http.js:4:32:10:1 | functio ... .foo;\\n} | -| src/https.js:6:26:6:32 | req.url | url | src/https.js:4:33:10:1 | functio ... .foo;\\n} | -| src/https.js:8:3:8:20 | req.headers.cookie | cookie | src/https.js:4:33:10:1 | functio ... .foo;\\n} | -| src/https.js:9:3:9:17 | req.headers.foo | header | src/https.js:4:33:10:1 | functio ... .foo;\\n} | -| src/indirect.js:17:28:17:34 | req.url | url | src/indirect.js:16:12:20:5 | functio ... ;\\n } | -test_RouteHandler_getAResponseHeader -| src/http.js:4:32:10:1 | functio ... .foo;\\n} | location | src/http.js:7:3:7:42 | res.wri ... rget }) | -| src/http.js:12:19:16:1 | functio ... ar");\\n} | content-type | src/http.js:13:3:13:44 | res.set ... /html') | -| src/https.js:4:33:10:1 | functio ... .foo;\\n} | location | src/https.js:7:3:7:42 | res.wri ... rget }) | -| src/https.js:12:20:16:1 | functio ... ar");\\n} | content-type | src/https.js:13:3:13:44 | res.set ... /html') | -| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | content-type | src/indirect2.js:14:3:14:51 | res.set ... /json') | -| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | content-type | src/indirect2.js:14:3:14:51 | res.set ... /json') | -test_HeaderDefinition_defines -| src/http.js:13:3:13:44 | res.set ... /html') | content-type | text/html | -| src/https.js:13:3:13:44 | res.set ... /html') | content-type | text/html | -| src/indirect2.js:14:3:14:51 | res.set ... /json') | content-type | application/json | -test_SystemCommandExecution -| es6-imported-exec.js:3:1:3:11 | exec("cmd") | es6-imported-exec.js:3:6:3:10 | "cmd" | -| exec.js:3:1:3:38 | cp.exec ... "], cb) | exec.js:3:13:3:18 | "node" | -| exec.js:4:1:4:47 | cp.exec ... sion"]) | exec.js:4:17:4:20 | "sh" | -| exec.js:5:1:5:23 | cp.fork ... "arg"]) | exec.js:5:9:5:13 | "foo" | -| exec.js:6:1:6:28 | cp.spaw ... "], cb) | exec.js:6:10:6:15 | "echo" | -| exec.js:7:1:7:37 | cp.spaw ... here"]) | exec.js:7:14:7:19 | "echo" | +test_Credentials +| src/http.js:18:22:18:27 | "auth" | credentials | +| src/https.js:18:23:18:28 | "auth" | credentials | +test_RequestExpr +| createServer.js:2:30:2:32 | req | createServer.js:2:20:2:41 | functio ... res) {} | +| createServer.js:3:33:3:35 | req | createServer.js:3:23:3:44 | functio ... res) {} | +| createServer.js:4:32:4:34 | req | createServer.js:4:31:4:46 | (req, res) => {} | +| createServer.js:25:47:25:49 | req | createServer.js:25:37:27:5 | functio ... ;\\n } | +| src/http.js:4:41:4:43 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | +| src/http.js:4:41:4:43 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | +| src/http.js:6:26:6:28 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | +| src/http.js:8:3:8:5 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | +| src/http.js:9:3:9:5 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | +| src/http.js:12:28:12:30 | req | src/http.js:12:19:16:1 | functio ... ar");\\n} | +| src/http.js:55:21:55:23 | req | src/http.js:55:12:55:30 | function(req,res){} | +| src/http.js:60:23:60:25 | req | src/http.js:60:14:60:32 | function(req,res){} | +| src/http.js:62:28:62:30 | req | src/http.js:62:19:65:1 | functio ... r2");\\n} | +| src/http.js:62:28:62:30 | req | src/http.js:62:19:65:1 | functio ... r2");\\n} | +| src/http.js:63:17:63:19 | req | src/http.js:62:19:65:1 | functio ... r2");\\n} | +| src/http.js:68:13:68:15 | req | src/http.js:68:12:68:27 | (req,res) => f() | +| src/http.js:72:29:72:31 | req | src/http.js:72:19:76:1 | functio ... \\n })\\n} | +| src/http.js:72:29:72:31 | req | src/http.js:72:19:76:1 | functio ... \\n })\\n} | +| src/http.js:73:3:73:5 | req | src/http.js:72:19:76:1 | functio ... \\n })\\n} | +| src/http.js:81:41:81:43 | req | src/http.js:81:22:86:1 | functio ... la");\\n} | +| src/http.js:81:41:81:43 | req | src/http.js:81:22:86:1 | functio ... la");\\n} | +| src/http.js:82:3:82:5 | req | src/http.js:81:22:86:1 | functio ... la");\\n} | +| src/https.js:4:42:4:44 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | +| src/https.js:4:42:4:44 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | +| src/https.js:6:26:6:28 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | +| src/https.js:8:3:8:5 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | +| src/https.js:9:3:9:5 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | +| src/https.js:12:29:12:31 | req | src/https.js:12:20:16:1 | functio ... ar");\\n} | +| src/indirect2.js:9:14:9:16 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | +| src/indirect2.js:9:14:9:16 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | +| src/indirect2.js:10:12:10:14 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | +| src/indirect2.js:10:42:10:44 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | +| src/indirect2.js:13:28:13:30 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | +| src/indirect2.js:13:28:13:30 | req | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | +| src/indirect.js:16:21:16:23 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | +| src/indirect.js:16:21:16:23 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | +| src/indirect.js:17:28:17:30 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | +| src/indirect.js:19:33:19:35 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | +| src/indirect.js:25:25:25:27 | req | src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | +| src/indirect.js:28:24:28:26 | req | src/indirect.js:28:15:30:3 | functio ... ");\\n } | +test_HeaderAccess +| src/http.js:9:3:9:17 | req.headers.foo | foo | +| src/https.js:9:3:9:17 | req.headers.foo | foo | test_ResponseExpr | createServer.js:2:35:2:37 | res | createServer.js:2:20:2:41 | functio ... res) {} | | createServer.js:3:38:3:40 | res | createServer.js:3:23:3:44 | functio ... res) {} | @@ -101,6 +106,47 @@ test_ResponseExpr | src/indirect.js:28:29:28:31 | res | src/indirect.js:28:15:30:3 | functio ... ");\\n } | | src/indirect.js:28:29:28:31 | res | src/indirect.js:28:15:30:3 | functio ... ");\\n } | | src/indirect.js:29:5:29:7 | res | src/indirect.js:28:15:30:3 | functio ... ");\\n } | +test_RouteHandler +| createServer.js:2:20:2:41 | functio ... res) {} | createServer.js:2:1:2:42 | https.c ... es) {}) | +| createServer.js:3:23:3:44 | functio ... res) {} | createServer.js:3:1:3:45 | https.c ... es) {}) | +| createServer.js:4:31:4:46 | (req, res) => {} | createServer.js:4:1:4:47 | require ... => {}) | +| createServer.js:25:37:27:5 | functio ... ;\\n } | createServer.js:31:17:31:58 | http.cr ... dler()) | +| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | +| src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:12:1:16:2 | http.cr ... r");\\n}) | +| src/http.js:55:12:55:30 | function(req,res){} | src/http.js:57:1:57:31 | http.cr ... dler()) | +| src/http.js:60:14:60:32 | function(req,res){} | src/http.js:60:1:60:33 | createS ... res){}) | +| src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | +| src/http.js:68:12:68:27 | (req,res) => f() | src/http.js:70:1:70:36 | http.cr ... dler()) | +| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:72:1:76:2 | http.cr ... })\\n}) | +| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:14:10:2 | https.c ... foo;\\n}) | +| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:1:16:2 | https.c ... r");\\n}) | +| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | +| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | +| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:34:14:34:58 | http.cr ... dler()) | +| src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | src/indirect.js:34:14:34:58 | http.cr ... dler()) | +| src/indirect.js:28:15:30:3 | functio ... ");\\n } | src/indirect.js:34:14:34:58 | http.cr ... dler()) | +test_ClientRequest +| http.js:2:1:2:56 | http.re ... fined)) | +| src/http.js:18:1:18:30 | http.re ... uth" }) | +| src/http.js:21:15:26:6 | http.re ... \\n }) | +| src/http.js:27:16:27:73 | http.re ... POST'}) | +| src/https.js:18:1:18:31 | https.r ... uth" }) | +test_isCreateServer +| createServer.js:2:1:2:42 | https.c ... es) {}) | +| createServer.js:3:1:3:45 | https.c ... es) {}) | +| createServer.js:4:1:4:47 | require ... => {}) | +| createServer.js:31:17:31:58 | http.cr ... dler()) | +| src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | +| src/http.js:12:1:16:2 | http.cr ... r");\\n}) | +| src/http.js:57:1:57:31 | http.cr ... dler()) | +| src/http.js:60:1:60:33 | createS ... res){}) | +| src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | +| src/http.js:70:1:70:36 | http.cr ... dler()) | +| src/http.js:72:1:76:2 | http.cr ... })\\n}) | +| src/https.js:4:14:10:2 | https.c ... foo;\\n}) | +| src/https.js:12:1:16:2 | https.c ... r");\\n}) | +| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | +| src/indirect.js:34:14:34:58 | http.cr ... dler()) | test_HeaderDefinition | src/http.js:7:3:7:42 | res.wri ... rget }) | src/http.js:4:32:10:1 | functio ... .foo;\\n} | | src/http.js:13:3:13:44 | res.set ... /html') | src/http.js:12:19:16:1 | functio ... ar");\\n} | @@ -109,34 +155,6 @@ test_HeaderDefinition | src/https.js:13:3:13:44 | res.set ... /html') | src/https.js:12:20:16:1 | functio ... ar");\\n} | | src/indirect2.js:14:3:14:51 | res.set ... /json') | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | | src/indirect2.js:14:3:14:51 | res.set ... /json') | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | -test_RouteSetup_getServer -| createServer.js:2:1:2:42 | https.c ... es) {}) | createServer.js:2:1:2:42 | https.c ... es) {}) | -| createServer.js:3:1:3:45 | https.c ... es) {}) | createServer.js:3:1:3:45 | https.c ... es) {}) | -| createServer.js:4:1:4:47 | require ... => {}) | createServer.js:4:1:4:47 | require ... => {}) | -| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:31:17:31:58 | http.cr ... dler()) | -| src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | -| src/http.js:12:1:16:2 | http.cr ... r");\\n}) | src/http.js:12:1:16:2 | http.cr ... r");\\n}) | -| src/http.js:57:1:57:31 | http.cr ... dler()) | src/http.js:57:1:57:31 | http.cr ... dler()) | -| src/http.js:60:1:60:33 | createS ... res){}) | src/http.js:60:1:60:33 | createS ... res){}) | -| src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | -| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:70:1:70:36 | http.cr ... dler()) | -| src/http.js:72:1:76:2 | http.cr ... })\\n}) | src/http.js:72:1:76:2 | http.cr ... })\\n}) | -| src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:14:10:2 | https.c ... foo;\\n}) | -| src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:1:16:2 | https.c ... r");\\n}) | -| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | -| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:34:14:34:58 | http.cr ... dler()) | -test_ClientRequest -| http.js:2:1:2:56 | http.re ... fined)) | -| src/http.js:18:1:18:30 | http.re ... uth" }) | -| src/http.js:21:15:26:6 | http.re ... \\n }) | -| src/http.js:27:16:27:73 | http.re ... POST'}) | -| src/https.js:18:1:18:31 | https.r ... uth" }) | -test_HeaderDefinition_getAHeaderName -| src/http.js:7:3:7:42 | res.wri ... rget }) | location | -| src/http.js:13:3:13:44 | res.set ... /html') | content-type | -| src/https.js:7:3:7:42 | res.wri ... rget }) | location | -| src/https.js:13:3:13:44 | res.set ... /html') | content-type | -| src/indirect2.js:14:3:14:51 | res.set ... /json') | content-type | test_ServerDefinition | createServer.js:2:1:2:42 | https.c ... es) {}) | | createServer.js:3:1:3:45 | https.c ... es) {}) | @@ -153,9 +171,106 @@ test_ServerDefinition | src/https.js:12:1:16:2 | https.c ... r");\\n}) | | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | | src/indirect.js:34:14:34:58 | http.cr ... dler()) | -test_HeaderAccess -| src/http.js:9:3:9:17 | req.headers.foo | foo | -| src/https.js:9:3:9:17 | req.headers.foo | foo | +test_RemoteFlowSources +| createServer.js:7:24:7:27 | data | +| createServer.js:14:24:14:27 | data | +| src/http.js:6:26:6:32 | req.url | +| src/http.js:8:3:8:20 | req.headers.cookie | +| src/http.js:9:3:9:17 | req.headers.foo | +| src/http.js:29:26:29:33 | response | +| src/http.js:30:28:30:32 | chunk | +| src/http.js:40:23:40:30 | authInfo | +| src/http.js:45:23:45:27 | error | +| src/http.js:63:17:63:33 | req.query.myParam | +| src/http.js:73:18:73:22 | chunk | +| src/http.js:82:18:82:22 | chunk | +| src/https.js:6:26:6:32 | req.url | +| src/https.js:8:3:8:20 | req.headers.cookie | +| src/https.js:9:3:9:17 | req.headers.foo | +| src/indirect2.js:10:12:10:25 | req.params.key | +| src/indirect.js:17:28:17:34 | req.url | +test_RequestInputAccess +| src/http.js:6:26:6:32 | req.url | url | src/http.js:4:32:10:1 | functio ... .foo;\\n} | +| src/http.js:8:3:8:20 | req.headers.cookie | cookie | src/http.js:4:32:10:1 | functio ... .foo;\\n} | +| src/http.js:9:3:9:17 | req.headers.foo | header | src/http.js:4:32:10:1 | functio ... .foo;\\n} | +| src/https.js:6:26:6:32 | req.url | url | src/https.js:4:33:10:1 | functio ... .foo;\\n} | +| src/https.js:8:3:8:20 | req.headers.cookie | cookie | src/https.js:4:33:10:1 | functio ... .foo;\\n} | +| src/https.js:9:3:9:17 | req.headers.foo | header | src/https.js:4:33:10:1 | functio ... .foo;\\n} | +| src/indirect.js:17:28:17:34 | req.url | url | src/indirect.js:16:12:20:5 | functio ... ;\\n } | +test_ResponseSendArgument +| createServer.js:26:17:26:25 | this.data | createServer.js:25:37:27:5 | functio ... ;\\n } | +| src/http.js:14:13:14:17 | "foo" | src/http.js:12:19:16:1 | functio ... ar");\\n} | +| src/http.js:15:11:15:15 | "bar" | src/http.js:12:19:16:1 | functio ... ar");\\n} | +| src/http.js:64:11:64:16 | "bar2" | src/http.js:62:19:65:1 | functio ... r2");\\n} | +| src/http.js:85:11:85:15 | "bla" | src/http.js:81:22:86:1 | functio ... la");\\n} | +| src/https.js:14:13:14:17 | "foo" | src/https.js:12:20:16:1 | functio ... ar");\\n} | +| src/https.js:15:11:15:15 | "bar" | src/https.js:12:20:16:1 | functio ... ar");\\n} | +| src/indirect.js:26:13:26:17 | "foo" | src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | +| src/indirect.js:29:13:29:17 | "bar" | src/indirect.js:28:15:30:3 | functio ... ");\\n } | +test_RouteSetup_getServer +| createServer.js:2:1:2:42 | https.c ... es) {}) | createServer.js:2:1:2:42 | https.c ... es) {}) | +| createServer.js:3:1:3:45 | https.c ... es) {}) | createServer.js:3:1:3:45 | https.c ... es) {}) | +| createServer.js:4:1:4:47 | require ... => {}) | createServer.js:4:1:4:47 | require ... => {}) | +| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:31:17:31:58 | http.cr ... dler()) | +| src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | +| src/http.js:12:1:16:2 | http.cr ... r");\\n}) | src/http.js:12:1:16:2 | http.cr ... r");\\n}) | +| src/http.js:57:1:57:31 | http.cr ... dler()) | src/http.js:57:1:57:31 | http.cr ... dler()) | +| src/http.js:60:1:60:33 | createS ... res){}) | src/http.js:60:1:60:33 | createS ... res){}) | +| src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | +| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:70:1:70:36 | http.cr ... dler()) | +| src/http.js:72:1:76:2 | http.cr ... })\\n}) | src/http.js:72:1:76:2 | http.cr ... })\\n}) | +| src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:14:10:2 | https.c ... foo;\\n}) | +| src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:1:16:2 | https.c ... r");\\n}) | +| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | +| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:34:14:34:58 | http.cr ... dler()) | +test_SystemCommandExecution +| es6-imported-exec.js:3:1:3:11 | exec("cmd") | es6-imported-exec.js:3:6:3:10 | "cmd" | +| exec.js:3:1:3:38 | cp.exec ... "], cb) | exec.js:3:13:3:18 | "node" | +| exec.js:4:1:4:47 | cp.exec ... sion"]) | exec.js:4:17:4:20 | "sh" | +| exec.js:5:1:5:23 | cp.fork ... "arg"]) | exec.js:5:9:5:13 | "foo" | +| exec.js:6:1:6:28 | cp.spaw ... "], cb) | exec.js:6:10:6:15 | "echo" | +| exec.js:7:1:7:37 | cp.spaw ... here"]) | exec.js:7:14:7:19 | "echo" | +| exec.js:10:14:10:58 | 'data:t ... lo!");' | exec.js:10:14:10:58 | 'data:t ... lo!");' | +test_HeaderDefinition_defines +| src/http.js:13:3:13:44 | res.set ... /html') | content-type | text/html | +| src/https.js:13:3:13:44 | res.set ... /html') | content-type | text/html | +| src/indirect2.js:14:3:14:51 | res.set ... /json') | content-type | application/json | +test_ClientRequest_getADataNode +| src/http.js:27:16:27:73 | http.re ... POST'}) | src/http.js:50:16:50:22 | 'stuff' | +| src/http.js:27:16:27:73 | http.re ... POST'}) | src/http.js:51:14:51:25 | 'more stuff' | +test_RouteSetup_getARouteHandler +| createServer.js:2:1:2:42 | https.c ... es) {}) | createServer.js:2:20:2:41 | functio ... res) {} | +| createServer.js:3:1:3:45 | https.c ... es) {}) | createServer.js:3:23:3:44 | functio ... res) {} | +| createServer.js:4:1:4:47 | require ... => {}) | createServer.js:4:31:4:46 | (req, res) => {} | +| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:22:41:24:5 | return of anonymous function | +| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:23:16:23:33 | this.handleRequest | +| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:23:16:23:44 | this.ha ... d(this) | +| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:25:37:27:5 | functio ... ;\\n } | +| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:31:35:31:57 | app.get ... ndler() | +| src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | src/http.js:4:32:10:1 | functio ... .foo;\\n} | +| src/http.js:12:1:16:2 | http.cr ... r");\\n}) | src/http.js:12:19:16:1 | functio ... ar");\\n} | +| src/http.js:57:1:57:31 | http.cr ... dler()) | src/http.js:54:1:56:1 | return of function getHandler | +| src/http.js:57:1:57:31 | http.cr ... dler()) | src/http.js:55:12:55:30 | function(req,res){} | +| src/http.js:57:1:57:31 | http.cr ... dler()) | src/http.js:57:19:57:30 | getHandler() | +| src/http.js:60:1:60:33 | createS ... res){}) | src/http.js:60:14:60:32 | function(req,res){} | +| src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | src/http.js:62:19:65:1 | functio ... r2");\\n} | +| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:67:1:69:1 | return of function getArrowHandler | +| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:68:12:68:27 | (req,res) => f() | +| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:70:19:70:35 | getArrowHandler() | +| src/http.js:72:1:76:2 | http.cr ... })\\n}) | src/http.js:72:19:76:1 | functio ... \\n })\\n} | +| src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:33:10:1 | functio ... .foo;\\n} | +| src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:20:16:1 | functio ... ar");\\n} | +| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | +| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:10:3:10:40 | handler ... Case()] | +| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | +| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:14:19:21:3 | return of method requestHandler | +| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:16:12:20:5 | functio ... ;\\n } | +| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:16:12:20:16 | functio ... d(this) | +| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:17:21:17:35 | routes[req.url] | +| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:17:40:17:50 | routes['*'] | +| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | +| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:28:15:30:3 | functio ... ");\\n } | +| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:34:32:34:57 | appServ ... ndler() | test_HeaderDefinition_getNameExpr | src/http.js:7:3:7:42 | res.wri ... rget }) | src/http.js:7:17:7:19 | 302 | | src/http.js:13:3:13:44 | res.set ... /html') | src/http.js:13:17:13:30 | 'Content-Type' | @@ -163,6 +278,47 @@ test_HeaderDefinition_getNameExpr | src/https.js:7:3:7:42 | res.wri ... rget }) | src/https.js:7:17:7:19 | 302 | | src/https.js:13:3:13:44 | res.set ... /html') | src/https.js:13:17:13:30 | 'Content-Type' | | src/indirect2.js:14:3:14:51 | res.set ... /json') | src/indirect2.js:14:17:14:30 | 'Content-Type' | +test_RouteHandler_getARequestExpr +| createServer.js:2:20:2:41 | functio ... res) {} | createServer.js:2:30:2:32 | req | +| createServer.js:3:23:3:44 | functio ... res) {} | createServer.js:3:33:3:35 | req | +| createServer.js:4:31:4:46 | (req, res) => {} | createServer.js:4:32:4:34 | req | +| createServer.js:25:37:27:5 | functio ... ;\\n } | createServer.js:25:47:25:49 | req | +| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:4:41:4:43 | req | +| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:4:41:4:43 | req | +| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:6:26:6:28 | req | +| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:8:3:8:5 | req | +| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:9:3:9:5 | req | +| src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:12:28:12:30 | req | +| src/http.js:55:12:55:30 | function(req,res){} | src/http.js:55:21:55:23 | req | +| src/http.js:60:14:60:32 | function(req,res){} | src/http.js:60:23:60:25 | req | +| src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:62:28:62:30 | req | +| src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:62:28:62:30 | req | +| src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:63:17:63:19 | req | +| src/http.js:68:12:68:27 | (req,res) => f() | src/http.js:68:13:68:15 | req | +| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:72:29:72:31 | req | +| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:72:29:72:31 | req | +| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:73:3:73:5 | req | +| src/http.js:81:22:86:1 | functio ... la");\\n} | src/http.js:81:41:81:43 | req | +| src/http.js:81:22:86:1 | functio ... la");\\n} | src/http.js:81:41:81:43 | req | +| src/http.js:81:22:86:1 | functio ... la");\\n} | src/http.js:82:3:82:5 | req | +| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:42:4:44 | req | +| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:42:4:44 | req | +| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:6:26:6:28 | req | +| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:8:3:8:5 | req | +| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:9:3:9:5 | req | +| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:29:12:31 | req | +| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:9:14:9:16 | req | +| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:9:14:9:16 | req | +| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:10:12:10:14 | req | +| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:10:42:10:44 | req | +| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:13:28:13:30 | req | +| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:13:28:13:30 | req | +| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:21:16:23 | req | +| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:21:16:23 | req | +| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:17:28:17:30 | req | +| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:19:33:19:35 | req | +| src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | src/indirect.js:25:25:25:27 | req | +| src/indirect.js:28:15:30:3 | functio ... ");\\n } | src/indirect.js:28:24:28:26 | req | test_RouteHandler_getAResponseExpr | createServer.js:2:20:2:41 | functio ... res) {} | createServer.js:2:35:2:37 | res | | createServer.js:3:23:3:44 | functio ... res) {} | createServer.js:3:38:3:40 | res | @@ -224,6 +380,19 @@ test_RouteHandler_getAResponseExpr | src/indirect.js:28:15:30:3 | functio ... ");\\n } | src/indirect.js:28:29:28:31 | res | | src/indirect.js:28:15:30:3 | functio ... ");\\n } | src/indirect.js:28:29:28:31 | res | | src/indirect.js:28:15:30:3 | functio ... ");\\n } | src/indirect.js:29:5:29:7 | res | +test_HeaderDefinition_getAHeaderName +| src/http.js:7:3:7:42 | res.wri ... rget }) | location | +| src/http.js:13:3:13:44 | res.set ... /html') | content-type | +| src/https.js:7:3:7:42 | res.wri ... rget }) | location | +| src/https.js:13:3:13:44 | res.set ... /html') | content-type | +| src/indirect2.js:14:3:14:51 | res.set ... /json') | content-type | +test_RouteHandler_getAResponseHeader +| src/http.js:4:32:10:1 | functio ... .foo;\\n} | location | src/http.js:7:3:7:42 | res.wri ... rget }) | +| src/http.js:12:19:16:1 | functio ... ar");\\n} | content-type | src/http.js:13:3:13:44 | res.set ... /html') | +| src/https.js:4:33:10:1 | functio ... .foo;\\n} | location | src/https.js:7:3:7:42 | res.wri ... rget }) | +| src/https.js:12:20:16:1 | functio ... ar");\\n} | content-type | src/https.js:13:3:13:44 | res.set ... /html') | +| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | content-type | src/indirect2.js:14:3:14:51 | res.set ... /json') | +| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | content-type | src/indirect2.js:14:3:14:51 | res.set ... /json') | test_ServerDefinition_getARouteHandler | createServer.js:2:1:2:42 | https.c ... es) {}) | createServer.js:2:20:2:41 | functio ... res) {} | | createServer.js:3:1:3:45 | https.c ... es) {}) | createServer.js:3:23:3:44 | functio ... res) {} | @@ -243,177 +412,9 @@ test_ServerDefinition_getARouteHandler | src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:16:12:20:5 | functio ... ;\\n } | | src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | | src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:28:15:30:3 | functio ... ");\\n } | -test_ResponseSendArgument -| createServer.js:26:17:26:25 | this.data | createServer.js:25:37:27:5 | functio ... ;\\n } | -| src/http.js:14:13:14:17 | "foo" | src/http.js:12:19:16:1 | functio ... ar");\\n} | -| src/http.js:15:11:15:15 | "bar" | src/http.js:12:19:16:1 | functio ... ar");\\n} | -| src/http.js:64:11:64:16 | "bar2" | src/http.js:62:19:65:1 | functio ... r2");\\n} | -| src/http.js:85:11:85:15 | "bla" | src/http.js:81:22:86:1 | functio ... la");\\n} | -| src/https.js:14:13:14:17 | "foo" | src/https.js:12:20:16:1 | functio ... ar");\\n} | -| src/https.js:15:11:15:15 | "bar" | src/https.js:12:20:16:1 | functio ... ar");\\n} | -| src/indirect.js:26:13:26:17 | "foo" | src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | -| src/indirect.js:29:13:29:17 | "bar" | src/indirect.js:28:15:30:3 | functio ... ");\\n } | -test_RouteSetup_getARouteHandler -| createServer.js:2:1:2:42 | https.c ... es) {}) | createServer.js:2:20:2:41 | functio ... res) {} | -| createServer.js:3:1:3:45 | https.c ... es) {}) | createServer.js:3:23:3:44 | functio ... res) {} | -| createServer.js:4:1:4:47 | require ... => {}) | createServer.js:4:31:4:46 | (req, res) => {} | -| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:22:41:24:5 | return of anonymous function | -| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:23:16:23:33 | this.handleRequest | -| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:23:16:23:44 | this.ha ... d(this) | -| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:25:37:27:5 | functio ... ;\\n } | -| createServer.js:31:17:31:58 | http.cr ... dler()) | createServer.js:31:35:31:57 | app.get ... ndler() | -| src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | src/http.js:4:32:10:1 | functio ... .foo;\\n} | -| src/http.js:12:1:16:2 | http.cr ... r");\\n}) | src/http.js:12:19:16:1 | functio ... ar");\\n} | -| src/http.js:57:1:57:31 | http.cr ... dler()) | src/http.js:54:1:56:1 | return of function getHandler | -| src/http.js:57:1:57:31 | http.cr ... dler()) | src/http.js:55:12:55:30 | function(req,res){} | -| src/http.js:57:1:57:31 | http.cr ... dler()) | src/http.js:57:19:57:30 | getHandler() | -| src/http.js:60:1:60:33 | createS ... res){}) | src/http.js:60:14:60:32 | function(req,res){} | -| src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | src/http.js:62:19:65:1 | functio ... r2");\\n} | -| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:67:1:69:1 | return of function getArrowHandler | -| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:68:12:68:27 | (req,res) => f() | -| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:70:19:70:35 | getArrowHandler() | -| src/http.js:72:1:76:2 | http.cr ... })\\n}) | src/http.js:72:19:76:1 | functio ... \\n })\\n} | -| src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:33:10:1 | functio ... .foo;\\n} | -| src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:20:16:1 | functio ... ar");\\n} | -| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | -| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:10:3:10:40 | handler ... Case()] | -| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | -| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:14:19:21:3 | return of method requestHandler | -| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:16:12:20:5 | functio ... ;\\n } | -| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:16:12:20:16 | functio ... d(this) | -| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:17:21:17:35 | routes[req.url] | -| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:17:40:17:50 | routes['*'] | -| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | -| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:28:15:30:3 | functio ... ");\\n } | -| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:34:32:34:57 | appServ ... ndler() | -test_ClientRequest_getADataNode -| src/http.js:27:16:27:73 | http.re ... POST'}) | src/http.js:50:16:50:22 | 'stuff' | -| src/http.js:27:16:27:73 | http.re ... POST'}) | src/http.js:51:14:51:25 | 'more stuff' | -test_RemoteFlowSources -| createServer.js:7:24:7:27 | data | -| createServer.js:14:24:14:27 | data | -| src/http.js:6:26:6:32 | req.url | -| src/http.js:8:3:8:20 | req.headers.cookie | -| src/http.js:9:3:9:17 | req.headers.foo | -| src/http.js:29:26:29:33 | response | -| src/http.js:30:28:30:32 | chunk | -| src/http.js:40:23:40:30 | authInfo | -| src/http.js:45:23:45:27 | error | -| src/http.js:63:17:63:33 | req.query.myParam | -| src/http.js:73:18:73:22 | chunk | -| src/http.js:82:18:82:22 | chunk | -| src/https.js:6:26:6:32 | req.url | -| src/https.js:8:3:8:20 | req.headers.cookie | -| src/https.js:9:3:9:17 | req.headers.foo | -| src/indirect2.js:10:12:10:25 | req.params.key | -| src/indirect.js:17:28:17:34 | req.url | -test_RouteHandler -| createServer.js:2:20:2:41 | functio ... res) {} | createServer.js:2:1:2:42 | https.c ... es) {}) | -| createServer.js:3:23:3:44 | functio ... res) {} | createServer.js:3:1:3:45 | https.c ... es) {}) | -| createServer.js:4:31:4:46 | (req, res) => {} | createServer.js:4:1:4:47 | require ... => {}) | -| createServer.js:25:37:27:5 | functio ... ;\\n } | createServer.js:31:17:31:58 | http.cr ... dler()) | -| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | -| src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:12:1:16:2 | http.cr ... r");\\n}) | -| src/http.js:55:12:55:30 | function(req,res){} | src/http.js:57:1:57:31 | http.cr ... dler()) | -| src/http.js:60:14:60:32 | function(req,res){} | src/http.js:60:1:60:33 | createS ... res){}) | -| src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | -| src/http.js:68:12:68:27 | (req,res) => f() | src/http.js:70:1:70:36 | http.cr ... dler()) | -| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:72:1:76:2 | http.cr ... })\\n}) | -| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:14:10:2 | https.c ... foo;\\n}) | -| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:1:16:2 | https.c ... r");\\n}) | -| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | -| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | -| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:34:14:34:58 | http.cr ... dler()) | -| src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | src/indirect.js:34:14:34:58 | http.cr ... dler()) | -| src/indirect.js:28:15:30:3 | functio ... ");\\n } | src/indirect.js:34:14:34:58 | http.cr ... dler()) | -test_RequestExpr -| createServer.js:2:30:2:32 | req | createServer.js:2:20:2:41 | functio ... res) {} | -| createServer.js:3:33:3:35 | req | createServer.js:3:23:3:44 | functio ... res) {} | -| createServer.js:4:32:4:34 | req | createServer.js:4:31:4:46 | (req, res) => {} | -| createServer.js:25:47:25:49 | req | createServer.js:25:37:27:5 | functio ... ;\\n } | -| src/http.js:4:41:4:43 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | -| src/http.js:4:41:4:43 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | -| src/http.js:6:26:6:28 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | -| src/http.js:8:3:8:5 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | -| src/http.js:9:3:9:5 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | -| src/http.js:12:28:12:30 | req | src/http.js:12:19:16:1 | functio ... ar");\\n} | -| src/http.js:55:21:55:23 | req | src/http.js:55:12:55:30 | function(req,res){} | -| src/http.js:60:23:60:25 | req | src/http.js:60:14:60:32 | function(req,res){} | -| src/http.js:62:28:62:30 | req | src/http.js:62:19:65:1 | functio ... r2");\\n} | -| src/http.js:62:28:62:30 | req | src/http.js:62:19:65:1 | functio ... r2");\\n} | -| src/http.js:63:17:63:19 | req | src/http.js:62:19:65:1 | functio ... r2");\\n} | -| src/http.js:68:13:68:15 | req | src/http.js:68:12:68:27 | (req,res) => f() | -| src/http.js:72:29:72:31 | req | src/http.js:72:19:76:1 | functio ... \\n })\\n} | -| src/http.js:72:29:72:31 | req | src/http.js:72:19:76:1 | functio ... \\n })\\n} | -| src/http.js:73:3:73:5 | req | src/http.js:72:19:76:1 | functio ... \\n })\\n} | -| src/http.js:81:41:81:43 | req | src/http.js:81:22:86:1 | functio ... la");\\n} | -| src/http.js:81:41:81:43 | req | src/http.js:81:22:86:1 | functio ... la");\\n} | -| src/http.js:82:3:82:5 | req | src/http.js:81:22:86:1 | functio ... la");\\n} | -| src/https.js:4:42:4:44 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | -| src/https.js:4:42:4:44 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | -| src/https.js:6:26:6:28 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | -| src/https.js:8:3:8:5 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | -| src/https.js:9:3:9:5 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | -| src/https.js:12:29:12:31 | req | src/https.js:12:20:16:1 | functio ... ar");\\n} | -| src/indirect2.js:9:14:9:16 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | -| src/indirect2.js:9:14:9:16 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | -| src/indirect2.js:10:12:10:14 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | -| src/indirect2.js:10:42:10:44 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | -| src/indirect2.js:13:28:13:30 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | -| src/indirect2.js:13:28:13:30 | req | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | -| src/indirect.js:16:21:16:23 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | -| src/indirect.js:16:21:16:23 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | -| src/indirect.js:17:28:17:30 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | -| src/indirect.js:19:33:19:35 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | -| src/indirect.js:25:25:25:27 | req | src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | -| src/indirect.js:28:24:28:26 | req | src/indirect.js:28:15:30:3 | functio ... ");\\n } | test_SystemCommandExecution_getAnArgumentForCommand | exec.js:3:1:3:38 | cp.exec ... "], cb) | exec.js:3:21:3:33 | ["--version"] | | exec.js:4:1:4:47 | cp.exec ... sion"]) | exec.js:4:23:4:46 | ["-c", ... rsion"] | | exec.js:5:1:5:23 | cp.fork ... "arg"]) | exec.js:5:16:5:22 | ["arg"] | | exec.js:6:1:6:28 | cp.spaw ... "], cb) | exec.js:6:18:6:23 | ["Hi"] | | exec.js:7:1:7:37 | cp.spaw ... here"]) | exec.js:7:22:7:36 | ["Hi", "there"] | -test_Credentials -| src/http.js:18:22:18:27 | "auth" | credentials | -| src/https.js:18:23:18:28 | "auth" | credentials | -test_RouteHandler_getARequestExpr -| createServer.js:2:20:2:41 | functio ... res) {} | createServer.js:2:30:2:32 | req | -| createServer.js:3:23:3:44 | functio ... res) {} | createServer.js:3:33:3:35 | req | -| createServer.js:4:31:4:46 | (req, res) => {} | createServer.js:4:32:4:34 | req | -| createServer.js:25:37:27:5 | functio ... ;\\n } | createServer.js:25:47:25:49 | req | -| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:4:41:4:43 | req | -| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:4:41:4:43 | req | -| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:6:26:6:28 | req | -| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:8:3:8:5 | req | -| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:9:3:9:5 | req | -| src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:12:28:12:30 | req | -| src/http.js:55:12:55:30 | function(req,res){} | src/http.js:55:21:55:23 | req | -| src/http.js:60:14:60:32 | function(req,res){} | src/http.js:60:23:60:25 | req | -| src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:62:28:62:30 | req | -| src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:62:28:62:30 | req | -| src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:63:17:63:19 | req | -| src/http.js:68:12:68:27 | (req,res) => f() | src/http.js:68:13:68:15 | req | -| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:72:29:72:31 | req | -| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:72:29:72:31 | req | -| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:73:3:73:5 | req | -| src/http.js:81:22:86:1 | functio ... la");\\n} | src/http.js:81:41:81:43 | req | -| src/http.js:81:22:86:1 | functio ... la");\\n} | src/http.js:81:41:81:43 | req | -| src/http.js:81:22:86:1 | functio ... la");\\n} | src/http.js:82:3:82:5 | req | -| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:42:4:44 | req | -| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:42:4:44 | req | -| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:6:26:6:28 | req | -| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:8:3:8:5 | req | -| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:9:3:9:5 | req | -| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:29:12:31 | req | -| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:9:14:9:16 | req | -| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:9:14:9:16 | req | -| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:10:12:10:14 | req | -| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:10:42:10:44 | req | -| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:13:28:13:30 | req | -| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:13:28:13:30 | req | -| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:21:16:23 | req | -| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:21:16:23 | req | -| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:17:28:17:30 | req | -| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:19:33:19:35 | req | -| src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | src/indirect.js:25:25:25:27 | req | -| src/indirect.js:28:15:30:3 | functio ... ");\\n } | src/indirect.js:28:24:28:26 | req |