Merge branch 'main' into labelNaming

This commit is contained in:
Erik Krogh Kristensen
2022-04-22 13:19:44 +02:00
732 changed files with 24834 additions and 22975 deletions

View File

@@ -106,9 +106,9 @@ predicate isKnownLibrarySink(DataFlow::Node n) {
* Holds if the node `n` is known as the predecessor in a modeled flow step.
*/
predicate isKnownStepSrc(DataFlow::Node n) {
any(TaintTracking::AdditionalTaintStep s).step(n, _) or
any(DataFlow::AdditionalFlowStep s).step(n, _) or
any(DataFlow::AdditionalFlowStep s).step(n, _, _, _)
TaintTracking::sharedTaintStep(n, _) or
DataFlow::SharedFlowStep::step(n, _) or
DataFlow::SharedFlowStep::step(n, _, _, _)
}
/**

View File

@@ -75,7 +75,11 @@ private DataFlow::Node getANotASink(NotASinkReason reason) {
*/
private DataFlow::Node getAnUnknown(Query query) {
getAtmCfg(query).isEffectiveSink(result) and
// Effective sinks should exclude sinks but this is a defensive requirement
not result = getASink(query) and
// Effective sinks should exclude NotASink but for some queries (e.g. Xss) this is currently not always the case and
// so this is a defensive requirement
not result = getANotASink(_) and
// Only consider the source code for the project being analyzed.
exists(result.getFile().getRelativePath())
}

View File

@@ -0,0 +1 @@
**/*.testproj

View File

@@ -8434,24 +8434,24 @@ tokenFeatures
| autogenerated/Xss/DomBasedXss/d3.js:14:15:14:29 | d => getTaint() | enclosingFunctionBody | d3 select #main attr width 100 style color red html getTaint html d getTaint call otherFunction html d getTaint |
| autogenerated/Xss/DomBasedXss/d3.js:14:15:14:29 | d => getTaint() | enclosingFunctionName | doSomething |
| autogenerated/Xss/DomBasedXss/d3.js:20:15:20:19 | 'foo' | argumentIndex | 0 |
| autogenerated/Xss/DomBasedXss/d3.js:20:15:20:19 | 'foo' | calleeAccessPath | d3 select attr style html html call selection attr d3 select attr style html html selection attr |
| autogenerated/Xss/DomBasedXss/d3.js:20:15:20:19 | 'foo' | calleeAccessPathWithStructuralInfo | d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn functionalarg param selection member attr instanceorreturn d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn member call functionalarg param selection member attr instanceorreturn |
| autogenerated/Xss/DomBasedXss/d3.js:20:15:20:19 | 'foo' | calleeAccessPath | d3 select attr style html html call selection attr |
| autogenerated/Xss/DomBasedXss/d3.js:20:15:20:19 | 'foo' | calleeAccessPathWithStructuralInfo | d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn member call functionalarg param selection member attr instanceorreturn |
| autogenerated/Xss/DomBasedXss/d3.js:20:15:20:19 | 'foo' | calleeApiName | d3 |
| autogenerated/Xss/DomBasedXss/d3.js:20:15:20:19 | 'foo' | calleeName | attr |
| autogenerated/Xss/DomBasedXss/d3.js:20:15:20:19 | 'foo' | enclosingFunctionBody | selection selection attr foo bar html getTaint |
| autogenerated/Xss/DomBasedXss/d3.js:20:15:20:19 | 'foo' | enclosingFunctionName | otherFunction |
| autogenerated/Xss/DomBasedXss/d3.js:20:15:20:19 | 'foo' | receiverName | selection |
| autogenerated/Xss/DomBasedXss/d3.js:20:22:20:26 | 'bar' | argumentIndex | 1 |
| autogenerated/Xss/DomBasedXss/d3.js:20:22:20:26 | 'bar' | calleeAccessPath | d3 select attr style html html call selection attr d3 select attr style html html selection attr |
| autogenerated/Xss/DomBasedXss/d3.js:20:22:20:26 | 'bar' | calleeAccessPathWithStructuralInfo | d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn functionalarg param selection member attr instanceorreturn d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn member call functionalarg param selection member attr instanceorreturn |
| autogenerated/Xss/DomBasedXss/d3.js:20:22:20:26 | 'bar' | calleeAccessPath | d3 select attr style html html call selection attr |
| autogenerated/Xss/DomBasedXss/d3.js:20:22:20:26 | 'bar' | calleeAccessPathWithStructuralInfo | d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn member call functionalarg param selection member attr instanceorreturn |
| autogenerated/Xss/DomBasedXss/d3.js:20:22:20:26 | 'bar' | calleeApiName | d3 |
| autogenerated/Xss/DomBasedXss/d3.js:20:22:20:26 | 'bar' | calleeName | attr |
| autogenerated/Xss/DomBasedXss/d3.js:20:22:20:26 | 'bar' | enclosingFunctionBody | selection selection attr foo bar html getTaint |
| autogenerated/Xss/DomBasedXss/d3.js:20:22:20:26 | 'bar' | enclosingFunctionName | otherFunction |
| autogenerated/Xss/DomBasedXss/d3.js:20:22:20:26 | 'bar' | receiverName | selection |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | argumentIndex | 0 |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeAccessPath | d3 select attr style html html call selection attr html d3 select attr style html html selection attr html |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeAccessPathWithStructuralInfo | d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn functionalarg param selection member attr instanceorreturn member html instanceorreturn d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn member call functionalarg param selection member attr instanceorreturn member html instanceorreturn |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeAccessPath | d3 select attr style html html call selection attr html |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeAccessPathWithStructuralInfo | d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn member call functionalarg param selection member attr instanceorreturn member html instanceorreturn |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeApiName | d3 |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeName | html |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | enclosingFunctionBody | selection selection attr foo bar html getTaint |
@@ -9926,6 +9926,10 @@ tokenFeatures
| autogenerated/Xss/DomBasedXss/string-manipulations.js:12:16:12:61 | escape( ... href))) | calleeAccessPathWithStructuralInfo | |
| autogenerated/Xss/DomBasedXss/string-manipulations.js:12:16:12:61 | escape( ... href))) | calleeName | write |
| autogenerated/Xss/DomBasedXss/string-manipulations.js:12:16:12:61 | escape( ... href))) | receiverName | document |
| autogenerated/Xss/DomBasedXss/translate.js:7:42:7:60 | target.substring(1) | calleeAccessPath | |
| autogenerated/Xss/DomBasedXss/translate.js:7:42:7:60 | target.substring(1) | calleeAccessPathWithStructuralInfo | |
| autogenerated/Xss/DomBasedXss/translate.js:7:42:7:60 | target.substring(1) | enclosingFunctionBody | translate own goal backpass fumble feint target document location search searchParams URLSearchParams target substring 1 $ original-term html searchParams get term $ translated-term html translate searchParams get term |
| autogenerated/Xss/DomBasedXss/translate.js:7:42:7:60 | target.substring(1) | enclosingFunctionName | |
| autogenerated/Xss/DomBasedXss/translate.js:7:59:7:59 | 1 | argumentIndex | 0 |
| autogenerated/Xss/DomBasedXss/translate.js:7:59:7:59 | 1 | calleeAccessPath | |
| autogenerated/Xss/DomBasedXss/translate.js:7:59:7:59 | 1 | calleeAccessPathWithStructuralInfo | |
@@ -10213,6 +10217,10 @@ tokenFeatures
| autogenerated/Xss/DomBasedXss/tst.js:18:29:18:34 | 'name' | enclosingFunctionBody | target document location search $ myId html target document write <OPTION value=1> document location href substring document location href indexOf default= 8 </OPTION> document write <OPTION value=2>English</OPTION> $ <div style="width: target px"> $ <div style="width: target px"> $ <div style="width: parseInt target px"> params URL document location searchParams $ name html params get name searchParams URLSearchParams target substring 1 $ name html searchParams get name |
| autogenerated/Xss/DomBasedXss/tst.js:18:29:18:34 | 'name' | enclosingFunctionName | test |
| autogenerated/Xss/DomBasedXss/tst.js:18:29:18:34 | 'name' | receiverName | params |
| autogenerated/Xss/DomBasedXss/tst.js:20:42:20:60 | target.substring(1) | calleeAccessPath | |
| autogenerated/Xss/DomBasedXss/tst.js:20:42:20:60 | target.substring(1) | calleeAccessPathWithStructuralInfo | |
| autogenerated/Xss/DomBasedXss/tst.js:20:42:20:60 | target.substring(1) | enclosingFunctionBody | target document location search $ myId html target document write <OPTION value=1> document location href substring document location href indexOf default= 8 </OPTION> document write <OPTION value=2>English</OPTION> $ <div style="width: target px"> $ <div style="width: target px"> $ <div style="width: parseInt target px"> params URL document location searchParams $ name html params get name searchParams URLSearchParams target substring 1 $ name html searchParams get name |
| autogenerated/Xss/DomBasedXss/tst.js:20:42:20:60 | target.substring(1) | enclosingFunctionName | test |
| autogenerated/Xss/DomBasedXss/tst.js:20:59:20:59 | 1 | argumentIndex | 0 |
| autogenerated/Xss/DomBasedXss/tst.js:20:59:20:59 | 1 | calleeAccessPath | |
| autogenerated/Xss/DomBasedXss/tst.js:20:59:20:59 | 1 | calleeAccessPathWithStructuralInfo | |
@@ -12661,6 +12669,13 @@ tokenFeatures
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:23:12:23:27 | marked(req.body) | enclosingFunctionBody | req res res send req body res send marked req body |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:23:12:23:27 | marked(req.body) | enclosingFunctionName | app.get#functionalargument |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:23:12:23:27 | marked(req.body) | receiverName | res |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:23:19:23:26 | req.body | argumentIndex | 0 |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:23:19:23:26 | req.body | calleeAccessPath | marked |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:23:19:23:26 | req.body | calleeAccessPathWithStructuralInfo | marked instanceorreturn |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:23:19:23:26 | req.body | calleeApiName | marked |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:23:19:23:26 | req.body | calleeName | marked |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:23:19:23:26 | req.body | enclosingFunctionBody | req res res send req body res send marked req body |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:23:19:23:26 | req.body | enclosingFunctionName | app.get#functionalargument |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:27:21:27:36 | 'markdown-table' | argumentIndex | 0 |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:27:21:27:36 | 'markdown-table' | calleeAccessPath | |
| autogenerated/Xss/ReflectedXss/ReflectedXss.js:27:21:27:36 | 'markdown-table' | calleeAccessPathWithStructuralInfo | |

View File

@@ -9393,8 +9393,8 @@ tokenFeatures
| autogenerated/Xss/DomBasedXss/d3.js:14:20:14:29 | getTaint() | enclosingFunctionName | doSomething |
| autogenerated/Xss/DomBasedXss/d3.js:14:20:14:29 | getTaint() | receiverName | |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | argumentIndex | 0 |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeAccessPath | d3 select attr style html html call selection attr html d3 select attr style html html selection attr html |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeAccessPathWithStructuralInfo | d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn functionalarg param selection member attr instanceorreturn member html instanceorreturn d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn member call functionalarg param selection member attr instanceorreturn member html instanceorreturn |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeAccessPath | d3 select attr style html html call selection attr html |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeAccessPathWithStructuralInfo | d3 member select instanceorreturn member attr instanceorreturn member style instanceorreturn member html instanceorreturn member html instanceorreturn member call functionalarg param selection member attr instanceorreturn member html instanceorreturn |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeApiName | d3 |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | calleeName | html |
| autogenerated/Xss/DomBasedXss/d3.js:21:15:21:24 | getTaint() | enclosingFunctionBody | selection selection attr foo bar html getTaint |

View File

@@ -251,6 +251,50 @@ endpoints
| index.js:78:30:78:39 | "someData" | Xss | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:78:30:78:39 | "someData" | Xss | notASinkReason | LoggerMethod | string |
| index.js:78:30:78:39 | "someData" | Xss | sinkLabel | NotASink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | sinkLabel | NotASink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | sinkLabel | NotASink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | sinkLabel | NotASink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | SqlInjection | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | SqlInjection | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | SqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | SqlInjection | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | SqlInjection | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | TaintedPath | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | TaintedPath | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | TaintedPath | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | TaintedPath | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | TaintedPath | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | Xss | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | Xss | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | Xss | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | Xss | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | Xss | sinkLabel | NotASink | string |
tokenFeatures
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | argumentIndex | 0 |
| applications/examples/static/epydoc/epydoc.js:2:15:2:33 | "Should be ignored" | calleeAccessPath | |
@@ -412,3 +456,19 @@ tokenFeatures
| index.js:78:30:78:39 | "someData" | enclosingFunctionBody | console log someData |
| index.js:78:30:78:39 | "someData" | enclosingFunctionName | identity#functionalargument |
| index.js:78:30:78:39 | "someData" | receiverName | console |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | argumentIndex | 0 |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPath | |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPathWithStructuralInfo | |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeApiName | |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeName | ajax |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionBody | foo $ ajax url foo bar |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionName | effectiveSinkAndNotASink |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | receiverName | $ |
| index.js:84:12:84:18 | foo.bar | argumentIndex | |
| index.js:84:12:84:18 | foo.bar | calleeAccessPath | |
| index.js:84:12:84:18 | foo.bar | calleeAccessPathWithStructuralInfo | |
| index.js:84:12:84:18 | foo.bar | calleeApiName | |
| index.js:84:12:84:18 | foo.bar | calleeName | |
| index.js:84:12:84:18 | foo.bar | enclosingFunctionBody | foo $ ajax url foo bar |
| index.js:84:12:84:18 | foo.bar | enclosingFunctionName | effectiveSinkAndNotASink |
| index.js:84:12:84:18 | foo.bar | receiverName | |

View File

@@ -231,6 +231,50 @@ endpoints
| index.js:78:30:78:39 | "someData" | Xss | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:78:30:78:39 | "someData" | Xss | notASinkReason | LoggerMethod | string |
| index.js:78:30:78:39 | "someData" | Xss | sinkLabel | NotASink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | sinkLabel | NotASink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | sinkLabel | NotASink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | sinkLabel | NotASink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | SqlInjection | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | SqlInjection | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | SqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | SqlInjection | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | SqlInjection | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | TaintedPath | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | TaintedPath | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | TaintedPath | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | TaintedPath | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | TaintedPath | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | Xss | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | Xss | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | Xss | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | Xss | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | Xss | sinkLabel | NotASink | string |
tokenFeatures
| index.js:1:25:1:33 | "express" | argumentIndex | 0 |
| index.js:1:25:1:33 | "express" | calleeAccessPath | |
@@ -384,3 +428,19 @@ tokenFeatures
| index.js:78:30:78:39 | "someData" | enclosingFunctionBody | console log someData |
| index.js:78:30:78:39 | "someData" | enclosingFunctionName | identity#functionalargument |
| index.js:78:30:78:39 | "someData" | receiverName | console |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | argumentIndex | 0 |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPath | |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPathWithStructuralInfo | |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeApiName | |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeName | ajax |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionBody | foo $ ajax url foo bar |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionName | effectiveSinkAndNotASink |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | receiverName | $ |
| index.js:84:12:84:18 | foo.bar | argumentIndex | |
| index.js:84:12:84:18 | foo.bar | calleeAccessPath | |
| index.js:84:12:84:18 | foo.bar | calleeAccessPathWithStructuralInfo | |
| index.js:84:12:84:18 | foo.bar | calleeApiName | |
| index.js:84:12:84:18 | foo.bar | calleeName | |
| index.js:84:12:84:18 | foo.bar | enclosingFunctionBody | foo $ ajax url foo bar |
| index.js:84:12:84:18 | foo.bar | enclosingFunctionName | effectiveSinkAndNotASink |
| index.js:84:12:84:18 | foo.bar | receiverName | |

View File

@@ -31,6 +31,50 @@ endpoints
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | NosqlInjection | isConstantExpression | false | boolean |
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | NosqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | NosqlInjection | sinkLabel | Sink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | NosqlInjection | sinkLabel | NotASink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | SqlInjection | sinkLabel | NotASink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | TaintedPath | sinkLabel | NotASink | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | hasFlowFromSource | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | isConstantExpression | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | notASinkReason | ClientRequest | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | notASinkReason | JQueryArgument | string |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | Xss | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | NosqlInjection | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | SqlInjection | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | SqlInjection | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | SqlInjection | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | SqlInjection | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | SqlInjection | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | TaintedPath | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | TaintedPath | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | TaintedPath | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | TaintedPath | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | TaintedPath | sinkLabel | NotASink | string |
| index.js:84:12:84:18 | foo.bar | Xss | hasFlowFromSource | false | boolean |
| index.js:84:12:84:18 | foo.bar | Xss | isConstantExpression | false | boolean |
| index.js:84:12:84:18 | foo.bar | Xss | isExcludedFromEndToEndEvaluation | false | boolean |
| index.js:84:12:84:18 | foo.bar | Xss | notASinkReason | ClientRequest | string |
| index.js:84:12:84:18 | foo.bar | Xss | sinkLabel | NotASink | string |
tokenFeatures
| index.js:9:15:9:45 | { 'isAd ... Admin } | argumentIndex | 0 |
| index.js:9:15:9:45 | { 'isAd ... Admin } | calleeAccessPath | mongoose model find |
@@ -64,3 +108,19 @@ tokenFeatures
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | enclosingFunctionBody | User find UNDEFINED_GLOBAL |
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | enclosingFunctionName | notConstantExpression |
| index.js:28:13:28:28 | UNDEFINED_GLOBAL | receiverName | User |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | argumentIndex | 0 |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPath | |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeAccessPathWithStructuralInfo | |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeApiName | |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | calleeName | ajax |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionBody | foo $ ajax url foo bar |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | enclosingFunctionName | effectiveSinkAndNotASink |
| index.js:83:10:85:3 | {\\n " ... ar,\\n } | receiverName | $ |
| index.js:84:12:84:18 | foo.bar | argumentIndex | |
| index.js:84:12:84:18 | foo.bar | calleeAccessPath | |
| index.js:84:12:84:18 | foo.bar | calleeAccessPathWithStructuralInfo | |
| index.js:84:12:84:18 | foo.bar | calleeApiName | |
| index.js:84:12:84:18 | foo.bar | calleeName | |
| index.js:84:12:84:18 | foo.bar | enclosingFunctionBody | foo $ ajax url foo bar |
| index.js:84:12:84:18 | foo.bar | enclosingFunctionName | effectiveSinkAndNotASink |
| index.js:84:12:84:18 | foo.bar | receiverName | |

View File

@@ -77,3 +77,10 @@ function veryLongFunctionBody() {
// We should name the anonymous function here that's passed as an argument to `identity`.
identity(() => console.log("someData"));
}
function effectiveSinkAndNotASink(foo) {
$.ajax({
"url": foo.bar,
});
}

View File

@@ -1,4 +1,3 @@
| index.js:5:23:5:52 | "mongod ... ePort/" |
| index.js:5:55:15:3 | (err, d ... });\\n } |
| index.js:10:79:14:5 | (err, r ... ;\\n } |
| index.js:20:21:20:31 | window.name |

View File

@@ -1,3 +1,20 @@
## 0.0.14
## 0.0.13
### Deprecated APIs
* Some predicates from `DefUse.qll`, `DataFlow.qll`, `TaintTracking.qll`, `DOM.qll`, `Definitions.qll` that weren't used by any query have been deprecated.
The documentation for each predicate points to an alternative.
* Many classes/predicates/modules that had upper-case acronyms have been renamed to follow our style-guide.
The old name still exists as a deprecated alias.
* Some modules that started with a lowercase letter have been renamed to follow our style-guide.
The old name still exists as a deprecated alias.
### Minor Analysis Improvements
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
## 0.0.12
### Major Analysis Improvements

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.

View File

@@ -1,5 +0,0 @@
---
category: deprecated
---
* Many classes/predicates/modules that had upper-case acronyms have been renamed to follow our style-guide.
The old name still exists as a deprecated alias.

View File

@@ -1,5 +0,0 @@
---
category: deprecated
---
* Some modules that started with a lowercase letter have been renamed to follow our style-guide.
The old name still exists as a deprecated alias.

View File

@@ -1,5 +0,0 @@
---
category: deprecated
---
* Some predicates from `DefUse.qll`, `DataFlow.qll`, `TaintTracking.qll`, `DOM.qll`, `Definitions.qll` that weren't used by any query have been deprecated.
The documentation for each predicate points to an alternative.

View File

@@ -0,0 +1,14 @@
## 0.0.13
### Deprecated APIs
* Some predicates from `DefUse.qll`, `DataFlow.qll`, `TaintTracking.qll`, `DOM.qll`, `Definitions.qll` that weren't used by any query have been deprecated.
The documentation for each predicate points to an alternative.
* Many classes/predicates/modules that had upper-case acronyms have been renamed to follow our style-guide.
The old name still exists as a deprecated alias.
* Some modules that started with a lowercase letter have been renamed to follow our style-guide.
The old name still exists as a deprecated alias.
### Minor Analysis Improvements
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.

View File

@@ -0,0 +1 @@
## 0.0.14

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.12
lastReleaseVersion: 0.0.14

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-all
version: 0.0.13-dev
version: 0.1.0-dev
groups: javascript
dbscheme: semmlecode.javascript.dbscheme
extractor: javascript

View File

@@ -230,6 +230,72 @@ module API {
result = this.getASuccessor(Label::promisedError())
}
/**
* Gets any class that has this value as a decorator.
*
* For example:
* ```js
* import { D } from "foo";
*
* // moduleImport("foo").getMember("D").getADecoratedClass()
* @D
* class C1 {}
*
* // moduleImport("foo").getMember("D").getReturn().getADecoratedClass()
* @D()
* class C2 {}
* ```
*/
cached
Node getADecoratedClass() { result = this.getASuccessor(Label::decoratedClass()) }
/**
* Gets any method, field, or accessor that has this value as a decorator.
*
* In the case of an accessor, this gets the return value of a getter, or argument to a setter.
*
* For example:
* ```js
* import { D } from "foo";
*
* class C {
* // moduleImport("foo").getMember("D").getADecoratedMember()
* @D m1() {}
* @D f;
* @D get g() { return this.x; }
*
* // moduleImport("foo").getMember("D").getReturn().getADecoratedMember()
* @D() m2() {}
* @D() f2;
* @D() get g2() { return this.x; }
* }
* ```
*/
cached
Node getADecoratedMember() { result = this.getASuccessor(Label::decoratedMember()) }
/**
* Gets any parameter that has this value as a decorator.
*
* For example:
* ```js
* import { D } from "foo";
*
* class C {
* method(
* // moduleImport("foo").getMember("D").getADecoratedParameter()
* @D
* param1,
* // moduleImport("foo").getMember("D").getReturn().getADecoratedParameter()
* @D()
* param2
* ) {}
* }
* ```
*/
cached
Node getADecoratedParameter() { result = this.getASuccessor(Label::decoratedParameter()) }
/**
* Gets a string representation of the lexicographically least among all shortest access paths
* from the root to this node.
@@ -570,6 +636,15 @@ module API {
lbl = Label::memberFromRef(pw)
)
)
or
decoratorDualEdge(base, lbl, rhs)
or
decoratorRhsEdge(base, lbl, rhs)
or
exists(DataFlow::PropWrite write |
decoratorPropEdge(base, lbl, write) and
rhs = write.getRhs()
)
}
/**
@@ -699,6 +774,98 @@ module API {
lbl = Label::parameter(1) and
ref = awaited(call)
)
or
decoratorDualEdge(base, lbl, ref)
or
decoratorUseEdge(base, lbl, ref)
or
// for fields and accessors, mark the reads as use-nodes
decoratorPropEdge(base, lbl, ref.(DataFlow::PropRead))
)
}
/** Holds if `base` is a use-node that flows to the decorator expression of the given decorator. */
pragma[nomagic]
private predicate useNodeFlowsToDecorator(TApiNode base, Decorator decorator) {
exists(DataFlow::SourceNode decoratorSrc |
use(base, decoratorSrc) and
trackUseNode(decoratorSrc).flowsToExpr(decorator.getExpression())
)
}
/**
* Holds if `ref` corresponds to both a use and def-node that should have an incoming edge from `base` labelled `lbl`.
*
* This happens because the decorated value escapes into the decorator function, and is then replaced
* by the function's return value. In the JS analysis we generally assume decorators return their input,
* but library models may want to find the return value.
*/
private predicate decoratorDualEdge(TApiNode base, Label::ApiLabel lbl, DataFlow::Node ref) {
exists(ClassDefinition cls |
useNodeFlowsToDecorator(base, cls.getADecorator()) and
lbl = Label::decoratedClass() and
ref = DataFlow::valueNode(cls)
)
or
exists(MethodDefinition method |
useNodeFlowsToDecorator(base, method.getADecorator()) and
not method instanceof AccessorMethodDefinition and
lbl = Label::decoratedMember() and
ref = DataFlow::valueNode(method.getBody())
)
}
/** Holds if `ref` is a use that should have an incoming edge from `base` labelled `lbl`, induced by a decorator. */
private predicate decoratorUseEdge(TApiNode base, Label::ApiLabel lbl, DataFlow::Node ref) {
exists(SetterMethodDefinition accessor |
useNodeFlowsToDecorator(base,
[accessor.getADecorator(), accessor.getCorrespondingGetter().getADecorator()]) and
lbl = Label::decoratedMember() and
ref = DataFlow::parameterNode(accessor.getBody().getParameter(0))
)
or
exists(Parameter param |
useNodeFlowsToDecorator(base, param.getADecorator()) and
lbl = Label::decoratedParameter() and
ref = DataFlow::parameterNode(param)
)
}
/** Holds if `rhs` is a def node that should have an incoming edge from `base` labelled `lbl`, induced by a decorator. */
private predicate decoratorRhsEdge(TApiNode base, Label::ApiLabel lbl, DataFlow::Node rhs) {
exists(GetterMethodDefinition accessor |
useNodeFlowsToDecorator(base,
[accessor.getADecorator(), accessor.getCorrespondingSetter().getADecorator()]) and
lbl = Label::decoratedMember() and
rhs = DataFlow::valueNode(accessor.getBody().getAReturnedExpr())
)
}
/**
* Holds if `ref` is a reference to a field/accessor that should have en incoming edge from base labelled `lbl`.
*
* Since fields do not have their own data-flow nodes, we generate a node for each read or write.
* For property writes, the right-hand side becomes a def-node and property reads become use-nodes.
*
* For accessors this predicate computes each use of the accessor.
* The return value inside the accessor is computed by the `decoratorRhsEdge` predicate.
*/
private predicate decoratorPropEdge(TApiNode base, Label::ApiLabel lbl, DataFlow::PropRef ref) {
exists(MemberDefinition fieldLike, DataFlow::ClassNode cls |
fieldLike instanceof FieldDefinition
or
fieldLike instanceof AccessorMethodDefinition
|
useNodeFlowsToDecorator(base, fieldLike.getADecorator()) and
lbl = Label::decoratedMember() and
cls = fieldLike.getDeclaringClass().flow() and
(
fieldLike.isStatic() and
ref = cls.getAClassReference().getAPropertyReference(fieldLike.getName())
or
not fieldLike.isStatic() and
ref = cls.getAnInstanceReference().getAPropertyReference(fieldLike.getName())
)
)
}
@@ -1106,6 +1273,15 @@ module API {
/** Gets the `promisedError` edge label connecting a promise to its rejected value. */
LabelPromisedError promisedError() { any() }
/** Gets the label for an edge leading from a value `D` to any class that has `D` as a decorator. */
LabelDecoratedClass decoratedClass() { any() }
/** Gets the label for an edge leading from a value `D` to any method, field, or accessor that has `D` as a decorator. */
LabelDecoratedMethod decoratedMember() { any() }
/** Gets the label for an edge leading from a value `D` to any parameter that has `D` as a decorator. */
LabelDecoratedParameter decoratedParameter() { any() }
/** Gets an entry-point label for the entry-point `e`. */
LabelEntryPoint entryPoint(API::EntryPoint e) { result.getEntryPoint() = e }
@@ -1140,10 +1316,13 @@ module API {
MkLabelReturn() or
MkLabelPromised() or
MkLabelPromisedError() or
MkLabelDecoratedClass() or
MkLabelDecoratedMember() or
MkLabelDecoratedParameter() or
MkLabelEntryPoint(API::EntryPoint e)
/** A label for an entry-point. */
class LabelEntryPoint extends ApiLabel {
class LabelEntryPoint extends ApiLabel, MkLabelEntryPoint {
API::EntryPoint e;
LabelEntryPoint() { this = MkLabelEntryPoint(e) }
@@ -1155,28 +1334,22 @@ module API {
}
/** A label that gets a promised value. */
class LabelPromised extends ApiLabel {
LabelPromised() { this = MkLabelPromised() }
class LabelPromised extends ApiLabel, MkLabelPromised {
override string toString() { result = "getPromised()" }
}
/** A label that gets a rejected promise. */
class LabelPromisedError extends ApiLabel {
LabelPromisedError() { this = MkLabelPromisedError() }
class LabelPromisedError extends ApiLabel, MkLabelPromisedError {
override string toString() { result = "getPromisedError()" }
}
/** A label that gets the return value of a function. */
class LabelReturn extends ApiLabel {
LabelReturn() { this = MkLabelReturn() }
class LabelReturn extends ApiLabel, MkLabelReturn {
override string toString() { result = "getReturn()" }
}
/** A label for a module. */
class LabelModule extends ApiLabel {
class LabelModule extends ApiLabel, MkLabelModule {
string mod;
LabelModule() { this = MkLabelModule(mod) }
@@ -1189,14 +1362,12 @@ module API {
}
/** A label that gets an instance from a `new` call. */
class LabelInstance extends ApiLabel {
LabelInstance() { this = MkLabelInstance() }
class LabelInstance extends ApiLabel, MkLabelInstance {
override string toString() { result = "getInstance()" }
}
/** A label for the member named `prop`. */
class LabelMember extends ApiLabel {
class LabelMember extends ApiLabel, MkLabelMember {
string prop;
LabelMember() { this = MkLabelMember(prop) }
@@ -1208,14 +1379,14 @@ module API {
}
/** A label for a member with an unknown name. */
class LabelUnknownMember extends ApiLabel {
class LabelUnknownMember extends ApiLabel, MkLabelUnknownMember {
LabelUnknownMember() { this = MkLabelUnknownMember() }
override string toString() { result = "getUnknownMember()" }
}
/** A label for parameter `i`. */
class LabelParameter extends ApiLabel {
class LabelParameter extends ApiLabel, MkLabelParameter {
int i;
LabelParameter() { this = MkLabelParameter(i) }
@@ -1230,6 +1401,21 @@ module API {
class LabelReceiver extends ApiLabel, MkLabelReceiver {
override string toString() { result = "getReceiver()" }
}
/** A label for a class decorated by the current value. */
class LabelDecoratedClass extends ApiLabel, MkLabelDecoratedClass {
override string toString() { result = "getADecoratedClass()" }
}
/** A label for a method, field, or accessor decorated by the current value. */
class LabelDecoratedMethod extends ApiLabel, MkLabelDecoratedMember {
override string toString() { result = "decoratedMember()" }
}
/** A label for a parameter decorated by the current value. */
class LabelDecoratedParameter extends ApiLabel, MkLabelDecoratedParameter {
override string toString() { result = "decoratedParameter()" }
}
}
}
}

View File

@@ -45,9 +45,20 @@ module ArrayTaintTracking {
)
or
// `array.reduce` with tainted value in callback
// The callback parameters are: (previousValue, currentValue, currentIndex, array)
call.(DataFlow::MethodCallNode).getMethodName() = "reduce" and
pred = call.getArgument(0).(DataFlow::FunctionNode).getAReturn() and // Require the argument to be a closure to avoid spurious call/return flow
succ = call
exists(DataFlow::FunctionNode callback |
callback = call.getArgument(0) // Require the argument to be a closure to avoid spurious call/return flow
|
pred = callback.getAReturn() and
succ = call
or
pred = call.getReceiver() and
succ = callback.getParameter([1, 3]) // into currentValue or array
or
pred = [call.getArgument(1), callback.getAReturn()] and
succ = callback.getParameter(0) // into previousValue
)
or
// `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
pred = call.getAnArgument() and

View File

@@ -895,7 +895,14 @@ class SyntheticConstructor extends Function {
* }
* ```
*/
abstract class AccessorMethodDeclaration extends MethodDeclaration { }
abstract class AccessorMethodDeclaration extends MethodDeclaration {
/** Get the corresponding getter (if this is a setter) or setter (if this is a getter). */
AccessorMethodDeclaration getOtherAccessor() {
getterSetterPair(this, result)
or
getterSetterPair(result, this)
}
}
/**
* A concrete accessor method definition in a class, that is, an accessor method with a function body.
@@ -958,6 +965,9 @@ abstract class AccessorMethodSignature extends MethodSignature, AccessorMethodDe
*/
class GetterMethodDeclaration extends AccessorMethodDeclaration, @property_getter {
override string getAPrimaryQlClass() { result = "GetterMethodDeclaration" }
/** Gets the correspinding setter declaration, if any. */
SetterMethodDeclaration getCorrespondingSetter() { getterSetterPair(this, result) }
}
/**
@@ -1020,6 +1030,9 @@ class GetterMethodSignature extends GetterMethodDeclaration, AccessorMethodSigna
*/
class SetterMethodDeclaration extends AccessorMethodDeclaration, @property_setter {
override string getAPrimaryQlClass() { result = "SetterMethodDeclaration" }
/** Gets the correspinding getter declaration, if any. */
GetterMethodDeclaration getCorrespondingGetter() { getterSetterPair(result, this) }
}
/**
@@ -1263,3 +1276,25 @@ class IndexSignature extends @index_signature, MemberSignature {
override string getAPrimaryQlClass() { result = "IndexSignature" }
}
private boolean getStaticness(AccessorMethodDefinition member) {
member.isStatic() and result = true
or
not member.isStatic() and result = false
}
pragma[nomagic]
private AccessorMethodDefinition getAnAccessorFromClass(
ClassDefinition cls, string name, boolean static
) {
result = cls.getMember(name) and
static = getStaticness(result)
}
pragma[nomagic]
private predicate getterSetterPair(GetterMethodDeclaration getter, SetterMethodDeclaration setter) {
exists(ClassDefinition cls, string name, boolean static |
getter = getAnAccessorFromClass(cls, name, static) and
setter = getAnAccessorFromClass(cls, name, static)
)
}

View File

@@ -430,6 +430,12 @@ module DOM {
result.hasUnderlyingType("Element")
or
result.hasUnderlyingType(any(string s | s.matches("HTML%Element")))
or
exists(DataFlow::ClassNode cls |
cls.getASuperClassNode().getALocalSource() =
DataFlow::globalVarRef(any(string s | s.matches("HTML%Element"))) and
result = cls.getAnInstanceReference()
)
}
module LocationSource {

View File

@@ -235,12 +235,12 @@ module StringOps {
*/
class EndsWith extends DataFlow::Node instanceof EndsWith::Range {
/**
* Gets the `A` in `A.startsWith(B)`.
* Gets the `A` in `A.endsWith(B)`.
*/
DataFlow::Node getBaseString() { result = super.getBaseString() }
/**
* Gets the `B` in `A.startsWith(B)`.
* Gets the `B` in `A.endsWith(B)`.
*/
DataFlow::Node getSubstring() { result = super.getSubstring() }

View File

@@ -786,6 +786,8 @@ class MemberKind extends string {
predicate isAccessor() { this = MemberKind::accessor() }
}
private import internal.StepSummary
module MemberKind {
/** Gets the kind of a method, such as `m() {}` */
MemberKind method() { result = "method" }
@@ -960,7 +962,16 @@ class ClassNode extends DataFlow::SourceNode instanceof ClassNode::Range {
result.getAstNode().getFile() = this.getAstNode().getFile()
)
or
exists(DataFlow::TypeTracker t2 | result = this.getAClassReference(t2).track(t2, t))
result = this.getAClassReferenceRec(t)
}
pragma[noopt]
private DataFlow::SourceNode getAClassReferenceRec(DataFlow::TypeTracker t) {
exists(DataFlow::TypeTracker t2, StepSummary summary, DataFlow::SourceNode prev |
prev = this.getAClassReference(t2) and
StepSummary::step(prev, result, summary) and
t = t2.append(summary)
)
}
/**

View File

@@ -27,3 +27,133 @@ module Handlebars {
SafeString() { this = any(Handlebars h).getAConstructorInvocation("SafeString") }
}
}
/** Provides logic for taint steps for the handlebars library. */
private module HandlebarsTaintSteps {
/**
* Gets a reference to a compiled Handlebars template.
*/
private DataFlow::SourceNode compiledTemplate(DataFlow::CallNode compileCall) {
result = compiledTemplate(DataFlow::TypeTracker::end(), compileCall)
}
private DataFlow::SourceNode compiledTemplate(
DataFlow::TypeTracker t, DataFlow::CallNode compileCall
) {
t.start() and
result = any(Handlebars::Handlebars hb).getAMethodCall(["compile", "template"]) and
result = compileCall
or
exists(DataFlow::TypeTracker t2 | result = compiledTemplate(t2, compileCall).track(t2, t))
}
/**
* Gets a reference to a parameter of a registered Handlebars helper.
*
* ```javascript
* function loudHelper(text) {
* return text.toUpperCase();
* }
*
* hb.registerHelper("loud", loudHelper);
* ```
* In this example, `getRegisteredHelperParameter("loud", func, 0)` will bind `func` to
* the `FunctionNode` representing `function loudHelper`, and return its parameter `text`.
*/
private DataFlow::ParameterNode getRegisteredHelperParam(
string helperName, DataFlow::FunctionNode helperFunction, int paramIndex
) {
exists(DataFlow::CallNode registerHelperCall |
registerHelperCall = any(Handlebars::Handlebars hb).getAMemberCall("registerHelper") and
registerHelperCall.getArgument(0).mayHaveStringValue(helperName) and
helperFunction = registerHelperCall.getArgument(1).getAFunctionValue() and
result = helperFunction.getParameter(paramIndex)
)
}
/**
* Gets a `call` (which is a block wrapped inside curly braces inside the template) from `templateText`.
*
* For example, `getAHelperCallFromTemplate("Hello {{loud customer}}")` will return `"loud customer"`.
*/
bindingset[templateText]
private string getAHelperCallFromTemplate(string templateText) {
result = templateText.regexpFind("\\{\\{[^}]+\\}\\}", _, _).regexpReplaceAll("[{}]", "").trim() and
result.regexpMatch(".*\\s.*")
}
/**
* Holds for calls to helpers from handlebars templates.
*
* ```javascript
* hb.compile("contents of file {{path}} are: {{catFile path}} {{echo p1 p2}}");
* ```
*
* In the example, the predicate will hold for:
*
* * helperName="catFile", argIdx=1, arg="path"
* * helperName="echo", argIdx=1, arg="p1"
* * helperName="echo", argIdx=2, arg="p2"
*
* The initial `{{path}}` will not be considered, as it has no arguments.
*/
bindingset[templateText]
private predicate isTemplateHelperCallArg(
string templateText, string helperName, int argIdx, string argVal
) {
exists(string call | call = getAHelperCallFromTemplate(templateText) |
helperName = call.regexpFind("[^\\s]+", 0, _) and
argIdx >= 0 and
argVal = call.regexpFind("[^\\s]+", argIdx + 1, _)
)
}
/**
* Holds if there's a step from `pred` to `succ` due to templating data being
* passed from a templating call to a registered helper via a parameter.
*
* To establish the step, we look at the template passed to `compile`, and will
* only track steps from templates to helpers they actually reference.
*
* ```javascript
* function loudHelper(text) {
* // ^^^^ succ
* return text.toUpperCase();
* }
*
* hb.registerHelper("loud", loudHelper);
*
* const template = hb.compile("Hello, {{loud name}}!");
*
* template({name: "user"});
* // ^^^^^^ pred
* ```
*/
private predicate isHandlebarsArgStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(
string helperName, DataFlow::CallNode templatingCall, DataFlow::CallNode compileCall,
DataFlow::FunctionNode helperFunction
|
templatingCall = compiledTemplate(compileCall).getACall() and
exists(string templateText, string paramName, int argIdx |
compileCall.getArgument(0).mayHaveStringValue(templateText)
|
pred = templatingCall.getArgument(0).getALocalSource().getAPropertyWrite(paramName).getRhs() and
isTemplateHelperCallArg(templateText, helperName, argIdx, paramName) and
succ = getRegisteredHelperParam(helperName, helperFunction, argIdx)
)
)
}
/**
* A shared flow step from passing data to a handlebars template with
* helpers registered.
*/
class HandlebarsStep extends DataFlow::SharedFlowStep {
DataFlow::CallNode templatingCall;
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
isHandlebarsArgStep(pred, succ)
}
}
}

View File

@@ -565,6 +565,10 @@ private module SpannerCsv {
"@google-cloud/spanner;~SqlExecutorDirect;@google-cloud/spanner;Database;Member[run,runPartitionedUpdate,runStream]",
"@google-cloud/spanner;~SqlExecutorDirect;@google-cloud/spanner;Transaction;Member[run,runStream,runUpdate]",
"@google-cloud/spanner;~SqlExecutorDirect;@google-cloud/spanner;BatchTransaction;Member[createQueryPartitions]",
"@google-cloud/spanner;~SpannerObject;@google-cloud/spanner;v1.SpannerClient;",
"@google-cloud/spanner;~SpannerObject;@google-cloud/spanner;Database;",
"@google-cloud/spanner;~SpannerObject;@google-cloud/spanner;Transaction;",
"@google-cloud/spanner;~SpannerObject;@google-cloud/spanner;Snapshot;",
]
}
}
@@ -584,21 +588,14 @@ private module SpannerCsv {
}
class SpannerSources extends ModelInput::SourceModelCsv {
string spannerClass() { result = ["v1.SpannerClient", "Database", "Transaction", "Snapshot",] }
string resultPath() {
result =
[
"Member[executeSql].Argument[0..].Parameter[1]",
"Member[executeSql].ReturnValue.Awaited.Member[0]", "Member[run].ReturnValue.Awaited",
"Member[run].Argument[0..].Parameter[1]",
]
}
override predicate row(string row) {
row =
"@google-cloud/spanner;" + this.spannerClass() + ";" + this.resultPath() +
";database-access-result"
[
"@google-cloud/spanner;~SpannerObject;Member[executeSql].Argument[0..].Parameter[1];database-access-result",
"@google-cloud/spanner;~SpannerObject;Member[executeSql].ReturnValue.Awaited.Member[0];database-access-result",
"@google-cloud/spanner;~SpannerObject;Member[run].ReturnValue.Awaited;database-access-result",
"@google-cloud/spanner;~SpannerObject;Member[run].Argument[0..].Parameter[1];database-access-result",
]
}
}
}

View File

@@ -42,9 +42,7 @@ module AccessPath {
* Parses a lower-bounded interval `n..` and gets the lower bound.
*/
bindingset[arg]
private int parseLowerBound(string arg) {
result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt()
}
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
/**
* Parses an integer constant or interval (bounded or unbounded) that explicitly

View File

@@ -124,6 +124,15 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
token.getName() = "Parameter" and
token.getAnArgument() = "this" and
result = node.getReceiver()
or
token.getName() = "DecoratedClass" and
result = node.getADecoratedClass()
or
token.getName() = "DecoratedMember" and
result = node.getADecoratedMember()
or
token.getName() = "DecoratedParameter" and
result = node.getADecoratedParameter()
}
/**
@@ -210,7 +219,11 @@ InvokeNode getAnInvocationOf(API::Node node) { result = node.getAnInvocation() }
*/
bindingset[name]
predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) {
name = ["Member", "Instance", "Awaited", "ArrayElement", "Element", "MapValue", "NewCall", "Call"]
name =
[
"Member", "Instance", "Awaited", "ArrayElement", "Element", "MapValue", "NewCall", "Call",
"DecoratedClass", "DecoratedMember", "DecoratedParameter"
]
}
/**
@@ -218,7 +231,11 @@ predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) {
* in an identifying access path.
*/
predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) {
name = ["Instance", "Awaited", "ArrayElement", "Element", "MapValue", "NewCall", "Call"]
name =
[
"Instance", "Awaited", "ArrayElement", "Element", "MapValue", "NewCall", "Call",
"DecoratedClass", "DecoratedMember", "DecoratedParameter"
]
}
/**

View File

@@ -0,0 +1,127 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about
* resource exhaustion vulnerabilities, as well as extension points for
* adding your own.
*/
import javascript
/**
* Provides sources, sinks, and sanitizers for reasoning about
* resource exhaustion vulnerabilities.
*/
module ResourceExhaustion {
/**
* A data flow source for resource exhaustion vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for resource exhaustion vulnerabilities.
*/
abstract class Sink extends DataFlow::Node {
/**
* Gets a description of why this is a problematic sink.
*/
abstract string getProblemDescription();
}
/**
* A data flow sanitizer for resource exhaustion vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/** A source of remote user input, considered as a data flow source for resource exhaustion vulnerabilities. */
class RemoteFlowSourceAsSource extends Source instanceof RemoteFlowSource {
RemoteFlowSourceAsSource() {
// exclude source that only happen client-side
not this instanceof ClientSideRemoteFlowSource and
not this = DataFlow::parameterNode(any(PostMessageEventHandler pmeh).getEventParameter())
}
}
/**
* A node that determines the repetitions of a string, considered as a data flow sink for resource exhaustion vulnerabilities.
*/
class StringRepetitionSink extends Sink {
StringRepetitionSink() {
exists(DataFlow::MethodCallNode repeat |
repeat.getMethodName() = "repeat" and
this = repeat.getArgument(0)
)
}
override string getProblemDescription() {
result = "This creates a string with a user-controlled length"
}
}
/**
* A node that determines the duration of a timer, considered as a data flow sink for resource exhaustion vulnerabilities.
*/
class TimerDurationSink extends Sink {
TimerDurationSink() {
this = DataFlow::globalVarRef(["setTimeout", "setInterval"]).getACall().getArgument(1) or
this = LodashUnderscore::member(["delay", "throttle", "debounce"]).getACall().getArgument(1)
}
override string getProblemDescription() {
result = "This creates a timer with a user-controlled duration"
}
}
/**
* A node that determines the size of a buffer, considered as a data flow sink for resource exhaustion vulnerabilities.
*/
class BufferSizeSink extends Sink {
BufferSizeSink() {
exists(DataFlow::SourceNode clazz, DataFlow::InvokeNode invk, int index |
clazz = DataFlow::globalVarRef("Buffer") and this = invk.getArgument(index)
|
exists(string name |
invk = clazz.getAMemberCall(name) and
(
name = "from" and index = 2 // the length argument
or
name = ["alloc", "allocUnsafe", "allocUnsafeSlow"] and index = 0 // the buffer size
)
)
or
invk = clazz.getAnInvocation() and
(
// invk.getNumArgument() = 1 and // `new Buffer(size)`, it's only an issue if the size is a number, which we don't track precisely.
// index = 0
// or
invk.getNumArgument() = 3 and index = 2 // the length argument
)
)
or
this = DataFlow::globalVarRef("SlowBuffer").getAnInstantiation().getArgument(0)
}
override string getProblemDescription() {
result = "This creates a buffer with a user-controlled size"
}
}
/**
* A node that determines the size of an array, considered as a data flow sink for resource exhaustion vulnerabilities.
* This is only an issue if the argument is a number, which we don't track precisely.
*/
class DenseArraySizeSink extends Sink {
DenseArraySizeSink() {
// Arrays are sparse by default, so we must also look at how the array is used
exists(DataFlow::ArrayConstructorInvokeNode instance |
this = instance.getArgument(0) and
instance.getNumArgument() = 1
|
exists(instance.getAMethodCall(["map", "fill", "join", "toString"])) or
instance.flowsToExpr(any(AddExpr p).getAnOperand())
)
}
override string getProblemDescription() {
result = "This creates an array with a user-controlled length"
}
}
}

View File

@@ -0,0 +1,65 @@
/**
* Provides a taint tracking configuration for reasoning about
* resource exhaustion vulnerabilities (CWE-770).
*
* Note, for performance reasons: only import this file if
* `ResourceExhaustion::Configuration` is needed, otherwise
* `ResourceExhaustionCustomizations` should be imported instead.
*/
import javascript
import ResourceExhaustionCustomizations::ResourceExhaustion
/**
* A data flow configuration for resource exhaustion vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "ResourceExhaustion" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof Sanitizer
}
override predicate isAdditionalTaintStep(DataFlow::Node src, DataFlow::Node dst) {
isNumericFlowStep(src, dst)
}
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) {
guard instanceof UpperBoundsCheckSanitizerGuard
}
override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) {
succ.(DataFlow::PropRead).accesses(pred, "length")
}
}
/** Holds if data is converted to a number from `src` to `dst`. */
predicate isNumericFlowStep(DataFlow::Node src, DataFlow::Node dst) {
exists(DataFlow::CallNode c |
c = dst and
src = c.getAnArgument()
|
c = DataFlow::globalVarRef("Math").getAMemberCall(_) or
c = DataFlow::globalVarRef(["Number", "parseInt", "parseFloat"]).getACall()
)
}
/**
* A sanitizer that blocks taint flow if the size of a number is limited.
*/
class UpperBoundsCheckSanitizerGuard extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode {
override RelationalComparison astNode;
override predicate sanitizes(boolean outcome, Expr e) {
true = outcome and
e = astNode.getLesserOperand()
or
false = outcome and
e = astNode.getGreaterOperand()
}
}

View File

@@ -850,7 +850,7 @@ module TaintedPath {
/**
* Holds if we should include a step from `src -> dst` with labels `srclabel -> dstlabel`, and the
* standard taint step `src -> dst` should be suppresesd.
* standard taint step `src -> dst` should be suppressed.
*/
private predicate isPosixPathStep(
DataFlow::Node src, DataFlow::Node dst, Label::PosixPath srclabel, Label::PosixPath dstlabel

View File

@@ -162,6 +162,11 @@ module XssThroughDom {
}
}
/** The `files` property of an `<input />` element */
class FilesSource extends Source {
FilesSource() { this = DOM::domValueRef().getAPropertyRead("files") }
}
/**
* A module for form inputs seen as sources for xss-through-dom.
*/

View File

@@ -36,6 +36,11 @@ class Configuration extends TaintTracking::Configuration {
DomBasedXss::isOptionallySanitizedEdge(pred, succ)
}
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
succ = DataFlow::globalVarRef("URL").getAMemberCall("createObjectURL") and
pred = succ.(DataFlow::InvokeNode).getArgument(0)
}
override predicate hasFlowPath(DataFlow::SourcePathNode src, DataFlow::SinkPathNode sink) {
super.hasFlowPath(src, sink) and
// filtering away readings of `src` that end in a URL sink.

View File

@@ -279,17 +279,6 @@ private class Trace extends TTrace {
}
}
/**
* Gets a string corresponding to the trace `t`.
*/
private string concretise(Trace t) {
t = Nil() and result = ""
or
exists(InputSymbol s1, InputSymbol s2, Trace rest | t = Step(s1, s2, rest) |
result = concretise(rest) + intersect(s1, s2)
)
}
/**
* Holds if `r` is reachable from `(fork, fork)` under input `w`, and there is
* a path from `r` back to `(fork, fork)` with `rem` steps.
@@ -321,14 +310,54 @@ private StatePair getAForkPair(State fork) {
result = MkStatePair(epsilonPred*(fork), epsilonPred*(fork))
}
private predicate hasSuffix(Trace suffix, Trace t, int i) {
// Declaring `t` to be a `RelevantTrace` currently causes a redundant check in the
// recursive case, so instead we check it explicitly here.
t instanceof RelevantTrace and
i = 0 and
suffix = t
or
hasSuffix(Step(_, _, suffix), t, i - 1)
}
pragma[noinline]
private predicate hasTuple(InputSymbol s1, InputSymbol s2, Trace t, int i) {
hasSuffix(Step(s1, s2, _), t, i)
}
private class RelevantTrace extends Trace, Step {
RelevantTrace() {
exists(State fork, StatePair q |
isReachableFromFork(fork, q, this, _) and
q = getAForkPair(fork)
)
}
pragma[noinline]
private string intersect(int i) {
exists(InputSymbol s1, InputSymbol s2 |
hasTuple(s1, s2, this, i) and
result = intersect(s1, s2)
)
}
/** Gets a string corresponding to this trace. */
// the pragma is needed for the case where `intersect(s1, s2)` has multiple values,
// not for recursion
language[monotonicAggregates]
string concretise() {
result = strictconcat(int i | hasTuple(_, _, this, i) | this.intersect(i) order by i desc)
}
}
/**
* Holds if `fork` is a pumpable fork with word `w`.
*/
private predicate isPumpable(State fork, string w) {
exists(StatePair q, Trace t |
exists(StatePair q, RelevantTrace t |
isReachableFromFork(fork, q, t, _) and
q = getAForkPair(fork) and
w = concretise(t)
w = t.concretise()
)
}

View File

@@ -12,7 +12,7 @@
* states that will cause backtracking (a rejecting suffix exists).
*/
import RegExpTreeView
import ReDoSUtilSpecific
/**
* A configuration for which parts of a regular expression should be considered relevant for
@@ -32,7 +32,7 @@ abstract class ReDoSConfiguration extends string {
}
/**
* Holds if repeating `pump' starting at `state` is a candidate for causing backtracking.
* Holds if repeating `pump` starting at `state` is a candidate for causing backtracking.
* No check whether a rejected suffix exists has been made.
*/
private predicate isReDoSCandidate(State state, string pump) {

View File

@@ -1,7 +1,5 @@
/**
* This module should provide a class hierarchy corresponding to a parse tree of regular expressions.
*
* Since the javascript extractor already provides such a hierarchy, we simply import that.
* Provides JavaScript-specific definitions for use in the ReDoSUtil module.
*/
import javascript

View File

@@ -254,17 +254,6 @@ class Trace extends TTrace {
}
}
/**
* Gets a string corresponding to the trace `t`.
*/
string concretise(Trace t) {
t = Nil() and result = ""
or
exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace rest | t = Step(s1, s2, s3, rest) |
result = concretise(rest) + getAThreewayIntersect(s1, s2, s3)
)
}
/**
* Holds if there exists a transition from `r` to `q` in the product automaton.
* Notice that the arguments are flipped, and thus the direction is backwards.
@@ -332,6 +321,51 @@ StateTuple getAnEndTuple(State pivot, State succ) {
result = MkStateTuple(pivot, succ, succ)
}
private predicate hasSuffix(Trace suffix, Trace t, int i) {
// Declaring `t` to be a `RelevantTrace` currently causes a redundant check in the
// recursive case, so instead we check it explicitly here.
t instanceof RelevantTrace and
i = 0 and
suffix = t
or
hasSuffix(Step(_, _, _, suffix), t, i - 1)
}
pragma[noinline]
private predicate hasTuple(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace t, int i) {
hasSuffix(Step(s1, s2, s3, _), t, i)
}
private class RelevantTrace extends Trace, Step {
RelevantTrace() {
exists(State pivot, State succ, StateTuple q |
isReachableFromStartTuple(pivot, succ, q, this, _) and
q = getAnEndTuple(pivot, succ)
)
}
pragma[noinline]
private string getAThreewayIntersect(int i) {
exists(InputSymbol s1, InputSymbol s2, InputSymbol s3 |
hasTuple(s1, s2, s3, this, i) and
result = getAThreewayIntersect(s1, s2, s3)
)
}
/** Gets a string corresponding to this trace. */
// the pragma is needed for the case where `getAThreewayIntersect(s1, s2, s3)` has multiple values,
// not for recursion
language[monotonicAggregates]
string concretise() {
result =
strictconcat(int i |
hasTuple(_, _, _, this, i)
|
this.getAThreewayIntersect(i) order by i desc
)
}
}
/**
* Holds if matching repetitions of `pump` can:
* 1) Transition from `pivot` back to `pivot`.
@@ -345,10 +379,10 @@ StateTuple getAnEndTuple(State pivot, State succ) {
* available in the `hasReDoSResult` predicate.
*/
predicate isPumpable(State pivot, State succ, string pump) {
exists(StateTuple q, Trace t |
exists(StateTuple q, RelevantTrace t |
isReachableFromStartTuple(pivot, succ, q, t, _) and
q = getAnEndTuple(pivot, succ) and
pump = concretise(t)
pump = t.concretise()
)
}

View File

@@ -1,3 +1,13 @@
## 0.0.14
## 0.0.13
### Minor Analysis Improvements
* Fixed an issue that would sometimes prevent the data-flow analysis from finding flow
paths through a function that stores its result on an object.
This may lead to more results for the security queries.
## 0.0.12
## 0.0.11

View File

@@ -11,60 +11,4 @@
* external/cwe/cwe-020
*/
import javascript
private import semmle.javascript.dataflow.InferredTypes
/**
* A check on a string for whether it contains a given substring, possibly with restrictions on the location of the substring.
*/
class SomeSubstringCheck extends DataFlow::Node {
DataFlow::Node substring;
SomeSubstringCheck() {
this.(StringOps::StartsWith).getSubstring() = substring or
this.(StringOps::Includes).getSubstring() = substring or
this.(StringOps::EndsWith).getSubstring() = substring
}
/**
* Gets the substring.
*/
DataFlow::Node getSubstring() { result = substring }
}
from SomeSubstringCheck check, DataFlow::Node substring, string target, string msg
where
substring = check.getSubstring() and
substring.mayHaveStringValue(target) and
(
// target contains a domain on a common TLD, and perhaps some other URL components
target
.regexpMatch("(?i)([a-z]*:?//)?\\.?([a-z0-9-]+\\.)+" + RegExpPatterns::getACommonTld() +
"(:[0-9]+)?/?")
or
// target is a HTTP URL to a domain on any TLD
target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/?")
or
// target is a HTTP URL to a domain on any TLD with path elements, and the check is an includes check
check instanceof StringOps::Includes and
target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/[a-z0-9/_-]+")
) and
(
if check instanceof StringOps::StartsWith
then msg = "may be followed by an arbitrary host name"
else
if check instanceof StringOps::EndsWith
then msg = "may be preceded by an arbitrary host name"
else msg = "can be anywhere in the URL, and arbitrary hosts may come before or after it"
) and
// whitelist
not (
// the leading dot in a subdomain sequence makes the suffix-check safe (if it is performed on the host of the url)
check instanceof StringOps::EndsWith and
target.regexpMatch("(?i)\\.([a-z0-9-]+)(\\.[a-z0-9-]+)+")
or
// the trailing port or slash makes the prefix-check safe
check instanceof StringOps::StartsWith and
target.regexpMatch(".*(:[0-9]+|/)")
)
select check, "'$@' " + msg + ".", substring, target
import IncompleteUrlSubstringSanitization

View File

@@ -0,0 +1,62 @@
/**
* Incomplete URL substring sanitization
*/
private import IncompleteUrlSubstringSanitizationSpecific
/**
* A check on a string for whether it contains a given substring, possibly with restrictions on the location of the substring.
*/
class SomeSubstringCheck extends DataFlow::Node {
DataFlow::Node substring;
SomeSubstringCheck() {
this.(StringOps::StartsWith).getSubstring() = substring or
this.(StringOps::Includes).getSubstring() = substring or
this.(StringOps::EndsWith).getSubstring() = substring
}
/**
* Gets the substring.
*/
DataFlow::Node getSubstring() { result = substring }
}
/** Holds if there is an incomplete URL substring sanitization problem */
query predicate problems(
SomeSubstringCheck check, string msg, DataFlow::Node substring, string target
) {
substring = check.getSubstring() and
mayHaveStringValue(substring, target) and
(
// target contains a domain on a common TLD, and perhaps some other URL components
target
.regexpMatch("(?i)([a-z]*:?//)?\\.?([a-z0-9-]+\\.)+" + RegExpPatterns::getACommonTld() +
"(:[0-9]+)?/?")
or
// target is a HTTP URL to a domain on any TLD
target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/?")
or
// target is a HTTP URL to a domain on any TLD with path elements, and the check is an includes check
check instanceof StringOps::Includes and
target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/[a-z0-9/_-]+")
) and
(
if check instanceof StringOps::StartsWith
then msg = "'$@' may be followed by an arbitrary host name."
else
if check instanceof StringOps::EndsWith
then msg = "'$@' may be preceded by an arbitrary host name."
else msg = "'$@' can be anywhere in the URL, and arbitrary hosts may come before or after it."
) and
// whitelist
not (
// the leading dot in a subdomain sequence makes the suffix-check safe (if it is performed on the host of the url)
check instanceof StringOps::EndsWith and
target.regexpMatch("(?i)\\.([a-z0-9-]+)(\\.[a-z0-9-]+)+")
or
// the trailing port or slash makes the prefix-check safe
check instanceof StringOps::StartsWith and
target.regexpMatch(".*(:[0-9]+|/)")
)
}

View File

@@ -0,0 +1,5 @@
import javascript
import semmle.javascript.dataflow.InferredTypes
/** Holds if `node` may evaluate to `value` */
predicate mayHaveStringValue(DataFlow::Node node, string value) { node.mayHaveStringValue(value) }

View File

@@ -0,0 +1,115 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Applications are constrained by how many resources they can make use
of. Failing to respect these constraints may cause the application to
be unresponsive or crash. It is therefore problematic if attackers
can control the sizes or lifetimes of allocated objects.
</p>
</overview>
<recommendation>
<p>
Ensure that attackers can not control object sizes and their
lifetimes. If object sizes and lifetimes must be controlled by
external parties, ensure you restrict the object sizes and lifetimes so that
they are within acceptable ranges.
</p>
</recommendation>
<example>
<p>
The following example allocates a buffer with a user-controlled
size.
</p>
<sample src="examples/ResourceExhaustion_buffer.js" />
<p>
This is problematic since an attacker can choose a size
that makes the application run out of memory. Even worse, in older
versions of Node.js, this could leak confidential memory.
To prevent such attacks, limit the buffer size:
</p>
<sample src="examples/ResourceExhaustion_buffer_fixed.js" />
</example>
<example>
<p>
As another example, consider an application that allocates an
array with a user-controlled size, and then fills it with values:
</p>
<sample src="examples/ResourceExhaustion_array.js" />
<p>
The allocation of the array itself is not problematic since arrays are
allocated sparsely, but the subsequent filling of the array will take
a long time, causing the application to be unresponsive, or even run
out of memory.
Again, a limit on the size will prevent the attack:
</p>
<sample src="examples/ResourceExhaustion_array_fixed.js" />
</example>
<example>
<p>
Finally, the following example lets a user choose a delay after
which a function is executed:
</p>
<sample src="examples/ResourceExhaustion_timeout.js" />
<p>
This is problematic because a large delay essentially makes the
application wait indefinitely before executing the function. Repeated
registrations of such delays will therefore use up all of the memory
in the application.
A limit on the delay will prevent the attack:
</p>
<sample src="examples/ResourceExhaustion_timeout_fixed.js" />
</example>
<references>
<li>
Wikipedia: <a href="https://en.wikipedia.org/wiki/Denial-of-service_attack">Denial-of-service attack</a>.
</li>
</references>
</qhelp>

View File

@@ -4,15 +4,17 @@
* sizes or durations can cause resource exhaustion.
* @kind path-problem
* @problem.severity warning
* @security-severity 7.5
* @id js/resource-exhaustion
* @precision high
* @tags security
* external/cwe/cwe-400
* external/cwe/cwe-770
*/
import javascript
import DataFlow::PathGraph
import experimental.semmle.javascript.security.dataflow.ResourceExhaustion::ResourceExhaustion
import semmle.javascript.security.dataflow.ResourceExhaustionQuery
from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink
where dataflow.hasFlowPath(source, sink)

View File

@@ -0,0 +1,10 @@
var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
let dogs = new Array(size).fill("dog"); // BAD
// ... use the dog
});

View File

@@ -0,0 +1,16 @@
var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
if (size > 1024) {
res.statusCode = 400;
res.end("Bad request.");
return;
}
let dogs = new Array(size).fill("dog"); // GOOD
// ... use the dogs
});

View File

@@ -0,0 +1,10 @@
var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
let buffer = Buffer.alloc(size); // BAD
// ... use the buffer
});

View File

@@ -0,0 +1,16 @@
var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
var size = parseInt(url.parse(req.url, true).query.size);
if (size > 1024) {
res.statusCode = 400;
res.end("Bad request.");
return;
}
let buffer = Buffer.alloc(size); // GOOD
// ... use the buffer
});

View File

@@ -1,6 +1,7 @@
let express = require('express');
let app = express()
express.put('/todos/:id', (req, res) => {
app.put('/todos/:id', (req, res) => {
let id = req.params.id;
let items = req.session.todos[id];
if (!items) {

View File

@@ -1,6 +1,7 @@
let express = require('express');
let app = express()
express.put('/todos/:id', (req, res) => {
app.put('/todos/:id', (req, res) => {
let id = req.params.id;
let items = req.session.todos.get(id);
if (!items) {

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* Improved handling of custom DOM elements, potentially leading to more alerts for the XSS queries.
* Improved taint tracking through calls to the `Array.prototype.reduce` function.

View File

@@ -0,0 +1,5 @@
---
category: newQuery
---
* The `js/resource-exhaustion` query has been added. It highlights locations where an attacker can cause a large amount of resources to be consumed.
The query previously existed as an experimental query.

View File

@@ -1,6 +1,7 @@
---
category: minorAnalysis
---
## 0.0.13
### Minor Analysis Improvements
* Fixed an issue that would sometimes prevent the data-flow analysis from finding flow
paths through a function that stores its result on an object.
This may lead to more results for the security queries.

View File

@@ -0,0 +1 @@
## 0.0.14

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.12
lastReleaseVersion: 0.0.14

View File

@@ -1,63 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Applications are constrained by how many resources they can make use
of. Failing to respect these constraints may cause the application to
be unresponsive or crash. It is therefore problematic if attackers
can control the sizes or lifetimes of allocated objects.
</p>
</overview>
<recommendation>
<p>
Ensure that attackers can not control object sizes and their
lifetimes. If object sizes and lifetimes must be controlled by
external parties, ensure you restrict the object sizes and lifetimes so that
they are within acceptable ranges.
</p>
</recommendation>
<example>
<p>
The following example lets a user choose a delay after
which a function is executed:
</p>
<sample src="examples/ResourceExhaustion_timeout.js" />
<p>
This is problematic because a large delay essentially makes the
application wait indefinitely before executing the function. Repeated
registrations of such delays will therefore use up all of the memory
in the application.
A limit on the delay will prevent the attack:
</p>
<sample src="examples/ResourceExhaustion_timeout_fixed.js" />
</example>
<references>
</references>
</qhelp>

View File

@@ -1,75 +0,0 @@
/**
* Provides a taint tracking configuration for reasoning about
* resource exhaustion vulnerabilities (CWE-770).
*
* Note, for performance reasons: only import this file if
* `ResourceExhaustion::Configuration` is needed, otherwise
* `ResourceExhaustionCustomizations` should be imported instead.
*/
import javascript
import semmle.javascript.security.dataflow.LoopBoundInjectionCustomizations
module ResourceExhaustion {
import ResourceExhaustionCustomizations::ResourceExhaustion
/**
* A data flow configuration for resource exhaustion vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "ResourceExhaustion" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof Sanitizer
}
override predicate isAdditionalTaintStep(DataFlow::Node src, DataFlow::Node dst) {
isNumericFlowStep(src, dst)
or
// reuse most existing taint steps
isRestrictedAdditionalTaintStep(src, dst)
}
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) {
guard instanceof LoopBoundInjection::LengthCheckSanitizerGuard or
guard instanceof UpperBoundsCheckSanitizerGuard
}
}
predicate isRestrictedAdditionalTaintStep(DataFlow::Node src, DataFlow::Node dst) {
TaintTracking::sharedTaintStep(src, dst) and
not dst.asExpr() instanceof AddExpr and
not dst.(DataFlow::MethodCallNode).calls(src, "toString")
}
/**
* Holds if data may flow from `src` to `dst` as a number.
*/
predicate isNumericFlowStep(DataFlow::Node src, DataFlow::Node dst) {
// steps that introduce or preserve a number
dst.(DataFlow::PropRead).accesses(src, ["length", "size"])
or
exists(DataFlow::CallNode c |
c = dst and
src = c.getAnArgument()
|
c = DataFlow::globalVarRef("Math").getAMemberCall(_) or
c = DataFlow::globalVarRef(["Number", "parseInt", "parseFloat"]).getACall()
)
or
exists(Expr dstExpr, Expr srcExpr |
dstExpr = dst.asExpr() and
srcExpr = src.asExpr()
|
dstExpr.(BinaryExpr).getAnOperand() = srcExpr and
not dstExpr instanceof AddExpr
or
dstExpr.(PlusExpr).getOperand() = srcExpr
)
}
}

View File

@@ -1,80 +0,0 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about
* resource exhaustion vulnerabilities, as well as extension points for
* adding your own.
*/
import javascript
module ResourceExhaustion {
/**
* A data flow source for resource exhaustion vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for resource exhaustion vulnerabilities.
*/
abstract class Sink extends DataFlow::Node {
/**
* Gets a description of why this is a problematic sink.
*/
abstract string getProblemDescription();
}
/**
* A data flow sanitizer for resource exhaustion vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A sanitizer that blocks taint flow if the size of a number is limited.
*/
class UpperBoundsCheckSanitizerGuard extends TaintTracking::SanitizerGuardNode,
DataFlow::ValueNode {
override RelationalComparison astNode;
override predicate sanitizes(boolean outcome, Expr e) {
true = outcome and
e = astNode.getLesserOperand()
or
false = outcome and
e = astNode.getGreaterOperand()
}
}
/** A source of remote user input, considered as a data flow source for resource exhaustion vulnerabilities. */
class RemoteFlowSourceAsSource extends Source {
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
}
/**
* A node that determines the repetitions of a string, considered as a data flow sink for resource exhaustion vulnerabilities.
*/
class StringRepetitionSink extends Sink {
StringRepetitionSink() {
exists(DataFlow::MethodCallNode repeat |
repeat.getMethodName() = "repeat" and
this = repeat.getArgument(0)
)
}
override string getProblemDescription() {
result = "This creates a string with a user-controlled length"
}
}
/**
* A node that determines the duration of a timer, considered as a data flow sink for resource exhaustion vulnerabilities.
*/
class TimerDurationSink extends Sink {
TimerDurationSink() {
this = DataFlow::globalVarRef(["setTimeout", "setInterval"]).getACall().getArgument(1) or
this = LodashUnderscore::member(["delay", "throttle", "debounce"]).getACall().getArgument(1)
}
override string getProblemDescription() {
result = "This creates a timer with a user-controlled duration"
}
}
}

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-queries
version: 0.0.13-dev
version: 0.1.0-dev
groups:
- javascript
- queries

View File

@@ -1,68 +0,0 @@
nodes
| documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url |
| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url |
| documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay |
| documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay |
| resource-exhaustion.js:9:7:9:42 | s |
| resource-exhaustion.js:9:11:9:34 | url.par ... , true) |
| resource-exhaustion.js:9:11:9:40 | url.par ... ).query |
| resource-exhaustion.js:9:11:9:42 | url.par ... query.s |
| resource-exhaustion.js:9:21:9:27 | req.url |
| resource-exhaustion.js:9:21:9:27 | req.url |
| resource-exhaustion.js:10:7:10:21 | n |
| resource-exhaustion.js:10:11:10:21 | parseInt(s) |
| resource-exhaustion.js:10:20:10:20 | s |
| resource-exhaustion.js:38:12:38:12 | n |
| resource-exhaustion.js:38:12:38:12 | n |
| resource-exhaustion.js:39:12:39:12 | s |
| resource-exhaustion.js:39:12:39:12 | s |
| resource-exhaustion.js:85:17:85:17 | n |
| resource-exhaustion.js:85:17:85:17 | n |
| resource-exhaustion.js:86:17:86:17 | s |
| resource-exhaustion.js:86:17:86:17 | s |
| resource-exhaustion.js:87:18:87:18 | n |
| resource-exhaustion.js:87:18:87:18 | n |
| resource-exhaustion.js:88:18:88:18 | s |
| resource-exhaustion.js:88:18:88:18 | s |
edges
| documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | documentaion-examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) |
| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) |
| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) |
| resource-exhaustion.js:9:7:9:42 | s | resource-exhaustion.js:10:20:10:20 | s |
| resource-exhaustion.js:9:7:9:42 | s | resource-exhaustion.js:39:12:39:12 | s |
| resource-exhaustion.js:9:7:9:42 | s | resource-exhaustion.js:39:12:39:12 | s |
| resource-exhaustion.js:9:7:9:42 | s | resource-exhaustion.js:86:17:86:17 | s |
| resource-exhaustion.js:9:7:9:42 | s | resource-exhaustion.js:86:17:86:17 | s |
| resource-exhaustion.js:9:7:9:42 | s | resource-exhaustion.js:88:18:88:18 | s |
| resource-exhaustion.js:9:7:9:42 | s | resource-exhaustion.js:88:18:88:18 | s |
| resource-exhaustion.js:9:11:9:34 | url.par ... , true) | resource-exhaustion.js:9:11:9:40 | url.par ... ).query |
| resource-exhaustion.js:9:11:9:40 | url.par ... ).query | resource-exhaustion.js:9:11:9:42 | url.par ... query.s |
| resource-exhaustion.js:9:11:9:42 | url.par ... query.s | resource-exhaustion.js:9:7:9:42 | s |
| resource-exhaustion.js:9:21:9:27 | req.url | resource-exhaustion.js:9:11:9:34 | url.par ... , true) |
| resource-exhaustion.js:9:21:9:27 | req.url | resource-exhaustion.js:9:11:9:34 | url.par ... , true) |
| resource-exhaustion.js:10:7:10:21 | n | resource-exhaustion.js:38:12:38:12 | n |
| resource-exhaustion.js:10:7:10:21 | n | resource-exhaustion.js:38:12:38:12 | n |
| resource-exhaustion.js:10:7:10:21 | n | resource-exhaustion.js:85:17:85:17 | n |
| resource-exhaustion.js:10:7:10:21 | n | resource-exhaustion.js:85:17:85:17 | n |
| resource-exhaustion.js:10:7:10:21 | n | resource-exhaustion.js:87:18:87:18 | n |
| resource-exhaustion.js:10:7:10:21 | n | resource-exhaustion.js:87:18:87:18 | n |
| resource-exhaustion.js:10:11:10:21 | parseInt(s) | resource-exhaustion.js:10:7:10:21 | n |
| resource-exhaustion.js:10:20:10:20 | s | resource-exhaustion.js:10:11:10:21 | parseInt(s) |
#select
| documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | This creates a timer with a user-controlled duration from $@. | documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | here |
| resource-exhaustion.js:38:12:38:12 | n | resource-exhaustion.js:9:21:9:27 | req.url | resource-exhaustion.js:38:12:38:12 | n | This creates a string with a user-controlled length from $@. | resource-exhaustion.js:9:21:9:27 | req.url | here |
| resource-exhaustion.js:39:12:39:12 | s | resource-exhaustion.js:9:21:9:27 | req.url | resource-exhaustion.js:39:12:39:12 | s | This creates a string with a user-controlled length from $@. | resource-exhaustion.js:9:21:9:27 | req.url | here |
| resource-exhaustion.js:85:17:85:17 | n | resource-exhaustion.js:9:21:9:27 | req.url | resource-exhaustion.js:85:17:85:17 | n | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:9:21:9:27 | req.url | here |
| resource-exhaustion.js:86:17:86:17 | s | resource-exhaustion.js:9:21:9:27 | req.url | resource-exhaustion.js:86:17:86:17 | s | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:9:21:9:27 | req.url | here |
| resource-exhaustion.js:87:18:87:18 | n | resource-exhaustion.js:9:21:9:27 | req.url | resource-exhaustion.js:87:18:87:18 | n | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:9:21:9:27 | req.url | here |
| resource-exhaustion.js:88:18:88:18 | s | resource-exhaustion.js:9:21:9:27 | req.url | resource-exhaustion.js:88:18:88:18 | s | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:9:21:9:27 | req.url | here |

View File

@@ -1 +0,0 @@
experimental/Security/CWE-770/ResourceExhaustion.ql

View File

@@ -1,89 +0,0 @@
// this file contains many `NOT OK [INCONSISTENCY]` annotations, those
// would be resolved if the query used flow labels to recognice
// numbers flowing to sinks
var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
let s = url.parse(req.url, true).query.s;
let n = parseInt(s);
Buffer.from(s); // OK
Buffer.from(n); // OK
Buffer.from(x, n); // OK
Buffer.from(x, y, s); // NOT OK
Buffer.from(x, y, n); // NOT OK [INCONSISTENCY]
Buffer.from(x, y, n); // NOT OK [INCONSISTENCY]
Buffer.alloc(n); // NOT OK [INCONSISTENCY]
Buffer.allocUnsafe(n); // NOT OK [INCONSISTENCY]
Buffer.allocUnsafeSlow(n); // NOT OK [INCONSISTENCY]
new Buffer(n); // NOT OK [INCONSISTENCY]
new Buffer(x, n); // OK
new Buffer(x, y, n); // NOT OK [INCONSISTENCY]
new SlowBuffer(n); // NOT OK [INCONSISTENCY]
Array(n); // OK
new Array(n); // OK
Array(n).map(); // NOT OK [INCONSISTENCY]
new Array(n).map(); // NOT OK [INCONSISTENCY]
Array(n).fill(); // NOT OK [INCONSISTENCY]
Array(n).join(); // NOT OK [INCONSISTENCY]
Array(n).toString(); // NOT OK [INCONSISTENCY]
Array(n) + x; // NOT OK [INCONSISTENCY]
x.repeat(n); // NOT OK
x.repeat(s); // NOT OK
new Buffer(n * x); // NOT OK [INCONSISTENCY]
new Buffer(n + n); // NOT OK [INCONSISTENCY]
new Buffer(n + x); // OK (maybe)
new Buffer(n + s); // OK (this is a string if `s` is a string)
new Buffer(s + 2); // OK (this is a string if `s` is a string)
new Buffer(s + s); // OK
new Buffer(n + "X"); // OK
new Buffer(Math.ceil(s)); // NOT OK [INCONSISTENCY]
new Buffer(Number(s)); // NOT OK [INCONSISTENCY]
new Buffer(new Number(s)); // OK
new Buffer(s + x.length); // OK (this is a string if `s` is a string)
new Buffer(s.length); // NOT OK [INCONSISTENCY]
if (n < 100) {
new Buffer(n); // OK
} else {
new Buffer(n); // NOT OK [INCONSISTENCY]
}
let ns = x ? n : s;
new Buffer(ns); // NOT OK [INCONSISTENCY]
new Buffer(n.toString()); // OK
if (typeof n === "string") {
new Buffer(n); // OK
} else {
new Buffer(n); // NOT OK [INCONSISTENCY]
}
if (typeof n === "number") {
new Buffer(n); // NOT OK [INCONSISTENCY]
} else {
new Buffer(n); // OK
}
if (typeof s === "number") {
new Buffer(s); // NOT OK [INCONSISTENCY]
} else {
new Buffer(s); // OK
}
setTimeout(f, n); // NOT OK
setTimeout(f, s); // NOT OK
setInterval(f, n); // NOT OK
setInterval(f, s); // NOT OK
});

View File

@@ -16,6 +16,7 @@ typeInferenceMismatch
| arrays.js:2:15:2:22 | source() | arrays.js:8:10:8:22 | arrayIfy(foo) |
| arrays.js:2:15:2:22 | source() | arrays.js:11:10:11:28 | union(["bla"], foo) |
| arrays.js:2:15:2:22 | source() | arrays.js:14:10:14:18 | flat(foo) |
| arrays.js:2:15:2:22 | source() | arrays.js:19:10:19:12 | res |
| booleanOps.js:2:11:2:18 | source() | booleanOps.js:4:8:4:8 | x |
| booleanOps.js:2:11:2:18 | source() | booleanOps.js:13:10:13:10 | x |
| booleanOps.js:2:11:2:18 | source() | booleanOps.js:19:10:19:10 | x |

View File

@@ -12,4 +12,9 @@ function test() {
const flat = require("arr-flatten");
sink(flat(foo)); // NOT OK
}
let res = foo.reduce((prev, current) => {
return prev + '<b>' + current + '</b>';
}, '');
sink(res); // NOT OK
}

View File

@@ -0,0 +1,17 @@
import * as testlib from 'testlib';
// parameter decorators are only valid in TypeScript so this test is in a .ts file
class C {
decoratedParamSource(@testlib.ParamDecoratorSource x) {
sink(x) // NOT OK
}
decoratedParamSink(@testlib.ParamDecoratorSink x) { // OK
}
decoratedParamSink2(@testlib.ParamDecoratorSink x) { // OK
x.push(source()); // OK
}
}
new C().decoratedParamSink(source()); // OK - parameter decorators can't be used to mark the parameter as a sink
new C().decoratedParamSink2([]); // OK

View File

@@ -1,5 +1,6 @@
consistencyIssue
taintFlow
| paramDecorator.ts:6:54:6:54 | x | paramDecorator.ts:7:10:7:10 | x |
| test.js:5:30:5:37 | source() | test.js:5:8:5:38 | testlib ... urce()) |
| test.js:6:22:6:29 | source() | test.js:6:8:6:30 | preserv ... urce()) |
| test.js:7:41:7:48 | source() | test.js:7:8:7:49 | require ... urce()) |
@@ -35,6 +36,25 @@ taintFlow
| test.js:97:17:97:24 | source() | test.js:97:17:97:24 | source() |
| test.js:102:16:102:34 | testlib.getSource() | test.js:103:8:103:13 | source |
| test.js:102:16:102:34 | testlib.getSource() | test.js:104:8:104:24 | source.continue() |
| test.js:111:12:111:19 | source() | test.js:111:12:111:19 | source() |
| test.js:113:17:113:17 | x | test.js:114:10:114:10 | x |
| test.js:132:10:132:25 | this.fieldSource | test.js:132:10:132:25 | this.fieldSource |
| test.js:133:10:133:37 | OtherCl ... dSource | test.js:133:10:133:37 | OtherCl ... dSource |
| test.js:134:22:134:29 | source() | test.js:134:22:134:29 | source() |
| test.js:135:34:135:41 | source() | test.js:135:34:135:41 | source() |
| test.js:144:16:144:23 | source() | test.js:144:16:144:23 | source() |
| test.js:147:29:147:36 | source() | test.js:147:29:147:36 | source() |
| test.js:150:19:150:19 | x | test.js:151:10:151:10 | x |
| test.js:152:12:152:19 | source() | test.js:152:12:152:19 | source() |
| test.js:156:32:156:32 | x | test.js:157:10:157:10 | x |
| test.js:158:12:158:19 | source() | test.js:158:12:158:19 | source() |
| test.js:162:20:162:20 | x | test.js:163:10:163:10 | x |
| test.js:164:12:164:19 | source() | test.js:164:12:164:19 | source() |
| test.js:172:12:172:19 | source() | test.js:172:12:172:19 | source() |
| test.js:176:23:176:23 | x | test.js:177:10:177:10 | x |
| test.js:182:12:182:19 | source() | test.js:182:12:182:19 | source() |
| test.js:187:31:187:31 | x | test.js:189:10:189:10 | x |
| test.js:203:32:203:39 | source() | test.js:203:32:203:39 | source() |
isSink
| test.js:54:18:54:25 | source() | test-sink |
| test.js:55:22:55:29 | source() | test-sink |
@@ -81,6 +101,18 @@ isSink
| test.js:95:17:95:24 | source() | test-sink |
| test.js:96:17:96:24 | source() | test-sink |
| test.js:97:17:97:24 | source() | test-sink |
| test.js:111:12:111:19 | source() | test-sink |
| test.js:134:22:134:29 | source() | test-sink |
| test.js:135:34:135:41 | source() | test-sink |
| test.js:144:16:144:23 | source() | test-sink |
| test.js:147:29:147:36 | source() | test-sink |
| test.js:152:12:152:19 | source() | test-sink |
| test.js:158:12:158:19 | source() | test-sink |
| test.js:164:12:164:19 | source() | test-sink |
| test.js:172:12:172:19 | source() | test-sink |
| test.js:182:12:182:19 | source() | test-sink |
| test.js:196:12:196:29 | this._wrappedField | test-sink |
| test.js:203:32:203:39 | source() | test-sink |
syntaxErrors
| Member[foo |
| Member[foo] .Member[bar] |

View File

@@ -104,3 +104,102 @@ function testFlowThroughReceiver() {
sink(source.continue()); // NOT OK
sink(source.blah()); // OK
}
@testlib.ClassDecorator
class DecoratedClass {
returnValueIsSink() {
return source(); // NOT OK
}
inputIsSource(x) {
sink(x); // NOT OK
}
}
class OtherClass {
@testlib.FieldDecoratorSink
fieldSink;
@testlib.FieldDecoratorSink
static staticFieldSink;
@testlib.FieldDecoratorSource
fieldSource;
@testlib.FieldDecoratorSource
static staticFieldSource;
useFields() {
sink(this.fieldSource); // NOT OK
sink(OtherClass.staticFieldSource); // NOT OK
this.fieldSink = source(); // NOT OK
OtherClass.staticFieldSink = source(); // NOT OK
sink(this.staticFieldSource); // OK - not a valid field access
sink(OtherClass.fieldSource); // OK - not a valid field access
this.staticFieldSink = source(); // OK - not a valid field access
OtherClass.fieldSink = source(); // OK - not a valid field access
}
@testlib.FieldDecoratorSink
fieldSink2 = source(); // NOT OK
@testlib.FieldDecoratorSink
static staticFieldSink2 = source(); // NOT OK
@testlib.MethodDecorator
decoratedMethod(x) {
sink(x); // NOT OK
return source(); // NOT OK
}
@testlib.MethodDecorator
static decoratedStaticMethod(x) {
sink(x); // NOT OK
return source(); // NOT OK
}
@testlib.MethodDecoratorWithArgs({ something: true })
decoratedMethod2(x) {
sink(x); // NOT OK
return source(); // NOT OK
}
@testlib.FieldDecoratorSink
get sinkViaGetter() {
// If a field with this decorator should be seen as a sink it generally means the framework
// will read it and pass it to an underlying sink. Therefore the return value of its getter
// should be seen as a sink as well.
return source(); // NOT OK
}
@testlib.FieldDecoratorSource
set sourceViaSetter(x) {
sink(x); // NOT OK
}
get sinkViaGetterIndirect() {
// Same as 'sinkViaGetter', but where the decorator is placed on the corresponding setter
return source(); // NOT OK
}
@testlib.FieldDecoratorSink // indirectly decorate the getter above
set sinkViaGetterIndirect(x) {}
set sourceViaSetterIndirect(x) {
// Same as 'sourceViaSetter', but where the decorator is placed on the corresponding getter
sink(x); // NOT OK
}
@testlib.FieldDecoratorSource // indirectly decorate the setter above
get sourceViaSetterIndirect() {}
@testlib.FieldDecoratorSink
get accessorAroundField() {
return this._wrappedField; // OK - the alert occurs at the assignment to 'accessorAroundField'
}
set accessorAroundField(x) {
this._wrappedField = x;
}
useWrappedField() {
this.accessorAroundField = source(); // NOT OK
}
}

View File

@@ -33,12 +33,27 @@ class Sinks extends ModelInput::SinkModelCsv {
"testlib;;Member[mySinkExceptLast].Argument[0..N-2];test-sink",
"testlib;;Member[mySinkIfArityTwo].WithArity[2].Argument[0];test-sink",
"testlib;;Member[sink1, sink2, sink3 ].Argument[0];test-sink",
"testlib;;Member[ClassDecorator].DecoratedClass.Instance.Member[returnValueIsSink].ReturnValue;test-sink",
"testlib;;Member[FieldDecoratorSink].DecoratedMember;test-sink",
"testlib;;Member[MethodDecorator].DecoratedMember.ReturnValue;test-sink",
"testlib;;Member[MethodDecoratorWithArgs].ReturnValue.DecoratedMember.ReturnValue;test-sink",
"testlib;;Member[ParamDecoratorSink].DecoratedParameter;test-sink",
]
}
}
class Sources extends ModelInput::SourceModelCsv {
override predicate row(string row) { row = "testlib;;Member[getSource].ReturnValue;test-source" }
override predicate row(string row) {
row =
[
"testlib;;Member[getSource].ReturnValue;test-source",
"testlib;;Member[ClassDecorator].DecoratedClass.Instance.Member[inputIsSource].Parameter[0];test-source",
"testlib;;Member[FieldDecoratorSource].DecoratedMember;test-source",
"testlib;;Member[ParamDecoratorSource].DecoratedParameter;test-source",
"testlib;;Member[MethodDecorator].DecoratedMember.Parameter[0];test-source",
"testlib;;Member[MethodDecoratorWithArgs].ReturnValue.DecoratedMember.Parameter[0];test-source",
]
}
}
class BasicTaintTracking extends TaintTracking::Configuration {

View File

@@ -1541,6 +1541,34 @@ nodes
| express.js:8:20:8:32 | req.query.bar |
| express.js:8:20:8:32 | req.query.bar |
| express.js:8:20:8:32 | req.query.bar |
| handlebars.js:10:51:10:58 | filePath |
| handlebars.js:10:51:10:58 | filePath |
| handlebars.js:10:51:10:58 | filePath |
| handlebars.js:10:51:10:58 | filePath |
| handlebars.js:11:32:11:39 | filePath |
| handlebars.js:11:32:11:39 | filePath |
| handlebars.js:11:32:11:39 | filePath |
| handlebars.js:11:32:11:39 | filePath |
| handlebars.js:11:32:11:39 | filePath |
| handlebars.js:13:73:13:80 | filePath |
| handlebars.js:13:73:13:80 | filePath |
| handlebars.js:13:73:13:80 | filePath |
| handlebars.js:13:73:13:80 | filePath |
| handlebars.js:15:25:15:32 | filePath |
| handlebars.js:15:25:15:32 | filePath |
| handlebars.js:15:25:15:32 | filePath |
| handlebars.js:15:25:15:32 | filePath |
| handlebars.js:15:25:15:32 | filePath |
| handlebars.js:29:46:29:60 | req.params.path |
| handlebars.js:29:46:29:60 | req.params.path |
| handlebars.js:29:46:29:60 | req.params.path |
| handlebars.js:29:46:29:60 | req.params.path |
| handlebars.js:29:46:29:60 | req.params.path |
| handlebars.js:43:15:43:29 | req.params.path |
| handlebars.js:43:15:43:29 | req.params.path |
| handlebars.js:43:15:43:29 | req.params.path |
| handlebars.js:43:15:43:29 | req.params.path |
| handlebars.js:43:15:43:29 | req.params.path |
| normalizedPaths.js:11:7:11:27 | path |
| normalizedPaths.js:11:7:11:27 | path |
| normalizedPaths.js:11:7:11:27 | path |
@@ -6414,6 +6442,38 @@ edges
| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) |
| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) |
| express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar |
| handlebars.js:10:51:10:58 | filePath | handlebars.js:11:32:11:39 | filePath |
| handlebars.js:10:51:10:58 | filePath | handlebars.js:11:32:11:39 | filePath |
| handlebars.js:10:51:10:58 | filePath | handlebars.js:11:32:11:39 | filePath |
| handlebars.js:10:51:10:58 | filePath | handlebars.js:11:32:11:39 | filePath |
| handlebars.js:10:51:10:58 | filePath | handlebars.js:11:32:11:39 | filePath |
| handlebars.js:10:51:10:58 | filePath | handlebars.js:11:32:11:39 | filePath |
| handlebars.js:10:51:10:58 | filePath | handlebars.js:11:32:11:39 | filePath |
| handlebars.js:10:51:10:58 | filePath | handlebars.js:11:32:11:39 | filePath |
| handlebars.js:13:73:13:80 | filePath | handlebars.js:15:25:15:32 | filePath |
| handlebars.js:13:73:13:80 | filePath | handlebars.js:15:25:15:32 | filePath |
| handlebars.js:13:73:13:80 | filePath | handlebars.js:15:25:15:32 | filePath |
| handlebars.js:13:73:13:80 | filePath | handlebars.js:15:25:15:32 | filePath |
| handlebars.js:13:73:13:80 | filePath | handlebars.js:15:25:15:32 | filePath |
| handlebars.js:13:73:13:80 | filePath | handlebars.js:15:25:15:32 | filePath |
| handlebars.js:13:73:13:80 | filePath | handlebars.js:15:25:15:32 | filePath |
| handlebars.js:13:73:13:80 | filePath | handlebars.js:15:25:15:32 | filePath |
| handlebars.js:29:46:29:60 | req.params.path | handlebars.js:10:51:10:58 | filePath |
| handlebars.js:29:46:29:60 | req.params.path | handlebars.js:10:51:10:58 | filePath |
| handlebars.js:29:46:29:60 | req.params.path | handlebars.js:10:51:10:58 | filePath |
| handlebars.js:29:46:29:60 | req.params.path | handlebars.js:10:51:10:58 | filePath |
| handlebars.js:29:46:29:60 | req.params.path | handlebars.js:10:51:10:58 | filePath |
| handlebars.js:29:46:29:60 | req.params.path | handlebars.js:10:51:10:58 | filePath |
| handlebars.js:29:46:29:60 | req.params.path | handlebars.js:10:51:10:58 | filePath |
| handlebars.js:29:46:29:60 | req.params.path | handlebars.js:10:51:10:58 | filePath |
| handlebars.js:43:15:43:29 | req.params.path | handlebars.js:13:73:13:80 | filePath |
| handlebars.js:43:15:43:29 | req.params.path | handlebars.js:13:73:13:80 | filePath |
| handlebars.js:43:15:43:29 | req.params.path | handlebars.js:13:73:13:80 | filePath |
| handlebars.js:43:15:43:29 | req.params.path | handlebars.js:13:73:13:80 | filePath |
| handlebars.js:43:15:43:29 | req.params.path | handlebars.js:13:73:13:80 | filePath |
| handlebars.js:43:15:43:29 | req.params.path | handlebars.js:13:73:13:80 | filePath |
| handlebars.js:43:15:43:29 | req.params.path | handlebars.js:13:73:13:80 | filePath |
| handlebars.js:43:15:43:29 | req.params.path | handlebars.js:13:73:13:80 | filePath |
| normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path |
| normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path |
| normalizedPaths.js:11:7:11:27 | path | normalizedPaths.js:13:19:13:22 | path |
@@ -9844,6 +9904,8 @@ edges
| TaintedPath.js:213:45:213:48 | path | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:213:45:213:48 | path | This path depends on $@. | TaintedPath.js:211:24:211:30 | req.url | a user-provided value |
| TaintedPath.js:214:35:214:38 | path | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:214:35:214:38 | path | This path depends on $@. | TaintedPath.js:211:24:211:30 | req.url | a user-provided value |
| express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | This path depends on $@. | express.js:8:20:8:32 | req.query.bar | a user-provided value |
| handlebars.js:11:32:11:39 | filePath | handlebars.js:29:46:29:60 | req.params.path | handlebars.js:11:32:11:39 | filePath | This path depends on $@. | handlebars.js:29:46:29:60 | req.params.path | a user-provided value |
| handlebars.js:15:25:15:32 | filePath | handlebars.js:43:15:43:29 | req.params.path | handlebars.js:15:25:15:32 | filePath | This path depends on $@. | handlebars.js:43:15:43:29 | req.params.path | a user-provided value |
| normalizedPaths.js:13:19:13:22 | path | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:13:19:13:22 | path | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value |
| normalizedPaths.js:14:19:14:29 | './' + path | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:14:19:14:29 | './' + path | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value |
| normalizedPaths.js:15:19:15:38 | path + '/index.html' | normalizedPaths.js:11:14:11:27 | req.query.path | normalizedPaths.js:15:19:15:38 | path + '/index.html' | This path depends on $@. | normalizedPaths.js:11:14:11:27 | req.query.path | a user-provided value |

View File

@@ -0,0 +1,52 @@
const express = require('express');
const hb = require("handlebars");
const fs = require("fs");
const app = express();
const data = {};
function init() {
hb.registerHelper("catFile", function catFile(filePath) {
return fs.readFileSync(filePath); // SINK (reads file)
});
hb.registerHelper("prependToLines", function prependToLines(prefix, filePath) {
return fs
.readFileSync(filePath)
.split("\n")
.map((line) => prefix + line)
.join("\n");
});
data.compiledFileAccess = hb.compile("contents of file {{path}} are: {{catFile path}}")
data.compiledBenign = hb.compile("hello, {{name}}");
data.compiledUnknown = hb.compile(fs.readFileSync("greeting.template"));
data.compiledMixed = hb.compile("helpers may have several args, like here: {{prependToLines prefix path}}");
}
init();
app.get('/some/path1', function (req, res) {
res.send(data.compiledFileAccess({ path: req.params.path })); // NOT ALLOWED (template uses vulnerable catFile)
});
app.get('/some/path2', function (req, res) {
res.send(data.compiledBenign({ name: req.params.name })); // ALLOWED (this template does not use catFile)
});
app.get('/some/path3', function (req, res) {
res.send(data.compiledUnknown({ name: req.params.name })); // ALLOWED (could be using a vulnerable helper, but we'll assume it's ok)
});
app.get('/some/path4', function (req, res) {
res.send(data.compiledMixed({
prefix: ">>> ",
path: req.params.path // NOT ALLOWED (template uses vulnerable helper)
}));
});
app.get('/some/path5', function (req, res) {
res.send(data.compiledMixed({
prefix: req.params.prefix, // ALLOWED (this parameter is safe)
path: "data/path-5.txt"
}));
});

View File

@@ -136,6 +136,10 @@ nodes
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
| custom-element.js:5:26:5:36 | window.name |
| custom-element.js:5:26:5:36 | window.name |
| custom-element.js:5:26:5:36 | window.name |
| custom-element.js:5:26:5:36 | window.name |
| d3.js:4:12:4:22 | window.name |
| d3.js:4:12:4:22 | window.name |
| d3.js:4:12:4:22 | window.name |
@@ -1130,6 +1134,7 @@ edges
| clipboard.ts:24:23:24:58 | e.clipb ... /html') | clipboard.ts:24:23:24:58 | e.clipb ... /html') |
| clipboard.ts:29:19:29:54 | e.clipb ... /html') | clipboard.ts:29:19:29:54 | e.clipb ... /html') |
| clipboard.ts:33:19:33:68 | e.origi ... /html') | clipboard.ts:33:19:33:68 | e.origi ... /html') |
| custom-element.js:5:26:5:36 | window.name | custom-element.js:5:26:5:36 | window.name |
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
@@ -2062,6 +2067,7 @@ edges
| clipboard.ts:24:23:24:58 | e.clipb ... /html') | clipboard.ts:24:23:24:58 | e.clipb ... /html') | clipboard.ts:24:23:24:58 | e.clipb ... /html') | Cross-site scripting vulnerability due to $@. | clipboard.ts:24:23:24:58 | e.clipb ... /html') | user-provided value |
| clipboard.ts:29:19:29:54 | e.clipb ... /html') | clipboard.ts:29:19:29:54 | e.clipb ... /html') | clipboard.ts:29:19:29:54 | e.clipb ... /html') | Cross-site scripting vulnerability due to $@. | clipboard.ts:29:19:29:54 | e.clipb ... /html') | user-provided value |
| clipboard.ts:33:19:33:68 | e.origi ... /html') | clipboard.ts:33:19:33:68 | e.origi ... /html') | clipboard.ts:33:19:33:68 | e.origi ... /html') | Cross-site scripting vulnerability due to $@. | clipboard.ts:33:19:33:68 | e.origi ... /html') | user-provided value |
| custom-element.js:5:26:5:36 | window.name | custom-element.js:5:26:5:36 | window.name | custom-element.js:5:26:5:36 | window.name | Cross-site scripting vulnerability due to $@. | custom-element.js:5:26:5:36 | window.name | user-provided value |
| d3.js:11:15:11:24 | getTaint() | d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() | Cross-site scripting vulnerability due to $@. | d3.js:4:12:4:22 | window.name | user-provided value |
| d3.js:12:20:12:29 | getTaint() | d3.js:4:12:4:22 | window.name | d3.js:12:20:12:29 | getTaint() | Cross-site scripting vulnerability due to $@. | d3.js:4:12:4:22 | window.name | user-provided value |
| d3.js:14:20:14:29 | getTaint() | d3.js:4:12:4:22 | window.name | d3.js:14:20:14:29 | getTaint() | Cross-site scripting vulnerability due to $@. | d3.js:4:12:4:22 | window.name | user-provided value |

View File

@@ -136,6 +136,10 @@ nodes
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
| custom-element.js:5:26:5:36 | window.name |
| custom-element.js:5:26:5:36 | window.name |
| custom-element.js:5:26:5:36 | window.name |
| custom-element.js:5:26:5:36 | window.name |
| d3.js:4:12:4:22 | window.name |
| d3.js:4:12:4:22 | window.name |
| d3.js:4:12:4:22 | window.name |
@@ -1180,6 +1184,7 @@ edges
| clipboard.ts:24:23:24:58 | e.clipb ... /html') | clipboard.ts:24:23:24:58 | e.clipb ... /html') |
| clipboard.ts:29:19:29:54 | e.clipb ... /html') | clipboard.ts:29:19:29:54 | e.clipb ... /html') |
| clipboard.ts:33:19:33:68 | e.origi ... /html') | clipboard.ts:33:19:33:68 | e.origi ... /html') |
| custom-element.js:5:26:5:36 | window.name | custom-element.js:5:26:5:36 | window.name |
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |

View File

@@ -0,0 +1,7 @@
import * as dummy from 'dummy';
class CustomElm extends HTMLElement {
test() {
this.innerHTML = window.name; // NOT OK
}
}

View File

@@ -129,6 +129,16 @@ nodes
| xss-through-dom.js:115:16:115:18 | src |
| xss-through-dom.js:117:26:117:28 | src |
| xss-through-dom.js:117:26:117:28 | src |
| xss-through-dom.js:120:23:120:37 | ev.target.files |
| xss-through-dom.js:120:23:120:37 | ev.target.files |
| xss-through-dom.js:120:23:120:40 | ev.target.files[0] |
| xss-through-dom.js:120:23:120:45 | ev.targ ... 0].name |
| xss-through-dom.js:120:23:120:45 | ev.targ ... 0].name |
| xss-through-dom.js:122:33:122:71 | URL.cre ... les[0]) |
| xss-through-dom.js:122:33:122:71 | URL.cre ... les[0]) |
| xss-through-dom.js:122:53:122:67 | ev.target.files |
| xss-through-dom.js:122:53:122:67 | ev.target.files |
| xss-through-dom.js:122:53:122:70 | ev.target.files[0] |
edges
| forms.js:8:23:8:28 | values | forms.js:9:31:9:36 | values |
| forms.js:8:23:8:28 | values | forms.js:9:31:9:36 | values |
@@ -207,6 +217,14 @@ edges
| xss-through-dom.js:114:11:114:52 | src | xss-through-dom.js:117:26:117:28 | src |
| xss-through-dom.js:114:17:114:52 | documen ... k").src | xss-through-dom.js:114:11:114:52 | src |
| xss-through-dom.js:114:17:114:52 | documen ... k").src | xss-through-dom.js:114:11:114:52 | src |
| xss-through-dom.js:120:23:120:37 | ev.target.files | xss-through-dom.js:120:23:120:40 | ev.target.files[0] |
| xss-through-dom.js:120:23:120:37 | ev.target.files | xss-through-dom.js:120:23:120:40 | ev.target.files[0] |
| xss-through-dom.js:120:23:120:40 | ev.target.files[0] | xss-through-dom.js:120:23:120:45 | ev.targ ... 0].name |
| xss-through-dom.js:120:23:120:40 | ev.target.files[0] | xss-through-dom.js:120:23:120:45 | ev.targ ... 0].name |
| xss-through-dom.js:122:53:122:67 | ev.target.files | xss-through-dom.js:122:53:122:70 | ev.target.files[0] |
| xss-through-dom.js:122:53:122:67 | ev.target.files | xss-through-dom.js:122:53:122:70 | ev.target.files[0] |
| xss-through-dom.js:122:53:122:70 | ev.target.files[0] | xss-through-dom.js:122:33:122:71 | URL.cre ... les[0]) |
| xss-through-dom.js:122:53:122:70 | ev.target.files[0] | xss-through-dom.js:122:33:122:71 | URL.cre ... les[0]) |
#select
| forms.js:9:31:9:40 | values.foo | forms.js:8:23:8:28 | values | forms.js:9:31:9:40 | values.foo | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:8:23:8:28 | values | DOM text |
| forms.js:12:31:12:40 | values.bar | forms.js:11:24:11:29 | values | forms.js:12:31:12:40 | values.bar | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:11:24:11:29 | values | DOM text |
@@ -242,3 +260,5 @@ edges
| xss-through-dom.js:96:17:96:47 | $("#foo ... ].value | xss-through-dom.js:96:17:96:47 | $("#foo ... ].value | xss-through-dom.js:96:17:96:47 | $("#foo ... ].value | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:96:17:96:47 | $("#foo ... ].value | DOM text |
| xss-through-dom.js:109:31:109:70 | "<a src ... oo</a>" | xss-through-dom.js:109:45:109:55 | this.el.src | xss-through-dom.js:109:31:109:70 | "<a src ... oo</a>" | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:109:45:109:55 | this.el.src | DOM text |
| xss-through-dom.js:115:16:115:18 | src | xss-through-dom.js:114:17:114:52 | documen ... k").src | xss-through-dom.js:115:16:115:18 | src | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:114:17:114:52 | documen ... k").src | DOM text |
| xss-through-dom.js:120:23:120:45 | ev.targ ... 0].name | xss-through-dom.js:120:23:120:37 | ev.target.files | xss-through-dom.js:120:23:120:45 | ev.targ ... 0].name | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:120:23:120:37 | ev.target.files | DOM text |
| xss-through-dom.js:122:33:122:71 | URL.cre ... les[0]) | xss-through-dom.js:122:53:122:67 | ev.target.files | xss-through-dom.js:122:33:122:71 | URL.cre ... les[0]) | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:122:53:122:67 | ev.target.files | DOM text |

View File

@@ -115,4 +115,10 @@ class Sub extends Super {
$("#id").html(src); // NOT OK.
$("#id").attr("src", src); // OK
$("input.foo")[0].onchange = function (ev) {
$("#id").html(ev.target.files[0].name); // NOT OK.
$("img#id").attr("src", URL.createObjectURL(ev.target.files[0])); // NOT OK
}
})();

View File

@@ -37,12 +37,14 @@ edges
| build-leaks.js:15:24:15:34 | process.env | build-leaks.js:14:18:14:20 | env |
| build-leaks.js:15:24:15:34 | process.env | build-leaks.js:14:18:14:20 | env |
| build-leaks.js:16:20:16:22 | env | build-leaks.js:13:17:19:10 | Object. ... }) |
| build-leaks.js:16:20:16:22 | env | build-leaks.js:14:18:14:20 | env |
| build-leaks.js:21:11:26:5 | stringifed | build-leaks.js:30:22:30:31 | stringifed |
| build-leaks.js:21:24:26:5 | {\\n ... )\\n } | build-leaks.js:21:11:26:5 | stringifed |
| build-leaks.js:22:24:25:14 | Object. ... }, {}) | build-leaks.js:21:24:26:5 | {\\n ... )\\n } |
| build-leaks.js:22:49:22:51 | env | build-leaks.js:24:20:24:22 | env |
| build-leaks.js:23:39:23:41 | raw | build-leaks.js:22:49:22:51 | env |
| build-leaks.js:24:20:24:22 | env | build-leaks.js:22:24:25:14 | Object. ... }, {}) |
| build-leaks.js:24:20:24:22 | env | build-leaks.js:22:49:22:51 | env |
| build-leaks.js:30:22:30:31 | stringifed | build-leaks.js:34:26:34:57 | getEnv( ... ngified |
| build-leaks.js:30:22:30:31 | stringifed | build-leaks.js:34:26:34:57 | getEnv( ... ngified |
| build-leaks.js:40:9:40:60 | pw | build-leaks.js:41:82:41:83 | pw |

View File

@@ -0,0 +1,3 @@
| MissingRateLimiting.js:4:19:8:1 | functio ... ath);\\n} | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:7:5:7:22 | res.sendFile(path) | a file system access |
| MissingRateLimiting.js:25:19:25:20 | f1 | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:13:5:13:22 | res.sendFile(path) | a file system access |
| MissingRateLimiting.js:25:27:25:28 | f3 | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:22:5:22:22 | res.sendFile(path) | a file system access |

View File

@@ -1,11 +0,0 @@
| MissingRateLimiting.js:4:19:8:1 | functio ... ath);\\n} | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:7:5:7:22 | res.sendFile(path) | a file system access |
| MissingRateLimiting.js:25:19:25:20 | f1 | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:13:5:13:22 | res.sendFile(path) | a file system access |
| MissingRateLimiting.js:25:27:25:28 | f3 | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:22:5:22:22 | res.sendFile(path) | a file system access |
| tst.js:22:24:22:40 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
| tst.js:35:20:35:36 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
| tst.js:36:20:36:36 | expensiveHandler2 | This route handler performs $@, but is not rate-limited. | tst.js:15:40:15:73 | fs.writ ... quest") | a file system access |
| tst.js:37:20:37:36 | expensiveHandler3 | This route handler performs $@, but is not rate-limited. | tst.js:16:40:16:70 | child_p ... /true") | a system command |
| tst.js:38:20:38:36 | expensiveHandler4 | This route handler performs $@, but is not rate-limited. | tst.js:17:40:17:83 | connect ... ution') | a database access |
| tst.js:64:25:64:63 | functio ... req); } | This route handler performs $@, but is not rate-limited. | tst.js:64:46:64:60 | verifyUser(req) | authorization |
| tst.js:76:25:76:53 | catchAs ... ndler1) | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
| tst.js:88:24:88:40 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |

View File

@@ -0,0 +1,3 @@
import javascript
import semmle.javascript.security.dataflow.ResourceExhaustionQuery
import testUtilities.ConsistencyChecking

View File

@@ -0,0 +1,148 @@
nodes
| documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url |
| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url |
| documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay |
| documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay |
| resource-exhaustion.js:5:7:5:42 | s |
| resource-exhaustion.js:5:11:5:34 | url.par ... , true) |
| resource-exhaustion.js:5:11:5:40 | url.par ... ).query |
| resource-exhaustion.js:5:11:5:42 | url.par ... query.s |
| resource-exhaustion.js:5:21:5:27 | req.url |
| resource-exhaustion.js:5:21:5:27 | req.url |
| resource-exhaustion.js:6:7:6:21 | n |
| resource-exhaustion.js:6:11:6:21 | parseInt(s) |
| resource-exhaustion.js:6:20:6:20 | s |
| resource-exhaustion.js:11:21:11:21 | s |
| resource-exhaustion.js:11:21:11:21 | s |
| resource-exhaustion.js:12:21:12:21 | n |
| resource-exhaustion.js:12:21:12:21 | n |
| resource-exhaustion.js:13:21:13:21 | n |
| resource-exhaustion.js:13:21:13:21 | n |
| resource-exhaustion.js:14:16:14:16 | n |
| resource-exhaustion.js:14:16:14:16 | n |
| resource-exhaustion.js:15:22:15:22 | n |
| resource-exhaustion.js:15:22:15:22 | n |
| resource-exhaustion.js:16:26:16:26 | n |
| resource-exhaustion.js:16:26:16:26 | n |
| resource-exhaustion.js:20:20:20:20 | n |
| resource-exhaustion.js:20:20:20:20 | n |
| resource-exhaustion.js:22:18:22:18 | n |
| resource-exhaustion.js:22:18:22:18 | n |
| resource-exhaustion.js:27:9:27:9 | n |
| resource-exhaustion.js:27:9:27:9 | n |
| resource-exhaustion.js:28:13:28:13 | n |
| resource-exhaustion.js:28:13:28:13 | n |
| resource-exhaustion.js:29:9:29:9 | n |
| resource-exhaustion.js:29:9:29:9 | n |
| resource-exhaustion.js:30:9:30:9 | n |
| resource-exhaustion.js:30:9:30:9 | n |
| resource-exhaustion.js:31:9:31:9 | n |
| resource-exhaustion.js:31:9:31:9 | n |
| resource-exhaustion.js:32:9:32:9 | n |
| resource-exhaustion.js:32:9:32:9 | n |
| resource-exhaustion.js:34:12:34:12 | n |
| resource-exhaustion.js:34:12:34:12 | n |
| resource-exhaustion.js:35:12:35:12 | s |
| resource-exhaustion.js:35:12:35:12 | s |
| resource-exhaustion.js:81:17:81:17 | n |
| resource-exhaustion.js:81:17:81:17 | n |
| resource-exhaustion.js:82:17:82:17 | s |
| resource-exhaustion.js:82:17:82:17 | s |
| resource-exhaustion.js:83:18:83:18 | n |
| resource-exhaustion.js:83:18:83:18 | n |
| resource-exhaustion.js:84:18:84:18 | s |
| resource-exhaustion.js:84:18:84:18 | s |
| resource-exhaustion.js:88:16:88:16 | n |
| resource-exhaustion.js:88:16:88:16 | n |
| resource-exhaustion.js:92:18:92:18 | n |
| resource-exhaustion.js:92:18:92:18 | n |
edges
| documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay | documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) | documentaion-examples/ResourceExhaustion_timeout.js:5:6:5:59 | delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:52 | url.par ... ).query | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay |
| documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:58 | url.par ... y.delay | documentaion-examples/ResourceExhaustion_timeout.js:5:14:5:59 | parseIn ... .delay) |
| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) |
| documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentaion-examples/ResourceExhaustion_timeout.js:5:23:5:46 | url.par ... , true) |
| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:6:20:6:20 | s |
| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:11:21:11:21 | s |
| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:11:21:11:21 | s |
| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:35:12:35:12 | s |
| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:35:12:35:12 | s |
| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:82:17:82:17 | s |
| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:82:17:82:17 | s |
| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:84:18:84:18 | s |
| resource-exhaustion.js:5:7:5:42 | s | resource-exhaustion.js:84:18:84:18 | s |
| resource-exhaustion.js:5:11:5:34 | url.par ... , true) | resource-exhaustion.js:5:11:5:40 | url.par ... ).query |
| resource-exhaustion.js:5:11:5:40 | url.par ... ).query | resource-exhaustion.js:5:11:5:42 | url.par ... query.s |
| resource-exhaustion.js:5:11:5:42 | url.par ... query.s | resource-exhaustion.js:5:7:5:42 | s |
| resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:5:11:5:34 | url.par ... , true) |
| resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:5:11:5:34 | url.par ... , true) |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:12:21:12:21 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:12:21:12:21 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:13:21:13:21 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:13:21:13:21 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:14:16:14:16 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:14:16:14:16 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:15:22:15:22 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:15:22:15:22 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:16:26:16:26 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:16:26:16:26 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:20:20:20:20 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:20:20:20:20 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:22:18:22:18 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:22:18:22:18 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:27:9:27:9 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:27:9:27:9 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:28:13:28:13 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:28:13:28:13 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:29:9:29:9 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:29:9:29:9 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:30:9:30:9 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:30:9:30:9 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:31:9:31:9 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:31:9:31:9 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:32:9:32:9 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:32:9:32:9 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:34:12:34:12 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:34:12:34:12 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:81:17:81:17 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:81:17:81:17 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:83:18:83:18 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:83:18:83:18 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:88:16:88:16 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:88:16:88:16 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:92:18:92:18 | n |
| resource-exhaustion.js:6:7:6:21 | n | resource-exhaustion.js:92:18:92:18 | n |
| resource-exhaustion.js:6:11:6:21 | parseInt(s) | resource-exhaustion.js:6:7:6:21 | n |
| resource-exhaustion.js:6:20:6:20 | s | resource-exhaustion.js:6:11:6:21 | parseInt(s) |
#select
| documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | documentaion-examples/ResourceExhaustion_timeout.js:7:16:7:20 | delay | This creates a timer with a user-controlled duration from $@. | documentaion-examples/ResourceExhaustion_timeout.js:5:33:5:39 | req.url | here |
| resource-exhaustion.js:11:21:11:21 | s | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:11:21:11:21 | s | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:12:21:12:21 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:12:21:12:21 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:13:21:13:21 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:13:21:13:21 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:14:16:14:16 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:14:16:14:16 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:15:22:15:22 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:15:22:15:22 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:16:26:16:26 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:16:26:16:26 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:20:20:20:20 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:20:20:20:20 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:22:18:22:18 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:22:18:22:18 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:27:9:27:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:27:9:27:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:28:13:28:13 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:28:13:28:13 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:29:9:29:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:29:9:29:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:30:9:30:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:30:9:30:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:31:9:31:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:31:9:31:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:32:9:32:9 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:32:9:32:9 | n | This creates an array with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:34:12:34:12 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:34:12:34:12 | n | This creates a string with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:35:12:35:12 | s | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:35:12:35:12 | s | This creates a string with a user-controlled length from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:81:17:81:17 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:81:17:81:17 | n | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:82:17:82:17 | s | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:82:17:82:17 | s | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:83:18:83:18 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:83:18:83:18 | n | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:84:18:84:18 | s | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:84:18:84:18 | s | This creates a timer with a user-controlled duration from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:88:16:88:16 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:88:16:88:16 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |
| resource-exhaustion.js:92:18:92:18 | n | resource-exhaustion.js:5:21:5:27 | req.url | resource-exhaustion.js:92:18:92:18 | n | This creates a buffer with a user-controlled size from $@. | resource-exhaustion.js:5:21:5:27 | req.url | here |

View File

@@ -0,0 +1 @@
Security/CWE-770/ResourceExhaustion.ql

View File

@@ -0,0 +1,105 @@
var http = require("http"),
url = require("url");
var server = http.createServer(function(req, res) {
let s = url.parse(req.url, true).query.s;
let n = parseInt(s);
Buffer.from(s); // OK
Buffer.from(n); // OK
Buffer.from(x, n); // OK
Buffer.from(x, y, s); // NOT OK
Buffer.from(x, y, n); // NOT OK
Buffer.from(x, y, n); // NOT OK
Buffer.alloc(n); // NOT OK
Buffer.allocUnsafe(n); // NOT OK
Buffer.allocUnsafeSlow(n); // NOT OK
new Buffer(n); // NOT OK - but not flagged [INCONSISTENCY]
new Buffer(x, n); // OK
new Buffer(x, y, n); // NOT OK
new SlowBuffer(n); // NOT OK
Array(n); // OK
new Array(n); // OK
Array(n).map(); // NOT OK
new Array(n).map(); // NOT OK
Array(n).fill(); // NOT OK
Array(n).join(); // NOT OK
Array(n).toString(); // NOT OK
Array(n) + x; // NOT OK
x.repeat(n); // NOT OK
x.repeat(s); // NOT OK
new Buffer(n * x); // NOT OK - but not flagged [INCONSISTENCY]
new Buffer(n + n); // NOT OK - but not flagged [INCONSISTENCY]
new Buffer(n + x); // OK (maybe)
new Buffer(n + s); // OK (this is a string if `s` is a string)
new Buffer(s + 2); // OK (this is a string if `s` is a string)
new Buffer(s + s); // OK
new Buffer(n + "X"); // OK
new Buffer(Math.ceil(s)); // NOT OK - but not flagged [INCONSISTENCY]
new Buffer(Number(s)); // NOT OK - but not flagged [INCONSISTENCY]
new Buffer(new Number(s)); // OK
new Buffer(s + x.length); // OK (this is a string if `s` is a string)
new Buffer(s.length); // NOT OK - but not flagged [INCONSISTENCY]
if (n < 100) {
new Buffer(n); // OK
} else {
new Buffer(n); // NOT OK - but not flagged [INCONSISTENCY]
}
let ns = x ? n : s;
new Buffer(ns); // NOT OK - but not flagged [INCONSISTENCY]
new Buffer(n.toString()); // OK
if (typeof n === "string") {
new Buffer(n); // OK
} else {
new Buffer(n); // NOT OK - but not flagged [INCONSISTENCY]
}
if (typeof n === "number") {
new Buffer(n); // NOT OK - but not flagged [INCONSISTENCY]
} else {
new Buffer(n); // OK
}
if (typeof s === "number") {
new Buffer(s); // NOT OK - but not flagged [INCONSISTENCY]
} else {
new Buffer(s); // OK
}
setTimeout(f, n); // NOT OK
setTimeout(f, s); // NOT OK
setInterval(f, n); // NOT OK
setInterval(f, s); // NOT OK
Buffer.alloc(n.length); // OK - only allocing as much as the length of the input.
Buffer.alloc(n); // NOT OK
if (n < 1000) {
Buffer.alloc(n); // OK - length check
} else {
Buffer.alloc(n); // NOT OK - NO length check
}
});
function browser() {
const delay = parseInt(window.location.search.replace('?', '')) || 5000;
setTimeout(() => {
console.log("f00");
}, delay); // OK - source is client side
window.onmessage = (e) => {
setTimeout(() => {}, e.data); // OK - source is client side
}
}