Merge branch 'main' into amammad-js-CodeInjection_execa

This commit is contained in:
amammad
2023-10-11 13:27:26 +02:00
committed by GitHub
1963 changed files with 168686 additions and 95124 deletions

View File

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

View File

@@ -0,0 +1,9 @@
const tag = require("tag");
tag.string`string1
${23}` // def=moduleImport("tag").getMember("exports").getMember("string").getParameter(1)
tag.highlight`string2
${23}
morestring
${42}` // def=moduleImport("tag").getMember("exports").getMember("highlight").getParameter(2)

View File

@@ -0,0 +1,3 @@
{
"name": "tagged-template"
}

View File

@@ -0,0 +1,3 @@
test.amd.range(function() {
return { foo: 42 };
});

View File

@@ -4,6 +4,7 @@ amoModule_exports
| lib/a.js:1:1:3:3 | <toplevel> | foo | lib/a.js:2:19:2:20 | 42 |
| lib/foo.js:1:1:4:0 | <toplevel> | foo | lib/foo.js:2:10:2:11 | 23 |
| lib/nested/a.js:1:1:3:3 | <toplevel> | foo | lib/nested/a.js:2:19:2:20 | 42 |
| test_range.js:1:1:4:0 | <toplevel> | foo | test_range.js:2:19:2:20 | 42 |
| tst2.js:1:1:3:3 | <toplevel> | foo | tst2.js:2:19:2:20 | 42 |
| 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 |
@@ -22,6 +23,7 @@ amdModule
| lib/a.js:1:1:3:3 | <toplevel> | lib/a.js:1:1:3:2 | define( ... 2 };\\n}) |
| lib/foo.js:1:1:4:0 | <toplevel> | lib/foo.js:1:1:3:2 | define( ... : 23\\n}) |
| lib/nested/a.js:1:1:3:3 | <toplevel> | lib/nested/a.js:1:1:3:2 | define( ... 2 };\\n}) |
| test_range.js:1:1:4:0 | <toplevel> | test_range.js:1:1:3:2 | test.am ... 2 };\\n}) |
| 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}) |
@@ -48,6 +50,7 @@ amdModuleDefinition
| lib/a.js:1:1:3:2 | define( ... 2 };\\n}) | lib/a.js:1:8:3:1 | functio ... 42 };\\n} |
| lib/foo.js:1:1:3:2 | define( ... : 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:1:8:3:1 | functio ... 42 };\\n} |
| test_range.js:1:1:3:2 | test.am ... 2 };\\n}) | test_range.js:1:16:3:1 | functio ... 42 };\\n} |
| 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} |
@@ -78,6 +81,7 @@ amdModuleExportedSymbol
| lib/a.js:1:1:3:3 | <toplevel> | foo |
| lib/foo.js:1:1:4:0 | <toplevel> | foo |
| lib/nested/a.js:1:1:3:3 | <toplevel> | foo |
| test_range.js:1:1:4:0 | <toplevel> | foo |
| tst2.js:1:1:3:3 | <toplevel> | foo |
| tst3.js:1:1:3:3 | <toplevel> | foo |
| tst4.js:1:1:11:3 | <toplevel> | bar |
@@ -96,6 +100,7 @@ amdModuleExpr
| lib/a.js:1:1:3:2 | define( ... 2 };\\n}) | lib/a.js:2:12:2:22 | { foo: 42 } | lib/a.js:2:12:2:22 | { foo: 42 } |
| 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 } |
| test_range.js:1:1:3:2 | test.am ... 2 };\\n}) | test_range.js:2:12:2:22 | { foo: 42 } | test_range.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 } |

View File

@@ -1,5 +1,9 @@
import javascript
class TestAmdModuleRange extends AmdModuleDefinition::Range {
TestAmdModuleRange() { this.getCallee().(PropAccess).getQualifiedName() = "test.amd.range" }
}
query predicate amoModule_exports(Module m, string name, DataFlow::Node exportValue) {
exportValue = m.getAnExportedValue(name)
}

View File

@@ -4,8 +4,11 @@ nodes
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| tst.html:1:5:1:9 | [ExprStmt] using | semmle.label | [ExprStmt] using |
| tst.html:1:5:1:9 | [ExprStmt] using | semmle.order | 1 |
| tst.html:1:5:1:9 | [VarRef] using | semmle.label | [VarRef] using |
| tst.js:1:1:10:1 | [FunctionDeclStmt] functio ... } } | semmle.label | [FunctionDeclStmt] functio ... } } |
| tst.js:1:1:10:1 | [FunctionDeclStmt] functio ... } } | semmle.order | 1 |
| tst.js:1:1:10:1 | [FunctionDeclStmt] functio ... } } | semmle.order | 2 |
| tst.js:1:10:1:10 | [VarDecl] g | semmle.label | [VarDecl] g |
| tst.js:1:14:10:1 | [BlockStmt] { u ... } } | semmle.label | [BlockStmt] { u ... } } |
| tst.js:2:5:2:33 | [DeclStmt] using stream = ... | semmle.label | [DeclStmt] using stream = ... |
@@ -28,7 +31,7 @@ nodes
| tst.js:6:45:9:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } |
| tst.js:8:9:8:14 | [BreakStmt] break; | semmle.label | [BreakStmt] break; |
| tst.js:12:1:21:1 | [FunctionDeclStmt] async f ... nd"); } | semmle.label | [FunctionDeclStmt] async f ... nd"); } |
| tst.js:12:1:21:1 | [FunctionDeclStmt] async f ... nd"); } | semmle.order | 2 |
| tst.js:12:1:21:1 | [FunctionDeclStmt] async f ... nd"); } | semmle.order | 3 |
| tst.js:12:16:12:16 | [VarDecl] h | semmle.label | [VarDecl] h |
| tst.js:12:20:21:1 | [BlockStmt] { a ... nd"); } | semmle.label | [BlockStmt] { a ... nd"); } |
| tst.js:13:5:13:39 | [DeclStmt] using stream = ... | semmle.label | [DeclStmt] using stream = ... |
@@ -51,7 +54,7 @@ nodes
| tst.js:20:13:20:15 | [Label] log | semmle.label | [Label] log |
| tst.js:20:17:20:21 | [Literal] "end" | semmle.label | [Literal] "end" |
| tst.js:23:1:29:1 | [FunctionDeclStmt] functio ... ing); } | semmle.label | [FunctionDeclStmt] functio ... ing); } |
| tst.js:23:1:29:1 | [FunctionDeclStmt] functio ... ing); } | semmle.order | 3 |
| tst.js:23:1:29:1 | [FunctionDeclStmt] functio ... ing); } | semmle.order | 4 |
| tst.js:23:10:23:18 | [VarDecl] usesUsing | semmle.label | [VarDecl] usesUsing |
| tst.js:23:22:29:1 | [BlockStmt] { u ... ing); } | semmle.label | [BlockStmt] { u ... ing); } |
| tst.js:24:5:24:9 | [VarRef] using | semmle.label | [VarRef] using |
@@ -77,6 +80,8 @@ edges
| file://:0:0:0:0 | (Arguments) | tst.js:28:11:28:15 | [VarRef] using | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | tst.js:25:20:25:22 | [SimpleParameter] foo | semmle.label | 0 |
| file://:0:0:0:0 | (Parameters) | tst.js:25:20:25:22 | [SimpleParameter] foo | semmle.order | 0 |
| tst.html:1:5:1:9 | [ExprStmt] using | tst.html:1:5:1:9 | [VarRef] using | semmle.label | 1 |
| tst.html:1:5:1:9 | [ExprStmt] using | tst.html:1:5:1:9 | [VarRef] using | semmle.order | 1 |
| tst.js:1:1:10:1 | [FunctionDeclStmt] functio ... } } | tst.js:1:10:1:10 | [VarDecl] g | semmle.label | 0 |
| tst.js:1:1:10:1 | [FunctionDeclStmt] functio ... } } | tst.js:1:10:1:10 | [VarDecl] g | semmle.order | 0 |
| tst.js:1:1:10:1 | [FunctionDeclStmt] functio ... } } | tst.js:1:14:10:1 | [BlockStmt] { u ... } } | semmle.label | 5 |

View File

@@ -0,0 +1,2 @@
<%= using %>
<!-- the above should not crash-->

View File

@@ -1,3 +0,0 @@
import javascript
query predicate test_getACallee(DataFlow::InvokeNode c, Function res) { res = c.getACallee() }

View File

@@ -1,5 +0,0 @@
import javascript
query predicate test_getAFunctionValue(DataFlow::Node node, DataFlow::FunctionNode res) {
res = node.getAFunctionValue()
}

View File

@@ -1,5 +0,0 @@
import javascript
query predicate test_getAnArgument(DataFlow::InvokeNode invk, DataFlow::Node res) {
res = invk.getAnArgument()
}

View File

@@ -1,5 +0,0 @@
import javascript
query predicate test_getArgument(DataFlow::InvokeNode invk, int i, DataFlow::Node res) {
res = invk.getArgument(i)
}

View File

@@ -1,5 +0,0 @@
import javascript
query predicate test_getCalleeName(DataFlow::InvokeNode invk, string res) {
res = invk.getCalleeName()
}

View File

@@ -1,5 +0,0 @@
import javascript
query predicate test_getCalleeNode(DataFlow::InvokeNode invk, DataFlow::Node res) {
res = invk.getCalleeNode()
}

View File

@@ -1,5 +0,0 @@
import javascript
query predicate test_getLastArgument(DataFlow::InvokeNode invk, DataFlow::Node res) {
res = invk.getLastArgument()
}

View File

@@ -1,5 +0,0 @@
import javascript
query predicate test_getNumArgument(DataFlow::InvokeNode invk, int res) {
res = invk.getNumArgument()
}

View File

@@ -1,3 +0,0 @@
import javascript
query predicate test_isImprecise(DataFlow::InvokeNode invk) { invk.isImprecise() }

View File

@@ -1,3 +0,0 @@
import javascript
query predicate test_isIncomplete(DataFlow::InvokeNode invk) { invk.isIncomplete() }

View File

@@ -1,3 +0,0 @@
import javascript
query predicate test_isUncertain(DataFlow::InvokeNode invk) { invk.isUncertain() }

View File

@@ -0,0 +1,5 @@
function fooTag(strings, par1, par2) {
}
fooTag`hello ${arg1} world ${arg2}`

View File

@@ -126,6 +126,8 @@ test_getAFunctionValue
| strict.js:1:1:8:2 | (functi ... ode.\\n}) | strict.js:1:2:8:1 | functio ... mode.\\n} |
| strict.js:1:2:8:1 | functio ... mode.\\n} | strict.js:1:2:8:1 | functio ... mode.\\n} |
| strict.js:3:5:5:5 | functio ... ;\\n } | strict.js:3:5:5:5 | functio ... ;\\n } |
| taggedTemplate.js:1:1:3:1 | functio ... 2) {\\n\\n} | taggedTemplate.js:1:1:3:1 | functio ... 2) {\\n\\n} |
| taggedTemplate.js:5:1:5:6 | fooTag | taggedTemplate.js:1:1:3:1 | functio ... 2) {\\n\\n} |
| tst3.js:1:1:1:22 | functio ... fn() {} | tst3.js:1:1:1:22 | functio ... fn() {} |
| tst3.js:2:1:2:23 | functio ... n2() {} | tst3.js:2:1:2:23 | functio ... n2() {} |
| tst.js:1:1:1:15 | function f() {} | tst.js:1:1:1:15 | function f() {} |
@@ -221,6 +223,8 @@ test_getArgument
| reflection.js:7:1:7:22 | reflective call | 1 | reflection.js:7:20:7:21 | 19 |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | 0 | reflection.js:8:11:8:14 | null |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | 1 | reflection.js:8:17:8:24 | [23, 19] |
| taggedTemplate.js:5:1:5:35 | fooTag` ... {arg2}` | 1 | taggedTemplate.js:5:16:5:19 | arg1 |
| taggedTemplate.js:5:1:5:35 | fooTag` ... {arg2}` | 2 | taggedTemplate.js:5:30:5:33 | arg2 |
| tst.js:22:1:22:4 | l(k) | 0 | tst.js:22:3:22:3 | k |
| tst.js:42:2:42:29 | functio ... x; }(o) | 0 | tst.js:42:28:42:28 | o |
test_getNumArgument
@@ -259,6 +263,7 @@ test_getNumArgument
| strict2.js:9:10:9:14 | foo() | 0 |
| strict.js:1:1:8:4 | (functi ... e.\\n})() | 0 |
| strict.js:7:10:7:14 | foo() | 0 |
| taggedTemplate.js:5:1:5:35 | fooTag` ... {arg2}` | 3 |
| tst.js:6:1:6:3 | f() | 0 |
| tst.js:7:1:7:3 | g() | 0 |
| tst.js:8:1:8:3 | h() | 0 |
@@ -362,6 +367,7 @@ test_getCalleeNode
| strict2.js:9:10:9:14 | foo() | strict2.js:9:10:9:12 | foo |
| strict.js:1:1:8:4 | (functi ... e.\\n})() | strict.js:1:1:8:2 | (functi ... ode.\\n}) |
| strict.js:7:10:7:14 | foo() | strict.js:7:10:7:12 | foo |
| taggedTemplate.js:5:1:5:35 | fooTag` ... {arg2}` | taggedTemplate.js:5:1:5:6 | fooTag |
| tst.js:6:1:6:3 | f() | tst.js:6:1:6:1 | f |
| tst.js:7:1:7:3 | g() | tst.js:7:1:7:1 | g |
| tst.js:8:1:8:3 | h() | tst.js:8:1:8:1 | h |
@@ -400,6 +406,7 @@ test_getLastArgument
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | reflection.js:7:20:7:21 | 19 |
| reflection.js:7:1:7:22 | reflective call | reflection.js:7:20:7:21 | 19 |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:8:17:8:24 | [23, 19] |
| taggedTemplate.js:5:1:5:35 | fooTag` ... {arg2}` | taggedTemplate.js:5:30:5:33 | arg2 |
| tst.js:22:1:22:4 | l(k) | tst.js:22:3:22:3 | k |
| tst.js:42:2:42:29 | functio ... x; }(o) | tst.js:42:28:42:28 | o |
test_getAnArgument
@@ -420,6 +427,8 @@ test_getAnArgument
| reflection.js:7:1:7:22 | reflective call | reflection.js:7:20:7:21 | 19 |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:8:11:8:14 | null |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:8:17:8:24 | [23, 19] |
| taggedTemplate.js:5:1:5:35 | fooTag` ... {arg2}` | taggedTemplate.js:5:16:5:19 | arg1 |
| taggedTemplate.js:5:1:5:35 | fooTag` ... {arg2}` | taggedTemplate.js:5:30:5:33 | arg2 |
| tst.js:22:1:22:4 | l(k) | tst.js:22:3:22:3 | k |
| tst.js:42:2:42:29 | functio ... x; }(o) | tst.js:42:28:42:28 | o |
test_getACallee
@@ -449,6 +458,7 @@ test_getACallee
| reflection.js:8:1:8:25 | reflective call | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| strict2.js:2:1:10:4 | (functi ... e.\\n})() | strict2.js:2:2:10:1 | functio ... mode.\\n} |
| strict.js:1:1:8:4 | (functi ... e.\\n})() | strict.js:1:2:8:1 | functio ... mode.\\n} |
| taggedTemplate.js:5:1:5:35 | fooTag` ... {arg2}` | taggedTemplate.js:1:1:3:1 | functio ... 2) {\\n\\n} |
| tst.js:6:1:6:3 | f() | tst.js:1:1:1:15 | function f() {} |
| tst.js:7:1:7:3 | g() | tst.js:2:9:2:21 | function() {} |
| tst.js:8:1:8:3 | h() | tst.js:3:5:3:17 | function() {} |
@@ -509,6 +519,7 @@ test_getCalleeName
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | apply |
| strict2.js:9:10:9:14 | foo() | foo |
| strict.js:7:10:7:14 | foo() | foo |
| taggedTemplate.js:5:1:5:35 | fooTag` ... {arg2}` | fooTag |
| tst.js:6:1:6:3 | f() | f |
| tst.js:7:1:7:3 | g() | g |
| tst.js:8:1:8:3 | h() | h |

View File

@@ -1,11 +1,37 @@
import isUncertain
import getAFunctionValue
import getArgument
import getNumArgument
import isIncomplete
import getCalleeNode
import getLastArgument
import getAnArgument
import getACallee
import getCalleeName
import isImprecise
import javascript
query predicate test_isUncertain(DataFlow::InvokeNode invk) { invk.isUncertain() }
query predicate test_getAFunctionValue(DataFlow::Node node, DataFlow::FunctionNode res) {
res = node.getAFunctionValue()
}
query predicate test_getArgument(DataFlow::InvokeNode invk, int i, DataFlow::Node res) {
res = invk.getArgument(i)
}
query predicate test_getNumArgument(DataFlow::InvokeNode invk, int res) {
res = invk.getNumArgument()
}
query predicate test_isIncomplete(DataFlow::InvokeNode invk) { invk.isIncomplete() }
query predicate test_getCalleeNode(DataFlow::InvokeNode invk, DataFlow::Node res) {
res = invk.getCalleeNode()
}
query predicate test_getLastArgument(DataFlow::InvokeNode invk, DataFlow::Node res) {
res = invk.getLastArgument()
}
query predicate test_getAnArgument(DataFlow::InvokeNode invk, DataFlow::Node res) {
res = invk.getAnArgument()
}
query predicate test_getACallee(DataFlow::InvokeNode c, Function res) { res = c.getACallee() }
query predicate test_getCalleeName(DataFlow::InvokeNode invk, string res) {
res = invk.getCalleeName()
}
query predicate test_isImprecise(DataFlow::InvokeNode invk) { invk.isImprecise() }

View File

@@ -231,6 +231,7 @@ typeInferenceMismatch
| tst.js:2:13:2:20 | source() | tst.js:47:10:47:30 | Buffer. ... 'hex') |
| tst.js:2:13:2:20 | source() | tst.js:48:10:48:22 | new Buffer(x) |
| tst.js:2:13:2:20 | source() | tst.js:51:10:51:31 | seriali ... ript(x) |
| tst.js:2:13:2:20 | source() | tst.js:54:14:54:19 | unsafe |
| xml.js:5:18:5:25 | source() | xml.js:8:14:8:17 | text |
| xml.js:12:17:12:24 | source() | xml.js:13:14:13:19 | result |
| xml.js:23:18:23:25 | source() | xml.js:20:14:20:17 | attr |

View File

@@ -109,3 +109,4 @@
| thisAssignments.js:4:17:4:24 | source() | thisAssignments.js:5:10:5:18 | obj.field |
| thisAssignments.js:7:19:7:26 | source() | thisAssignments.js:8:10:8:20 | this.field2 |
| tst.js:2:13:2:20 | source() | tst.js:4:10:4:10 | x |
| tst.js:2:13:2:20 | source() | tst.js:54:14:54:19 | unsafe |

View File

@@ -49,4 +49,12 @@ function test() {
const serializeJavaScript = require("serialize-javascript");
sink(serializeJavaScript(x)) // NOT OK
function tagged(strings, safe, unsafe) {
sink(unsafe) // NOT OK
sink(safe) // OK
sink(strings) // OK
}
tagged`foo ${"safe"} bar ${x} baz`;
}

View File

@@ -1,4 +1,7 @@
nodes
| ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] |
| ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] |
| ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] |
| express.js:7:16:7:34 | req.param("target") |
| express.js:7:16:7:34 | req.param("target") |
| express.js:7:16:7:34 | req.param("target") |
@@ -114,6 +117,7 @@ nodes
| react-native.js:9:26:9:32 | tainted |
| react-native.js:9:26:9:32 | tainted |
edges
| ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] | ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] |
| express.js:7:16:7:34 | req.param("target") | express.js:7:16:7:34 | req.param("target") |
| express.js:12:26:12:44 | req.param("target") | express.js:12:26:12:44 | req.param("target") |
| express.js:27:7:27:34 | target | express.js:33:18:33:23 | target |
@@ -211,6 +215,7 @@ edges
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
#select
| ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] | ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] | ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] | Untrusted URL redirection depends on a $@. | ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] | user-provided value |
| express.js:7:16:7:34 | req.param("target") | express.js:7:16:7:34 | req.param("target") | express.js:7:16:7:34 | req.param("target") | Untrusted URL redirection depends on a $@. | express.js:7:16:7:34 | req.param("target") | user-provided value |
| express.js:12:26:12:44 | req.param("target") | express.js:12:26:12:44 | req.param("target") | express.js:12:26:12:44 | req.param("target") | Untrusted URL redirection depends on a $@. | express.js:12:26:12:44 | req.param("target") | user-provided value |
| express.js:33:18:33:23 | target | express.js:27:16:27:34 | req.param("target") | express.js:33:18:33:23 | target | Untrusted URL redirection depends on a $@. | express.js:27:16:27:34 | req.param("target") | user-provided value |

View File

@@ -0,0 +1,6 @@
const app = require("express")();
app.get("/redirect", function (req, res) {
// BAD: a request parameter is incorporated without validation into a URL redirect
res.redirect(req.query["target"]);
});

View File

@@ -0,0 +1,13 @@
const app = require("express")();
const VALID_REDIRECT = "http://cwe.mitre.org/data/definitions/601.html";
app.get("/redirect", function (req, res) {
// GOOD: the request parameter is validated against a known fixed string
let target = req.query["target"];
if (VALID_REDIRECT === target) {
res.redirect(target);
} else {
res.redirect("/");
}
});

View File

@@ -0,0 +1,22 @@
const app = require("express")();
function isLocalUrl(path) {
try {
return (
// TODO: consider substituting your own domain for example.com
new URL(path, "https://example.com").origin === "https://example.com"
);
} catch (e) {
return false;
}
}
app.get("/redirect", function (req, res) {
// GOOD: check that we don't redirect to a different host
let target = req.query["target"];
if (isLocalUrl(target)) {
res.redirect(target);
} else {
res.redirect("/");
}
});