Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions javascript/frameworks/ui5/ext/ui5.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ extensions:
- ["SapUICoreInstance", "global", "Member[sap].Member[ui].Member[getCore].ReturnValue"]
- ["Control", "Control", "Instance"]
- ["Control", "sap/ui/core/Control", ""]
- ["Control", "UI5HTMLControl", ""]
- ["Control", "UI5InputControl", ""]
- ["Control", "CustomControl", ""]
- ["Control", "global", "Member[sap].Member[ui].Member[core].Member[Control]"]
- ["Controller", "Controller", "Instance"]
- ["Controller", "sap/ui/core/mvc/Controller", ""]
Expand Down Expand Up @@ -112,6 +115,7 @@ extensions:
data:
- ["UI5InputControl", "Member[value]", "remote"]
- ["UI5InputControl", "Member[getValue].ReturnValue", "remote"]
- ["UI5HTMLControl", "Member[getContent].ReturnValue", "remote"]
- ["UI5CodeEditor", "Member[value]", "remote"]
- ["UI5CodeEditor", "Member[getCurrentValue].ReturnValue", "remote"]
- ["global", "Member[jQuery].Member[sap].Member[syncHead,syncGet,syncGetText,syncPost,syncPostText].ReturnValue", "remote"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,8 @@ private newtype TUI5Control =
.(ArrayLiteralNode)
.asExpr()
)
or
control = ModelOutput::getATypeNode("Control").getAnInvocation()
}

class UI5Control extends TUI5Control {
Expand Down Expand Up @@ -852,7 +854,9 @@ class UI5Control extends TUI5Control {
)
or
exists(NewNode control | control = this.asJsControl() |
result = this.asJsControl().asExpr().getAChildExpr().(DotExpr).getQualifiedName()
result = control.asExpr().getAChildExpr().(PropAccess).getQualifiedName()
or
control = API::moduleImport(result).getAnInvocation()
)
}

Expand Down Expand Up @@ -988,9 +992,12 @@ class UI5Control extends TUI5Control {
)
or
/* 3. `sanitizeContent` attribute is set programmatically using a setter. */
exists(CallNode node |
node = this.getAReference().getAMemberCall("setS" + propName.suffix(1)) and
exists(CallNode node, string setterName |
setterName = "set" + propName.prefix(1).toUpperCase() + propName.suffix(1) and
not node.getArgument(0).mayHaveBooleanValue(val.booleanNot())
|
node = this.getAReference().getAMemberCall(setterName) or
node = this.asJsControl().getAMemberCall(setterName)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ module UI5Xss implements DataFlow::ConfigSig {
node.(DataFlow::CallNode).getReceiver().asExpr().(PropAccess).getQualifiedName() = "jQuery.sap" and
node.(DataFlow::CallNode).getCalleeName() =
["encodeCSS", "encodeJS", "encodeURL", "encodeURLParameters", "encodeXML", "encodeHTML"]
or
/* Flow through `setContent/getContent` of a sanitized UI5Control */
Comment thread
mbaluda marked this conversation as resolved.
Outdated
exists(UI5Control control, DataFlow::MethodCallNode content |
control.asJsControl() = content.getReceiver().getALocalSource() and
control.isHTMLSanitized() and
content.getMethodName() = ["setContent", "getContent"] and
node = [content, content.getArgument(0)]
)
}

predicate isSink(DataFlow::Node node) {
Expand All @@ -56,6 +64,19 @@ module UI5Xss implements DataFlow::ConfigSig {

end = h.getParameter(0)
)
or
/* Flow from `setContent` to `getContent` of a control */
exists(
UI5Control control, DataFlow::MethodCallNode getContent, DataFlow::MethodCallNode setContent
|
control.asJsControl() = setContent.getReceiver().getALocalSource() and
control.asJsControl() = getContent.getReceiver().getALocalSource() and
setContent.getMethodName() = "setContent" and
getContent.getMethodName() = "getContent" and
start = setContent.getArgument(0) and
end = getContent and
not control.isHTMLSanitized()
)
}
}

Expand Down Expand Up @@ -93,10 +114,6 @@ private class UI5ExtHtmlISink extends DataFlow::Node {
}
}

private class HTMLControlInstantiation extends ElementInstantiation {
HTMLControlInstantiation() { typeModel("UI5HTMLControl", this.getImportPath(), _) }
}

private module TrackPlaceAtCallConfigFlow = TaintTracking::Global<TrackPlaceAtCallConfig>;

abstract class DynamicallySetElementValueOfHTML extends DataFlow::Node { }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
nodes
| webapp/controller/app.controller.js:10:17:10:27 | input: null |
| webapp/controller/app.controller.js:16:35:16:62 | oModel. ... input') |
| webapp/controller/app.controller.js:19:36:19:63 | oModel. ... input') |
| webapp/controller/app.controller.js:20:35:20:58 | unsanit ... ntent() |
| webapp/view/app.view.xml:5:5:7:28 | value={/input} |
| webapp/view/app.view.xml:8:5:8:79 | content={/output} |
edges
| webapp/controller/app.controller.js:10:17:10:27 | input: null | webapp/controller/app.controller.js:16:35:16:62 | oModel. ... input') |
| webapp/controller/app.controller.js:10:17:10:27 | input: null | webapp/controller/app.controller.js:19:36:19:63 | oModel. ... input') |
| webapp/controller/app.controller.js:10:17:10:27 | input: null | webapp/view/app.view.xml:5:5:7:28 | value={/input} |
| webapp/controller/app.controller.js:11:17:11:28 | output: null | webapp/view/app.view.xml:8:5:8:79 | content={/output} |
| webapp/controller/app.controller.js:13:26:13:45 | new JSONModel(oData) | webapp/view/app.view.xml:8:5:8:79 | content={/output} |
| webapp/controller/app.controller.js:19:36:19:63 | oModel. ... input') | webapp/controller/app.controller.js:20:35:20:58 | unsanit ... ntent() |
| webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:10:17:10:27 | input: null |
| webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:13:26:13:45 | new JSONModel(oData) |
| webapp/view/app.view.xml:8:5:8:79 | content={/output} | webapp/controller/app.controller.js:11:17:11:28 | output: null |
#select
| webapp/controller/app.controller.js:16:35:16:62 | oModel. ... input') | webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:16:35:16:62 | oModel. ... input') | XSS vulnerability due to $@. | webapp/view/app.view.xml:5:5:7:28 | value={/input} | user-provided value |
| webapp/controller/app.controller.js:19:36:19:63 | oModel. ... input') | webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:19:36:19:63 | oModel. ... input') | XSS vulnerability due to $@. | webapp/view/app.view.xml:5:5:7:28 | value={/input} | user-provided value |
| webapp/controller/app.controller.js:20:35:20:58 | unsanit ... ntent() | webapp/controller/app.controller.js:20:35:20:58 | unsanit ... ntent() | webapp/controller/app.controller.js:20:35:20:58 | unsanit ... ntent() | XSS vulnerability due to $@. | webapp/controller/app.controller.js:20:35:20:58 | unsanit ... ntent() | user-provided value |
| webapp/controller/app.controller.js:20:35:20:58 | unsanit ... ntent() | webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:20:35:20:58 | unsanit ... ntent() | XSS vulnerability due to $@. | webapp/view/app.view.xml:5:5:7:28 | value={/input} | user-provided value |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UI5Xss/UI5Xss.ql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "sap-ui5-xss",
"version": "1.0.0",
"main": "index.js"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
specVersion: '3.0'
metadata:
name: sap-ui5-xss
type: application
framework:
name: SAPUI5
version: "1.115.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel",
"sap/ui/core/HTML"
], function (Controller, JSONModel, HTML) {
"use strict";
return Controller.extend("codeql-sap-js.controller.app", {
onInit: function () {
var oData = {
input: null,
output: null,
};
var oModel = new JSONModel(oData);
this.getView().setModel(oModel);

jQuery.sap.globalEval(oModel.getProperty('/input')); // UNSAFE: evaluating user input
Comment thread Fixed
Comment thread Dismissed

var unsanitized = new HTML();
unsanitized.setContent(oModel.getProperty('/input')); // UNSAFE: evaluating user input
Comment thread Dismissed
jQuery.sap.globalEval(unsanitized.getContent()); // UNSAFE: setContent->getContent flow step
Comment thread Dismissed

var sanitized = new HTML();
sanitized.setSanitizeContent(true);
sanitized.setContent(oModel.getProperty('/input')); // SAFE: content is sanitized before eval
jQuery.sap.globalEval(sanitized.getContent()); // SAFE: content is sanitized before eval

let htmlSanitized = this.getView().byId("htmlSanitized");
jQuery.sap.globalEval(htmlSanitized.getContent()); // SAFE: content is sanitized declaratively in the view
}
});
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>

<head>

<meta charset="utf-8">
<title>SAPUI5 XSS</title>
<script src="https://sdk.openui5.org/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m"
data-sap-ui-onInit="module:codeql-sap-js/index"
data-sap-ui-resourceroots='{
"codeql-sap-js": "./"
}'>
</script>
</head>

<body class="sapUiBody" id="content">

</body>

</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
sap.ui.define([
"sap/ui/core/mvc/XMLView"
], function (XMLView) {
"use strict";
XMLView.create({
viewName: "codeql-sap-js.view.app"
}).then(function (oView) {
oView.placeAt("content");
});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sap.app": {
"id": "sap-ui5-xss"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<mvc:View controllerName="codeql-sap-js.controller.app"
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc">
<Input placeholder="Enter Payload (will be sanitized)"
description="Try: &lt;img src=x onerror=alert(&quot;XSS&quot;)&gt;"
value="{/input}" /> <!--User input source sap.m.Input.value -->
<core:HTML id="htmlSanitized" content="{/output}" sanitizeContent="true"/> <!--sanitized XSS sink sap.ui.core.HTML.content -->
</mvc:View>
Loading