Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion .github/workflows/javascript.sarif.expected

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -748,4 +748,9 @@ class Binding extends TBinding {
BindingPath getBindingPath() { result.getBinding() = this }

BindingTarget getBindingTarget() { result.getBinding() = this }

/**
* Gets the `BindElementMethodCallNode` for this binding, if it is a context binding via `bindElement`.
*/
BindElementMethodCallNode getBindElementCall() { this = TLateJavaScriptContextBinding(result, _) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,12 @@ class DefaultODataServiceModel extends UI5ExternalModel {

override string getName() { result = "" }

Binding asBinding() { result.getBindingTarget().asDataFlowNode() = this }
/**
* Gets bindings associated with this default OData model source.
* Since `DefaultODataServiceModel` represents a `bindElement` call,
* we match context bindings whose `bindElement` call is this node.
*/
Binding asBinding() { result.getBindElementCall() = this }
}

/** Model which gains content from an SAP OData service. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,13 @@ abstract class UI5BindingPath extends BindingPath {
not exists(this.getModelName())
)
or
/* 5. There is no call to `setModel` at all and a default model exists that is related to the binding path this refers to */
/* 5. There is no call to `setModel` in the same webapp and a default model exists that is related to the binding path this refers to */
exists(DefaultODataServiceModel defaultModel |
result = defaultModel and
not exists(MethodCallNode viewSetModelCall | viewSetModelCall.getMethodName() = "setModel") and
not exists(MethodCallNode viewSetModelCall |
viewSetModelCall.getMethodName() = "setModel" and
inSameWebApp(this.getLocation().getFile(), viewSetModelCall.getFile())
) and
/*
* this binding path can occur in a fragment that is the receiver object for the bindElement model approximation
* i.e. checks that the default model is relevant
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
nodes
| webapp/controller/App.controller.js:21:17:21:53 | oFragme ... es(1)") |
| webapp/fragments/DataDisplay.fragment.xml:7:9:7:42 | content={message} |
edges
| webapp/controller/App.controller.js:21:17:21:53 | oFragme ... es(1)") | webapp/fragments/DataDisplay.fragment.xml:7:9:7:42 | content={message} |
| webapp/fragments/DataDisplay.fragment.xml:7:9:7:42 | content={message} | webapp/controller/App.controller.js:21:17:21:53 | oFragme ... es(1)") |
#select
| webapp/fragments/DataDisplay.fragment.xml:7:9:7:42 | content={message} | webapp/controller/App.controller.js:21:17:21:53 | oFragme ... es(1)") | webapp/fragments/DataDisplay.fragment.xml:7:9:7:42 | content={message} | XSS vulnerability due to $@. | webapp/controller/App.controller.js:21:17:21:53 | oFragme ... es(1)") | user-provided value |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UI5Xss/UI5Xss.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "xss-fragment-odata-default-model",
"version": "1.0.0",
"description": "Test case for XSS vulnerability via default OData model in fragment with bindElement"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
specVersion: "3.1"
type: application
metadata:
name: xss-fragment-odata-default-model
framework:
name: SAPUI5
version: "1.120.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
sap.ui.define([
"sap/ui/core/UIComponent"
], function (UIComponent) {
"use strict";
return UIComponent.extend("xss.fragment.odata.defaultmodel.Component", {
metadata: {
manifest: "json"
},
init: function () {
UIComponent.prototype.init.apply(this, arguments);
}
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/core/Fragment"
], function (Controller, Fragment) {
"use strict";

return Controller.extend("xss.fragment.odata.defaultmodel.controller.App", {
onInit: function () {
// XSS vulnerability pattern:
// 1. OData model is configured as default model in manifest.json
// 2. Fragment is loaded dynamically
// 3. Fragment is bound to OData entity via bindElement
// 4. Fragment contains HTML control that renders OData content

Fragment.load({
id: this.getView().getId(),
name: "xss.fragment.odata.defaultmodel.fragments.DataDisplay",
controller: this
}).then(function (oFragment) {
// Bind fragment to an OData entity - vulnerability is in the backend data
oFragment.bindElement("/Messages(1)");

// Add the fragment to the page content
this.byId("page").addContent(oFragment);
}.bind(this));
}
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core">
<VBox class="sapUiSmallMargin">
<Label text="Message from OData:" />
<!-- XSS vulnerability: HTML content bound to OData property containing unsanitized data -->
<core:HTML content="{message}" />
Comment thread Dismissed
</VBox>
</core:FragmentDefinition>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>XSS Fragment OData Default Model Test</title>
<script
id="sap-ui-bootstrap"
src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-resourceroots='{
"xss.fragment.odata.defaultmodel": "./"
}'
data-sap-ui-oninit="module:xss/fragment/odata/defaultmodel/index"
data-sap-ui-async="true">
</script>
</head>
<body class="sapUiBody" id="content">
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
sap.ui.define([
"sap/ui/core/ComponentContainer"
], function (ComponentContainer) {
"use strict";
new ComponentContainer({
name: "xss.fragment.odata.defaultmodel",
settings: {
id: "xss.fragment.odata.defaultmodel"
},
async: true
}).placeAt("content");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"_version": "1.58.0",
"sap.app": {
"id": "xss.fragment.odata.defaultmodel",
"type": "application",
"title": "XSS Fragment OData Default Model Test",
"applicationVersion": {
"version": "1.0.0"
},
"dataSources": {
"mainService": {
"uri": "/odata/v4/catalog/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}
}
},
"sap.ui": {
"technology": "UI5",
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
}
},
"sap.ui5": {
"rootView": {
"viewName": "xss.fragment.odata.defaultmodel.view.App",
"type": "XML",
"id": "app"
},
"dependencies": {
"minUI5Version": "1.60",
"libs": {
"sap.m": {}
}
},
"models": {
"": {
"dataSource": "mainService",
"preload": true,
"settings": {}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<mvc:View
controllerName="xss.fragment.odata.defaultmodel.controller.App"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:core="sap.ui.core"
displayBlock="true">
<App id="app">
<Page id="page" title="XSS Fragment OData Default Model Test">
<content>
<!-- Fragment will be loaded here dynamically -->
</content>
</Page>
</App>
</mvc:View>
Loading