diff --git a/javascript/ql/lib/change-notes/2025-01-03-angular-source-sink.md b/javascript/ql/lib/change-notes/2025-01-03-angular-source-sink.md new file mode 100644 index 000000000000..a1ca70800b3b --- /dev/null +++ b/javascript/ql/lib/change-notes/2025-01-03-angular-source-sink.md @@ -0,0 +1,4 @@ +--- +category: majorAnalysis +--- +* Added new XSS sink where `innerHTML` or `outerHTML` is assigned to with the Angular Renderer2 API, plus modeled this API as a general attribute setter diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll b/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll index 6c52001ac527..a2702dbc7429 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll @@ -556,6 +556,41 @@ module Angular2 { } } + /** + * A DOM attribute write, using the AngularJS Renderer2 API: a call to `Renderer2.setProperty`. + */ + class AngularRenderer2AttributeDefinition extends DOM::AttributeDefinition { + DataFlow::Node propertyNode; + DataFlow::Node valueNode; + DataFlow::Node elementNode; + + AngularRenderer2AttributeDefinition() { + exists(API::CallNode setProperty | + setProperty = + API::moduleImport("@angular/core") + .getMember("Renderer2") + .getInstance() + .getMember("setProperty") + .getACall() and + elementNode = setProperty.getArgument(0) and + propertyNode = setProperty.getArgument(1) and + valueNode = setProperty.getArgument(2) and + this = setProperty.asExpr() + ) + } + + override string getName() { result = propertyNode.getStringValue() } + + /** + * Get the `DataFlow::Node` that is affected by this Attribute Definition. + * + * Defined instead of defining `getElement()`, which requires returning a DOM element definition, `ElementDefinition`. + */ + DataFlow::Node getElementNode() { result = elementNode } + + override DataFlow::Node getValueNode() { result = valueNode } + } + /** * A source of DOM events originating from the `$event` variable in an event handler installed in an Angular template. */ diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll index b9f27c6a8c2e..1440fb5539d7 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll @@ -287,6 +287,20 @@ module DomBasedXss { } } + /** + * A write to the `innerHTML` or `outerHTML` property of a DOM element, viewed as an XSS sink. + * + * Uses the Angular Renderer2 API, instead of the default `Element.innerHTML` property. + */ + class AngularRender2SetPropertyInnerHtmlSink2 extends Sink { + AngularRender2SetPropertyInnerHtmlSink2() { + exists(Angular2::AngularRenderer2AttributeDefinition attrDef | + attrDef.getName() = ["innerHTML", "outerHTML"] and + this = attrDef.getValueNode() + ) + } + } + /** * A value being piped into the `safe` pipe in a template file, * disabling subsequent HTML escaping. diff --git a/javascript/ql/src/Security/CWE-079/ExceptionXss.expected b/javascript/ql/src/Security/CWE-079/ExceptionXss.expected new file mode 100644 index 000000000000..7f73c5678172 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/ExceptionXss.expected @@ -0,0 +1,6 @@ +nodes +| examples/ExceptionXssAjv.js:11:18:11:33 | ajv.errorsText() | semmle.label | ajv.errorsText() | +edges +subpaths +#select +| examples/ExceptionXssAjv.js:11:18:11:33 | ajv.errorsText() | examples/ExceptionXssAjv.js:11:18:11:33 | ajv.errorsText() | examples/ExceptionXssAjv.js:11:18:11:33 | ajv.errorsText() | $@ is reinterpreted as HTML without escaping meta-characters. | examples/ExceptionXssAjv.js:11:18:11:33 | ajv.errorsText() | JSON schema validation error | diff --git a/javascript/ql/src/Security/CWE-079/ReflectedXss.expected b/javascript/ql/src/Security/CWE-079/ReflectedXss.expected new file mode 100644 index 000000000000..a7b27a2e9885 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/ReflectedXss.expected @@ -0,0 +1,8 @@ +edges +| examples/ReflectedXss.js:6:33:6:45 | req.params.id | examples/ReflectedXss.js:6:14:6:45 | "Unknow ... rams.id | provenance | | +nodes +| examples/ReflectedXss.js:6:14:6:45 | "Unknow ... rams.id | semmle.label | "Unknow ... rams.id | +| examples/ReflectedXss.js:6:33:6:45 | req.params.id | semmle.label | req.params.id | +subpaths +#select +| examples/ReflectedXss.js:6:14:6:45 | "Unknow ... rams.id | examples/ReflectedXss.js:6:33:6:45 | req.params.id | examples/ReflectedXss.js:6:14:6:45 | "Unknow ... rams.id | Cross-site scripting vulnerability due to a $@. | examples/ReflectedXss.js:6:33:6:45 | req.params.id | user-provided value | diff --git a/javascript/ql/src/Security/CWE-079/StoredXss.expected b/javascript/ql/src/Security/CWE-079/StoredXss.expected new file mode 100644 index 000000000000..8b0e8dc8bde7 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/StoredXss.expected @@ -0,0 +1,21 @@ +edges +| examples/StoredXss.js:5:44:5:52 | fileNames | examples/StoredXss.js:7:9:7:17 | fileNames | provenance | | +| examples/StoredXss.js:7:9:7:17 | fileNames | examples/StoredXss.js:7:27:7:34 | fileName | provenance | | +| examples/StoredXss.js:7:9:7:17 | fileNames | examples/StoredXss.js:11:9:11:12 | list | provenance | | +| examples/StoredXss.js:7:27:7:34 | fileName | examples/StoredXss.js:9:30:9:37 | fileName | provenance | | +| examples/StoredXss.js:9:30:9:37 | fileName | examples/StoredXss.js:9:13:9:47 | list | provenance | | +| examples/StoredXss.js:11:9:11:12 | list | examples/StoredXss.js:11:9:11:23 | list | provenance | | +| examples/StoredXss.js:11:9:11:23 | list | examples/StoredXss.js:12:18:12:21 | list | provenance | | +nodes +| examples/StoredXss.js:5:44:5:52 | fileNames | semmle.label | fileNames | +| examples/StoredXss.js:7:9:7:17 | fileNames | semmle.label | fileNames | +| examples/StoredXss.js:7:27:7:34 | fileName | semmle.label | fileName | +| examples/StoredXss.js:9:13:9:47 | list | semmle.label | list | +| examples/StoredXss.js:9:30:9:37 | fileName | semmle.label | fileName | +| examples/StoredXss.js:11:9:11:12 | list | semmle.label | list | +| examples/StoredXss.js:11:9:11:23 | list | semmle.label | list | +| examples/StoredXss.js:12:18:12:21 | list | semmle.label | list | +subpaths +| examples/StoredXss.js:7:9:7:17 | fileNames | examples/StoredXss.js:7:27:7:34 | fileName | examples/StoredXss.js:9:13:9:47 | list | examples/StoredXss.js:11:9:11:12 | list | +#select +| examples/StoredXss.js:12:18:12:21 | list | examples/StoredXss.js:5:44:5:52 | fileNames | examples/StoredXss.js:12:18:12:21 | list | Stored cross-site scripting vulnerability due to $@. | examples/StoredXss.js:5:44:5:52 | fileNames | stored value | diff --git a/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.expected b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.expected new file mode 100644 index 000000000000..57a021bf40a2 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.expected @@ -0,0 +1,4 @@ +nodes +edges +subpaths +#select diff --git a/javascript/ql/src/Security/CWE-079/UnsafeJQueryPlugin.expected b/javascript/ql/src/Security/CWE-079/UnsafeJQueryPlugin.expected new file mode 100644 index 000000000000..e805f2863381 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/UnsafeJQueryPlugin.expected @@ -0,0 +1,10 @@ +edges +| examples/UnsafeJQueryPlugin.js:1:31:1:37 | options | examples/UnsafeJQueryPlugin.js:3:22:3:28 | options | provenance | | +| examples/UnsafeJQueryPlugin.js:3:22:3:28 | options | examples/UnsafeJQueryPlugin.js:3:22:3:43 | options ... elector | provenance | | +nodes +| examples/UnsafeJQueryPlugin.js:1:31:1:37 | options | semmle.label | options | +| examples/UnsafeJQueryPlugin.js:3:22:3:28 | options | semmle.label | options | +| examples/UnsafeJQueryPlugin.js:3:22:3:43 | options ... elector | semmle.label | options ... elector | +subpaths +#select +| examples/UnsafeJQueryPlugin.js:3:22:3:43 | options ... elector | examples/UnsafeJQueryPlugin.js:1:31:1:37 | options | examples/UnsafeJQueryPlugin.js:3:22:3:43 | options ... elector | Potential XSS vulnerability in the $@. | examples/UnsafeJQueryPlugin.js:1:22:6:1 | functio ... ext);\\n} | '$.fn.copyText' plugin | diff --git a/javascript/ql/src/Security/CWE-079/Xss.expected b/javascript/ql/src/Security/CWE-079/Xss.expected new file mode 100644 index 000000000000..57a021bf40a2 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/Xss.expected @@ -0,0 +1,4 @@ +nodes +edges +subpaths +#select diff --git a/javascript/ql/src/Security/CWE-079/XssThroughDom.expected b/javascript/ql/src/Security/CWE-079/XssThroughDom.expected new file mode 100644 index 000000000000..c0c29d0863f5 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/XssThroughDom.expected @@ -0,0 +1,10 @@ +edges +| examples/XssThroughDom.js:2:9:2:44 | target | examples/XssThroughDom.js:3:7:3:12 | target | provenance | | +| examples/XssThroughDom.js:2:18:2:44 | $(this) ... arget") | examples/XssThroughDom.js:2:9:2:44 | target | provenance | | +nodes +| examples/XssThroughDom.js:2:9:2:44 | target | semmle.label | target | +| examples/XssThroughDom.js:2:18:2:44 | $(this) ... arget") | semmle.label | $(this) ... arget") | +| examples/XssThroughDom.js:3:7:3:12 | target | semmle.label | target | +subpaths +#select +| examples/XssThroughDom.js:3:7:3:12 | target | examples/XssThroughDom.js:2:18:2:44 | $(this) ... arget") | examples/XssThroughDom.js:3:7:3:12 | target | $@ is reinterpreted as HTML without escaping meta-characters. | examples/XssThroughDom.js:2:18:2:44 | $(this) ... arget") | DOM text | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected index c8f12b176f86..7d067911c197 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected @@ -8,24 +8,26 @@ nodes | addEventListener.js:10:21:10:25 | event | semmle.label | event | | addEventListener.js:12:24:12:28 | event | semmle.label | event | | addEventListener.js:12:24:12:33 | event.data | semmle.label | event.data | -| angular2-client.ts:22:44:22:71 | \\u0275getDOM ... ().href | semmle.label | \\u0275getDOM ... ().href | -| angular2-client.ts:24:44:24:69 | this.ro ... .params | semmle.label | this.ro ... .params | -| angular2-client.ts:24:44:24:73 | this.ro ... ams.foo | semmle.label | this.ro ... ams.foo | -| angular2-client.ts:25:44:25:74 | this.ro ... yParams | semmle.label | this.ro ... yParams | -| angular2-client.ts:25:44:25:78 | this.ro ... ams.foo | semmle.label | this.ro ... ams.foo | -| angular2-client.ts:26:44:26:71 | this.ro ... ragment | semmle.label | this.ro ... ragment | -| angular2-client.ts:27:44:27:82 | this.ro ... ('foo') | semmle.label | this.ro ... ('foo') | -| angular2-client.ts:28:44:28:87 | this.ro ... ('foo') | semmle.label | this.ro ... ('foo') | -| angular2-client.ts:30:46:30:59 | map.get('foo') | semmle.label | map.get('foo') | -| angular2-client.ts:33:44:33:74 | this.ro ... 1].path | semmle.label | this.ro ... 1].path | -| angular2-client.ts:34:44:34:80 | this.ro ... ameters | semmle.label | this.ro ... ameters | -| angular2-client.ts:34:44:34:82 | this.ro ... eters.x | semmle.label | this.ro ... eters.x | -| angular2-client.ts:35:44:35:91 | this.ro ... et('x') | semmle.label | this.ro ... et('x') | -| angular2-client.ts:36:44:36:89 | this.ro ... .params | semmle.label | this.ro ... .params | -| angular2-client.ts:36:44:36:91 | this.ro ... arams.x | semmle.label | this.ro ... arams.x | -| angular2-client.ts:38:44:38:58 | this.router.url | semmle.label | this.router.url | -| angular2-client.ts:40:45:40:59 | this.router.url | semmle.label | this.router.url | -| angular2-client.ts:44:44:44:76 | routeSn ... ('foo') | semmle.label | routeSn ... ('foo') | +| angular2-client.ts:24:44:24:71 | \\u0275getDOM ... ().href | semmle.label | \\u0275getDOM ... ().href | +| angular2-client.ts:26:44:26:69 | this.ro ... .params | semmle.label | this.ro ... .params | +| angular2-client.ts:26:44:26:73 | this.ro ... ams.foo | semmle.label | this.ro ... ams.foo | +| angular2-client.ts:27:44:27:74 | this.ro ... yParams | semmle.label | this.ro ... yParams | +| angular2-client.ts:27:44:27:78 | this.ro ... ams.foo | semmle.label | this.ro ... ams.foo | +| angular2-client.ts:28:44:28:71 | this.ro ... ragment | semmle.label | this.ro ... ragment | +| angular2-client.ts:29:44:29:82 | this.ro ... ('foo') | semmle.label | this.ro ... ('foo') | +| angular2-client.ts:30:44:30:87 | this.ro ... ('foo') | semmle.label | this.ro ... ('foo') | +| angular2-client.ts:32:46:32:59 | map.get('foo') | semmle.label | map.get('foo') | +| angular2-client.ts:35:44:35:74 | this.ro ... 1].path | semmle.label | this.ro ... 1].path | +| angular2-client.ts:36:44:36:80 | this.ro ... ameters | semmle.label | this.ro ... ameters | +| angular2-client.ts:36:44:36:82 | this.ro ... eters.x | semmle.label | this.ro ... eters.x | +| angular2-client.ts:37:44:37:91 | this.ro ... et('x') | semmle.label | this.ro ... et('x') | +| angular2-client.ts:38:44:38:89 | this.ro ... .params | semmle.label | this.ro ... .params | +| angular2-client.ts:38:44:38:91 | this.ro ... arams.x | semmle.label | this.ro ... arams.x | +| angular2-client.ts:40:44:40:58 | this.router.url | semmle.label | this.router.url | +| angular2-client.ts:42:45:42:59 | this.router.url | semmle.label | this.router.url | +| angular2-client.ts:43:75:43:105 | this.ro ... yParams | semmle.label | this.ro ... yParams | +| angular2-client.ts:43:75:43:109 | this.ro ... ams.foo | semmle.label | this.ro ... ams.foo | +| angular2-client.ts:47:44:47:76 | routeSn ... ('foo') | semmle.label | routeSn ... ('foo') | | angular-tempate-url.js:9:26:9:45 | Cookie.get("unsafe") | semmle.label | Cookie.get("unsafe") | | angular-tempate-url.js:13:30:13:31 | ev | semmle.label | ev | | angular-tempate-url.js:14:26:14:27 | ev | semmle.label | ev | @@ -637,10 +639,11 @@ edges | addEventListener.js:5:43:5:48 | {data} | addEventListener.js:5:43:5:48 | data | provenance | | | addEventListener.js:10:21:10:25 | event | addEventListener.js:12:24:12:28 | event | provenance | | | addEventListener.js:12:24:12:28 | event | addEventListener.js:12:24:12:33 | event.data | provenance | | -| angular2-client.ts:24:44:24:69 | this.ro ... .params | angular2-client.ts:24:44:24:73 | this.ro ... ams.foo | provenance | | -| angular2-client.ts:25:44:25:74 | this.ro ... yParams | angular2-client.ts:25:44:25:78 | this.ro ... ams.foo | provenance | | -| angular2-client.ts:34:44:34:80 | this.ro ... ameters | angular2-client.ts:34:44:34:82 | this.ro ... eters.x | provenance | | -| angular2-client.ts:36:44:36:89 | this.ro ... .params | angular2-client.ts:36:44:36:91 | this.ro ... arams.x | provenance | | +| angular2-client.ts:26:44:26:69 | this.ro ... .params | angular2-client.ts:26:44:26:73 | this.ro ... ams.foo | provenance | | +| angular2-client.ts:27:44:27:74 | this.ro ... yParams | angular2-client.ts:27:44:27:78 | this.ro ... ams.foo | provenance | | +| angular2-client.ts:36:44:36:80 | this.ro ... ameters | angular2-client.ts:36:44:36:82 | this.ro ... eters.x | provenance | | +| angular2-client.ts:38:44:38:89 | this.ro ... .params | angular2-client.ts:38:44:38:91 | this.ro ... arams.x | provenance | | +| angular2-client.ts:43:75:43:105 | this.ro ... yParams | angular2-client.ts:43:75:43:109 | this.ro ... ams.foo | provenance | | | angular-tempate-url.js:13:30:13:31 | ev | angular-tempate-url.js:14:26:14:27 | ev | provenance | | | angular-tempate-url.js:14:26:14:27 | ev | angular-tempate-url.js:14:26:14:32 | ev.data | provenance | | | angular-tempate-url.js:14:26:14:32 | ev.data | angular-tempate-url.js:9:26:9:45 | Cookie.get("unsafe") | provenance | | @@ -1181,20 +1184,21 @@ subpaths | addEventListener.js:2:20:2:29 | event.data | addEventListener.js:1:43:1:47 | event | addEventListener.js:2:20:2:29 | event.data | Cross-site scripting vulnerability due to $@. | addEventListener.js:1:43:1:47 | event | user-provided value | | addEventListener.js:6:20:6:23 | data | addEventListener.js:5:43:5:48 | {data} | addEventListener.js:6:20:6:23 | data | Cross-site scripting vulnerability due to $@. | addEventListener.js:5:43:5:48 | {data} | user-provided value | | addEventListener.js:12:24:12:33 | event.data | addEventListener.js:10:21:10:25 | event | addEventListener.js:12:24:12:33 | event.data | Cross-site scripting vulnerability due to $@. | addEventListener.js:10:21:10:25 | event | user-provided value | -| angular2-client.ts:22:44:22:71 | \\u0275getDOM ... ().href | angular2-client.ts:22:44:22:71 | \\u0275getDOM ... ().href | angular2-client.ts:22:44:22:71 | \\u0275getDOM ... ().href | Cross-site scripting vulnerability due to $@. | angular2-client.ts:22:44:22:71 | \\u0275getDOM ... ().href | user-provided value | -| angular2-client.ts:24:44:24:73 | this.ro ... ams.foo | angular2-client.ts:24:44:24:69 | this.ro ... .params | angular2-client.ts:24:44:24:73 | this.ro ... ams.foo | Cross-site scripting vulnerability due to $@. | angular2-client.ts:24:44:24:69 | this.ro ... .params | user-provided value | -| angular2-client.ts:25:44:25:78 | this.ro ... ams.foo | angular2-client.ts:25:44:25:74 | this.ro ... yParams | angular2-client.ts:25:44:25:78 | this.ro ... ams.foo | Cross-site scripting vulnerability due to $@. | angular2-client.ts:25:44:25:74 | this.ro ... yParams | user-provided value | -| angular2-client.ts:26:44:26:71 | this.ro ... ragment | angular2-client.ts:26:44:26:71 | this.ro ... ragment | angular2-client.ts:26:44:26:71 | this.ro ... ragment | Cross-site scripting vulnerability due to $@. | angular2-client.ts:26:44:26:71 | this.ro ... ragment | user-provided value | -| angular2-client.ts:27:44:27:82 | this.ro ... ('foo') | angular2-client.ts:27:44:27:82 | this.ro ... ('foo') | angular2-client.ts:27:44:27:82 | this.ro ... ('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:27:44:27:82 | this.ro ... ('foo') | user-provided value | -| angular2-client.ts:28:44:28:87 | this.ro ... ('foo') | angular2-client.ts:28:44:28:87 | this.ro ... ('foo') | angular2-client.ts:28:44:28:87 | this.ro ... ('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:28:44:28:87 | this.ro ... ('foo') | user-provided value | -| angular2-client.ts:30:46:30:59 | map.get('foo') | angular2-client.ts:30:46:30:59 | map.get('foo') | angular2-client.ts:30:46:30:59 | map.get('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:30:46:30:59 | map.get('foo') | user-provided value | -| angular2-client.ts:33:44:33:74 | this.ro ... 1].path | angular2-client.ts:33:44:33:74 | this.ro ... 1].path | angular2-client.ts:33:44:33:74 | this.ro ... 1].path | Cross-site scripting vulnerability due to $@. | angular2-client.ts:33:44:33:74 | this.ro ... 1].path | user-provided value | -| angular2-client.ts:34:44:34:82 | this.ro ... eters.x | angular2-client.ts:34:44:34:80 | this.ro ... ameters | angular2-client.ts:34:44:34:82 | this.ro ... eters.x | Cross-site scripting vulnerability due to $@. | angular2-client.ts:34:44:34:80 | this.ro ... ameters | user-provided value | -| angular2-client.ts:35:44:35:91 | this.ro ... et('x') | angular2-client.ts:35:44:35:91 | this.ro ... et('x') | angular2-client.ts:35:44:35:91 | this.ro ... et('x') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:35:44:35:91 | this.ro ... et('x') | user-provided value | -| angular2-client.ts:36:44:36:91 | this.ro ... arams.x | angular2-client.ts:36:44:36:89 | this.ro ... .params | angular2-client.ts:36:44:36:91 | this.ro ... arams.x | Cross-site scripting vulnerability due to $@. | angular2-client.ts:36:44:36:89 | this.ro ... .params | user-provided value | -| angular2-client.ts:38:44:38:58 | this.router.url | angular2-client.ts:38:44:38:58 | this.router.url | angular2-client.ts:38:44:38:58 | this.router.url | Cross-site scripting vulnerability due to $@. | angular2-client.ts:38:44:38:58 | this.router.url | user-provided value | -| angular2-client.ts:40:45:40:59 | this.router.url | angular2-client.ts:40:45:40:59 | this.router.url | angular2-client.ts:40:45:40:59 | this.router.url | Cross-site scripting vulnerability due to $@. | angular2-client.ts:40:45:40:59 | this.router.url | user-provided value | -| angular2-client.ts:44:44:44:76 | routeSn ... ('foo') | angular2-client.ts:44:44:44:76 | routeSn ... ('foo') | angular2-client.ts:44:44:44:76 | routeSn ... ('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:44:44:44:76 | routeSn ... ('foo') | user-provided value | +| angular2-client.ts:24:44:24:71 | \\u0275getDOM ... ().href | angular2-client.ts:24:44:24:71 | \\u0275getDOM ... ().href | angular2-client.ts:24:44:24:71 | \\u0275getDOM ... ().href | Cross-site scripting vulnerability due to $@. | angular2-client.ts:24:44:24:71 | \\u0275getDOM ... ().href | user-provided value | +| angular2-client.ts:26:44:26:73 | this.ro ... ams.foo | angular2-client.ts:26:44:26:69 | this.ro ... .params | angular2-client.ts:26:44:26:73 | this.ro ... ams.foo | Cross-site scripting vulnerability due to $@. | angular2-client.ts:26:44:26:69 | this.ro ... .params | user-provided value | +| angular2-client.ts:27:44:27:78 | this.ro ... ams.foo | angular2-client.ts:27:44:27:74 | this.ro ... yParams | angular2-client.ts:27:44:27:78 | this.ro ... ams.foo | Cross-site scripting vulnerability due to $@. | angular2-client.ts:27:44:27:74 | this.ro ... yParams | user-provided value | +| angular2-client.ts:28:44:28:71 | this.ro ... ragment | angular2-client.ts:28:44:28:71 | this.ro ... ragment | angular2-client.ts:28:44:28:71 | this.ro ... ragment | Cross-site scripting vulnerability due to $@. | angular2-client.ts:28:44:28:71 | this.ro ... ragment | user-provided value | +| angular2-client.ts:29:44:29:82 | this.ro ... ('foo') | angular2-client.ts:29:44:29:82 | this.ro ... ('foo') | angular2-client.ts:29:44:29:82 | this.ro ... ('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:29:44:29:82 | this.ro ... ('foo') | user-provided value | +| angular2-client.ts:30:44:30:87 | this.ro ... ('foo') | angular2-client.ts:30:44:30:87 | this.ro ... ('foo') | angular2-client.ts:30:44:30:87 | this.ro ... ('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:30:44:30:87 | this.ro ... ('foo') | user-provided value | +| angular2-client.ts:32:46:32:59 | map.get('foo') | angular2-client.ts:32:46:32:59 | map.get('foo') | angular2-client.ts:32:46:32:59 | map.get('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:32:46:32:59 | map.get('foo') | user-provided value | +| angular2-client.ts:35:44:35:74 | this.ro ... 1].path | angular2-client.ts:35:44:35:74 | this.ro ... 1].path | angular2-client.ts:35:44:35:74 | this.ro ... 1].path | Cross-site scripting vulnerability due to $@. | angular2-client.ts:35:44:35:74 | this.ro ... 1].path | user-provided value | +| angular2-client.ts:36:44:36:82 | this.ro ... eters.x | angular2-client.ts:36:44:36:80 | this.ro ... ameters | angular2-client.ts:36:44:36:82 | this.ro ... eters.x | Cross-site scripting vulnerability due to $@. | angular2-client.ts:36:44:36:80 | this.ro ... ameters | user-provided value | +| angular2-client.ts:37:44:37:91 | this.ro ... et('x') | angular2-client.ts:37:44:37:91 | this.ro ... et('x') | angular2-client.ts:37:44:37:91 | this.ro ... et('x') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:37:44:37:91 | this.ro ... et('x') | user-provided value | +| angular2-client.ts:38:44:38:91 | this.ro ... arams.x | angular2-client.ts:38:44:38:89 | this.ro ... .params | angular2-client.ts:38:44:38:91 | this.ro ... arams.x | Cross-site scripting vulnerability due to $@. | angular2-client.ts:38:44:38:89 | this.ro ... .params | user-provided value | +| angular2-client.ts:40:44:40:58 | this.router.url | angular2-client.ts:40:44:40:58 | this.router.url | angular2-client.ts:40:44:40:58 | this.router.url | Cross-site scripting vulnerability due to $@. | angular2-client.ts:40:44:40:58 | this.router.url | user-provided value | +| angular2-client.ts:42:45:42:59 | this.router.url | angular2-client.ts:42:45:42:59 | this.router.url | angular2-client.ts:42:45:42:59 | this.router.url | Cross-site scripting vulnerability due to $@. | angular2-client.ts:42:45:42:59 | this.router.url | user-provided value | +| angular2-client.ts:43:75:43:109 | this.ro ... ams.foo | angular2-client.ts:43:75:43:105 | this.ro ... yParams | angular2-client.ts:43:75:43:109 | this.ro ... ams.foo | Cross-site scripting vulnerability due to $@. | angular2-client.ts:43:75:43:105 | this.ro ... yParams | user-provided value | +| angular2-client.ts:47:44:47:76 | routeSn ... ('foo') | angular2-client.ts:47:44:47:76 | routeSn ... ('foo') | angular2-client.ts:47:44:47:76 | routeSn ... ('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:47:44:47:76 | routeSn ... ('foo') | user-provided value | | angular-tempate-url.js:9:26:9:45 | Cookie.get("unsafe") | angular-tempate-url.js:13:30:13:31 | ev | angular-tempate-url.js:9:26:9:45 | Cookie.get("unsafe") | Cross-site scripting vulnerability due to $@. | angular-tempate-url.js:13:30:13:31 | ev | user-provided value | | classnames.js:7:31:7:84 | `` | classnames.js:7:58:7:68 | window.name | classnames.js:7:31:7:84 | `` | Cross-site scripting vulnerability due to $@. | classnames.js:7:58:7:68 | window.name | user-provided value | | classnames.js:8:31:8:85 | `` | classnames.js:8:59:8:69 | window.name | classnames.js:8:31:8:85 | `` | Cross-site scripting vulnerability due to $@. | classnames.js:8:59:8:69 | window.name | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected index ce6a053abc57..80871ad4e512 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected @@ -8,24 +8,26 @@ nodes | addEventListener.js:10:21:10:25 | event | semmle.label | event | | addEventListener.js:12:24:12:28 | event | semmle.label | event | | addEventListener.js:12:24:12:33 | event.data | semmle.label | event.data | -| angular2-client.ts:22:44:22:71 | \\u0275getDOM ... ().href | semmle.label | \\u0275getDOM ... ().href | -| angular2-client.ts:24:44:24:69 | this.ro ... .params | semmle.label | this.ro ... .params | -| angular2-client.ts:24:44:24:73 | this.ro ... ams.foo | semmle.label | this.ro ... ams.foo | -| angular2-client.ts:25:44:25:74 | this.ro ... yParams | semmle.label | this.ro ... yParams | -| angular2-client.ts:25:44:25:78 | this.ro ... ams.foo | semmle.label | this.ro ... ams.foo | -| angular2-client.ts:26:44:26:71 | this.ro ... ragment | semmle.label | this.ro ... ragment | -| angular2-client.ts:27:44:27:82 | this.ro ... ('foo') | semmle.label | this.ro ... ('foo') | -| angular2-client.ts:28:44:28:87 | this.ro ... ('foo') | semmle.label | this.ro ... ('foo') | -| angular2-client.ts:30:46:30:59 | map.get('foo') | semmle.label | map.get('foo') | -| angular2-client.ts:33:44:33:74 | this.ro ... 1].path | semmle.label | this.ro ... 1].path | -| angular2-client.ts:34:44:34:80 | this.ro ... ameters | semmle.label | this.ro ... ameters | -| angular2-client.ts:34:44:34:82 | this.ro ... eters.x | semmle.label | this.ro ... eters.x | -| angular2-client.ts:35:44:35:91 | this.ro ... et('x') | semmle.label | this.ro ... et('x') | -| angular2-client.ts:36:44:36:89 | this.ro ... .params | semmle.label | this.ro ... .params | -| angular2-client.ts:36:44:36:91 | this.ro ... arams.x | semmle.label | this.ro ... arams.x | -| angular2-client.ts:38:44:38:58 | this.router.url | semmle.label | this.router.url | -| angular2-client.ts:40:45:40:59 | this.router.url | semmle.label | this.router.url | -| angular2-client.ts:44:44:44:76 | routeSn ... ('foo') | semmle.label | routeSn ... ('foo') | +| angular2-client.ts:24:44:24:71 | \\u0275getDOM ... ().href | semmle.label | \\u0275getDOM ... ().href | +| angular2-client.ts:26:44:26:69 | this.ro ... .params | semmle.label | this.ro ... .params | +| angular2-client.ts:26:44:26:73 | this.ro ... ams.foo | semmle.label | this.ro ... ams.foo | +| angular2-client.ts:27:44:27:74 | this.ro ... yParams | semmle.label | this.ro ... yParams | +| angular2-client.ts:27:44:27:78 | this.ro ... ams.foo | semmle.label | this.ro ... ams.foo | +| angular2-client.ts:28:44:28:71 | this.ro ... ragment | semmle.label | this.ro ... ragment | +| angular2-client.ts:29:44:29:82 | this.ro ... ('foo') | semmle.label | this.ro ... ('foo') | +| angular2-client.ts:30:44:30:87 | this.ro ... ('foo') | semmle.label | this.ro ... ('foo') | +| angular2-client.ts:32:46:32:59 | map.get('foo') | semmle.label | map.get('foo') | +| angular2-client.ts:35:44:35:74 | this.ro ... 1].path | semmle.label | this.ro ... 1].path | +| angular2-client.ts:36:44:36:80 | this.ro ... ameters | semmle.label | this.ro ... ameters | +| angular2-client.ts:36:44:36:82 | this.ro ... eters.x | semmle.label | this.ro ... eters.x | +| angular2-client.ts:37:44:37:91 | this.ro ... et('x') | semmle.label | this.ro ... et('x') | +| angular2-client.ts:38:44:38:89 | this.ro ... .params | semmle.label | this.ro ... .params | +| angular2-client.ts:38:44:38:91 | this.ro ... arams.x | semmle.label | this.ro ... arams.x | +| angular2-client.ts:40:44:40:58 | this.router.url | semmle.label | this.router.url | +| angular2-client.ts:42:45:42:59 | this.router.url | semmle.label | this.router.url | +| angular2-client.ts:43:75:43:105 | this.ro ... yParams | semmle.label | this.ro ... yParams | +| angular2-client.ts:43:75:43:109 | this.ro ... ams.foo | semmle.label | this.ro ... ams.foo | +| angular2-client.ts:47:44:47:76 | routeSn ... ('foo') | semmle.label | routeSn ... ('foo') | | angular-tempate-url.js:9:26:9:45 | Cookie.get("unsafe") | semmle.label | Cookie.get("unsafe") | | angular-tempate-url.js:13:30:13:31 | ev | semmle.label | ev | | angular-tempate-url.js:14:26:14:27 | ev | semmle.label | ev | @@ -657,10 +659,11 @@ edges | addEventListener.js:5:43:5:48 | {data} | addEventListener.js:5:43:5:48 | data | provenance | | | addEventListener.js:10:21:10:25 | event | addEventListener.js:12:24:12:28 | event | provenance | | | addEventListener.js:12:24:12:28 | event | addEventListener.js:12:24:12:33 | event.data | provenance | | -| angular2-client.ts:24:44:24:69 | this.ro ... .params | angular2-client.ts:24:44:24:73 | this.ro ... ams.foo | provenance | | -| angular2-client.ts:25:44:25:74 | this.ro ... yParams | angular2-client.ts:25:44:25:78 | this.ro ... ams.foo | provenance | | -| angular2-client.ts:34:44:34:80 | this.ro ... ameters | angular2-client.ts:34:44:34:82 | this.ro ... eters.x | provenance | | -| angular2-client.ts:36:44:36:89 | this.ro ... .params | angular2-client.ts:36:44:36:91 | this.ro ... arams.x | provenance | | +| angular2-client.ts:26:44:26:69 | this.ro ... .params | angular2-client.ts:26:44:26:73 | this.ro ... ams.foo | provenance | | +| angular2-client.ts:27:44:27:74 | this.ro ... yParams | angular2-client.ts:27:44:27:78 | this.ro ... ams.foo | provenance | | +| angular2-client.ts:36:44:36:80 | this.ro ... ameters | angular2-client.ts:36:44:36:82 | this.ro ... eters.x | provenance | | +| angular2-client.ts:38:44:38:89 | this.ro ... .params | angular2-client.ts:38:44:38:91 | this.ro ... arams.x | provenance | | +| angular2-client.ts:43:75:43:105 | this.ro ... yParams | angular2-client.ts:43:75:43:109 | this.ro ... ams.foo | provenance | | | angular-tempate-url.js:13:30:13:31 | ev | angular-tempate-url.js:14:26:14:27 | ev | provenance | | | angular-tempate-url.js:14:26:14:27 | ev | angular-tempate-url.js:14:26:14:32 | ev.data | provenance | | | angular-tempate-url.js:14:26:14:32 | ev.data | angular-tempate-url.js:9:26:9:45 | Cookie.get("unsafe") | provenance | | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/angular2-client.ts b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/angular2-client.ts index 734a06da3bc1..6d1823c2f601 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/angular2-client.ts +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/angular2-client.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, DomSanitizer as DomSanitizer2 } from '@angular/core'; +import { Component, OnInit, DomSanitizer as DomSanitizer2, Renderer2, Inject } from '@angular/core'; import { ɵgetDOM } from '@angular/common'; import { ActivatedRoute, ActivatedRouteSnapshot, Router } from '@angular/router'; import { DomSanitizer } from '@angular/platform-browser'; @@ -15,7 +15,9 @@ export class AppComponent implements OnInit { private route: ActivatedRoute, private sanitizer: DomSanitizer, private router: Router, - private sanitizer2: DomSanitizer2 + private sanitizer2: DomSanitizer2, + private renderer: Renderer2, + @Inject(DOCUMENT) private document: Document ) {} ngOnInit() { @@ -38,6 +40,7 @@ export class AppComponent implements OnInit { this.sanitizer.bypassSecurityTrustHtml(this.router.url); // NOT OK this.sanitizer2.bypassSecurityTrustHtml(this.router.url); // NOT OK + this.renderer.setProperty(this.document.documentElement, 'innerHTML', this.route.snapshot.queryParams.foo); // NOT OK } someMethod(routeSnapshot: ActivatedRouteSnapshot) {