diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/RemoteFlowSources.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/RemoteFlowSources.qll
index b3d38669b..e78632936 100644
--- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/RemoteFlowSources.qll
+++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/RemoteFlowSources.qll
@@ -203,14 +203,3 @@ private class DisplayEventHandlerParameterAccess extends RemoteFlowSource instan
)
}
}
-
-/**
- * Method calls that fetch a piece of data either from a library control capable of accepting user input, or from a URI parameter.
- */
-private class UI5ExtRemoteSource extends RemoteFlowSource {
- UI5ExtRemoteSource() { this = ModelOutput::getASourceNode("remote").asSource() }
-
- override string getSourceType() {
- result = "Remote flow" // Don't discriminate between UI5-specific remote flows and vanilla ones
- }
-}
diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5Control.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5Control.qll
new file mode 100644
index 000000000..fc49facfb
--- /dev/null
+++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5Control.qll
@@ -0,0 +1,315 @@
+import advanced_security.javascript.frameworks.ui5.UI5
+
+private newtype TUI5Control =
+ TXmlControl(XmlElement control) {
+ control
+ .(Locatable)
+ .getFile()
+ .getBaseName()
+ .matches(["%.view.xml", "%.view.html", "%.fragment.xml"])
+ } or
+ TJsonControl(JsonObject control) {
+ exists(JsonView view | control.getParent() = view.getRoot().getPropValue("content"))
+ } or
+ TJsControl(NewNode control) {
+ exists(JsView view |
+ control.asExpr().getParentExpr() =
+ view.getRoot()
+ .getArgument(1)
+ .getALocalSource()
+ .(ObjectLiteralNode)
+ .getAPropertyWrite("createContent")
+ .getRhs()
+ .(FunctionNode)
+ .getReturnNode()
+ .getALocalSource()
+ .(ArrayLiteralNode)
+ .asExpr()
+ )
+ or
+ control = ModelOutput::getATypeNode("Control").getAnInvocation()
+ }
+
+class UI5Control extends TUI5Control {
+ XmlElement asXmlControl() { this = TXmlControl(result) }
+
+ JsonObject asJsonControl() { this = TJsonControl(result) }
+
+ NewNode asJsControl() { this = TJsControl(result) }
+
+ string toString() {
+ result = this.asXmlControl().toString()
+ or
+ result = this.asJsonControl().toString()
+ or
+ result = this.asJsControl().toString()
+ }
+
+ predicate hasLocationInfo(
+ string filepath, int startcolumn, int startline, int endcolumn, int endline
+ ) {
+ this.asXmlControl().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ or
+ /* Since JsonValue does not implement `hasLocationInfo`, we use `getLocation` instead. */
+ exists(Location location | location = this.asJsonControl().getLocation() |
+ location.getFile().getAbsolutePath() = filepath and
+ location.getStartColumn() = startcolumn and
+ location.getStartLine() = startline and
+ location.getEndColumn() = endcolumn and
+ location.getEndLine() = endline
+ )
+ or
+ this.asJsControl().hasLocationInfo(filepath, startcolumn, startline, endcolumn, endline)
+ }
+
+ /**
+ * Gets the qualified type string, e.g. `sap.m.SearchField`.
+ */
+ string getQualifiedType() {
+ exists(XmlElement control | control = this.asXmlControl() |
+ result = control.getNamespace().getUri() + "." + control.getName()
+ )
+ or
+ exists(JsonObject control | control = this.asJsonControl() |
+ result = control.getPropStringValue("Type")
+ )
+ or
+ exists(NewNode control | control = this.asJsControl() |
+ result = control.asExpr().getAChildExpr().(PropAccess).getQualifiedName()
+ or
+ control = API::moduleImport(result).getAnInvocation()
+ )
+ }
+
+ File getFile() {
+ result = this.asXmlControl().getFile() or
+ result = this.asJsonControl().getFile() or
+ result = this.asJsControl().getFile()
+ }
+
+ /**
+ * Gets the `id` property of this control.
+ */
+ string getId() { result = this.getProperty("id").getValue() }
+
+ /**
+ * Gets the qualified type name, e.g. `sap/m/SearchField`.
+ */
+ string getImportPath() { result = this.getQualifiedType().replaceAll(".", "/") }
+
+ /**
+ * Gets the definition of this control if this is a custom one.
+ */
+ CustomControl getDefinition() {
+ result.getName() = this.getQualifiedType() and
+ inSameWebApp(this.getFile(), result.getFile())
+ }
+
+ /**
+ * Gets a reference to this control. Currently supports only such references made through `byId`.
+ * Handles both:
+ * - `this.byId("controlId")` or `this.getView().byId("controlId")` - ID in argument 0
+ * - `Fragment.byId(viewId, "controlId")` - ID in argument 1
+ */
+ ControlReference getAReference() {
+ result.getMethodName() = "byId" and
+ (
+ // Standard byId: ID in first argument
+ result.getNumArgument() = 1 and
+ result.getArgument(0).getALocalSource().asExpr().(StringLiteral).getValue() =
+ this.getProperty("id").getValue()
+ or
+ // Fragment.byId: ID in second argument
+ result.getNumArgument() = 2 and
+ result.getArgument(1).getALocalSource().asExpr().(StringLiteral).getValue() =
+ this.getProperty("id").getValue()
+ )
+ }
+
+ /** Gets a property of this control having the name. */
+ UI5ControlProperty getProperty(string propName) {
+ result.asXmlControlProperty() = this.asXmlControl().getAttribute(propName)
+ or
+ result.asJsonControlProperty() = this.asJsonControl().getPropValue(propName)
+ or
+ result.asJsControlProperty() =
+ this.asJsControl()
+ .getArgument(0)
+ .getALocalSource()
+ .asExpr()
+ .(ObjectExpr)
+ .getPropertyByName(propName)
+ .getAChildExpr()
+ .flow() and
+ not exists(Property property | result.asJsControlProperty() = property.getNameExpr().flow())
+ }
+
+ /** Gets a property of this control. */
+ UI5ControlProperty getAProperty() { result = this.getProperty(_) }
+
+ bindingset[propName]
+ MethodCallNode getARead(string propName) {
+ // TODO: in same view
+ inSameWebApp(this.getFile(), result.getFile()) and
+ result.getMethodName() = "get" + capitalize(propName)
+ }
+
+ bindingset[propName]
+ MethodCallNode getAWrite(string propName) {
+ // TODO: in same view
+ inSameWebApp(this.getFile(), result.getFile()) and
+ result.getMethodName() = "set" + capitalize(propName)
+ }
+
+ /** Holds if this control reads from or writes to a model. */
+ predicate accessesModel(UI5Model model) { this.accessesModel(model, _) }
+
+ /** Holds if this control reads from or writes to a model with regards to a binding path. */
+ predicate accessesModel(UI5Model model, XmlBindingPath bindingPath) {
+ // Verify that the controller's model has the referenced property
+ exists(XmlView view |
+ // Both this control and the model belong to the same view
+ this = view.getControl() and
+ model = view.getController().getModel() and
+ model.(UI5InternalModel).getPathString() = bindingPath.getPath() and
+ bindingPath.getBindingTarget() = this.asXmlControl().getAnAttribute()
+ )
+ }
+
+ /** Get the view that this control is part of. */
+ UI5View getView() { result = this.asXmlControl().getFile() }
+
+ /** Get the controller that manages this control. */
+ CustomController getController() { result = this.getView().getController() }
+
+ /**
+ * Gets the full import path of the associated control.
+ */
+ string getControlTypeName() { result = this.getQualifiedType().replaceAll(".", "/") }
+
+ /**
+ * Holds if the control content is sanitized for HTML
+ * 'sap/ui/core/HTML' sanitized using the property 'sanitizeContent'
+ * 'sap/ui/richttexteditor/RichTextEditor' sanitized using the property 'sanitizeValue'
+ */
+ predicate isHTMLSanitized() {
+ this.getControlTypeName() = "sap/ui/richtexteditor/RichTextEditor" and
+ not this.isSanitizePropertySetTo("sanitizeValue", false)
+ or
+ this.getControlTypeName() = "sap/ui/core/HTML" and
+ this.isSanitizePropertySetTo("sanitizeContent", true) and
+ not this.isSanitizePropertySetTo("sanitizeContent", false)
+ }
+
+ bindingset[propName, val]
+ private predicate isSanitizePropertySetTo(string propName, boolean val) {
+ /* 1. `sanitizeContent` attribute is set declaratively. */
+ this.getProperty(propName).toString() = val.toString()
+ or
+ /* 2. `sanitizeContent` attribute is set programmatically using setProperty(). */
+ exists(CallNode node | node = this.getAReference().getAMemberCall("setProperty") |
+ node.getArgument(0).getStringValue() = propName and
+ not node.getArgument(1).mayHaveBooleanValue(val.booleanNot())
+ )
+ or
+ /* 3. `sanitizeContent` attribute is set programmatically using a setter. */
+ 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)
+ )
+ }
+}
+
+class XmlControlProperty extends XmlAttribute {
+ XmlControlProperty() { exists(UI5Control control | this.getElement() = control.asXmlControl()) }
+}
+
+bindingset[qualifiedTypeUri]
+predicate isBuiltInControl(string qualifiedTypeUri) {
+ exists(string namespace |
+ namespace =
+ [
+ "sap\\.m.*", // https://sapui5.hana.ondemand.com/#/api/sap.m: The main UI5 control library, with responsive controls that can be used in touch devices as well as desktop browsers.
+ "sap\\.f.*", // https://sapui5.hana.ondemand.com/#/api/sap.f: SAPUI5 library with controls specialized for SAP Fiori apps.
+ "sap\\.ui.*" // https://sapui5.hana.ondemand.com/#/api/sap.ui: The sap.ui namespace is the central OpenAjax compliant entry point for UI related JavaScript functionality provided by SAP.
+ ]
+ |
+ qualifiedTypeUri.regexpMatch(namespace)
+ )
+}
+
+private newtype TUI5ControlProperty =
+ TXmlControlProperty(XmlAttribute property) or
+ TJsonControlProperty(JsonValue property) or
+ TJsControlProperty(ValueNode property)
+
+class UI5ControlProperty extends TUI5ControlProperty {
+ XmlAttribute asXmlControlProperty() { this = TXmlControlProperty(result) }
+
+ JsonValue asJsonControlProperty() { this = TJsonControlProperty(result) }
+
+ ValueNode asJsControlProperty() { this = TJsControlProperty(result) }
+
+ string toString() {
+ result = this.asXmlControlProperty().getValue().toString() or
+ result = this.asJsonControlProperty().toString() or
+ result = this.asJsControlProperty().toString()
+ }
+
+ UI5Control getControl() {
+ result.asXmlControl() = this.asXmlControlProperty().getElement() or
+ result.asJsonControl() = this.asJsonControlProperty().getParent() or
+ result.asJsControl().getArgument(0).asExpr() = this.asJsControlProperty().getEnclosingExpr()
+ }
+
+ string getName() {
+ result = this.asXmlControlProperty().getName()
+ or
+ exists(JsonValue parent | parent.getPropValue(result) = this.asJsonControlProperty())
+ or
+ exists(Property property |
+ property.getAChildExpr() = this.asJsControlProperty().asExpr() and result = property.getName()
+ )
+ }
+
+ string getValue() {
+ result = this.asXmlControlProperty().getValue() or
+ result = this.asJsonControlProperty().getStringValue() or
+ result = this.asJsControlProperty().asExpr().(StringLiteral).getValue()
+ }
+}
+
+/**
+ * Utility predicate capturing the handler name.
+ */
+bindingset[notation]
+private string handlerNotationCaptureName(string notation) {
+ result =
+ notation.replaceAll(" ", "").regexpCapture("\\.([\\w-]+)(?:\\([^)]*\\$(\\{[^}]+}).*)?", 1)
+}
+
+/**
+ * A function mentioned in a property of a UI5Control, usually an event handler.
+ *
+ * e.g. The function referred to by `doSomething()` as in ``.
+ */
+class UI5Handler extends FunctionNode {
+ UI5Control control;
+
+ UI5Handler() {
+ this = control.getController().getAMethod() and
+ handlerNotationCaptureName(control.getProperty(_).getValue()) = this.getName()
+ }
+
+ UI5BindingPath getBindingPath() {
+ exists(string propName |
+ handlerNotationCaptureName(control.getProperty(propName).getValue()) = this.getName() and
+ result.getLiteralRepr() = control.getProperty(result.getPropertyName()).getValue()
+ )
+ }
+
+ UI5Control getControl() { result = control }
+}
diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5FormulaInjectionQuery.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5FormulaInjectionQuery.qll
index 0a9e87bda..e4b5235e9 100644
--- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5FormulaInjectionQuery.qll
+++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5FormulaInjectionQuery.qll
@@ -1,5 +1,5 @@
import javascript
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow
/**
* Call to [`sap.ui.util.Storage.put`](https://sapui5.hana.ondemand.com/sdk/#/api/module:sap/ui/util/Storage%23methods/put)
diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5LogInjectionQuery.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5LogInjectionQuery.qll
index 94523cd27..09e0a7fc2 100644
--- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5LogInjectionQuery.qll
+++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5LogInjectionQuery.qll
@@ -1,5 +1,5 @@
import javascript
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow
import semmle.javascript.security.dataflow.LogInjectionQuery
module UI5LogInjection implements DataFlow::ConfigSig {
diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5LogsToHttpQuery.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5LogsToHttpQuery.qll
index 7b847f8ad..74dbeab70 100644
--- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5LogsToHttpQuery.qll
+++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5LogsToHttpQuery.qll
@@ -1,5 +1,5 @@
import javascript
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow
import advanced_security.javascript.frameworks.ui5.UI5LogInjectionQuery
class ClientRequestInjectionVector extends DataFlow::Node {
diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5UnsafeLogAccessQuery.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5UnsafeLogAccessQuery.qll
index 0121696b7..336dbe914 100644
--- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5UnsafeLogAccessQuery.qll
+++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5UnsafeLogAccessQuery.qll
@@ -1,5 +1,5 @@
import javascript
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow
import semmle.javascript.security.dataflow.LogInjectionQuery
module UI5UnsafeLogAccess implements DataFlow::ConfigSig {
diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5View.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5View.qll
index d878e02ad..688e52ccb 100644
--- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5View.qll
+++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5View.qll
@@ -1,5 +1,6 @@
import advanced_security.javascript.frameworks.ui5.UI5
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
+import advanced_security.javascript.frameworks.ui5.UI5Control
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow
private import semmle.javascript.frameworks.data.internal.ApiGraphModelsExtensions as ApiGraphModelsExtensions
import advanced_security.javascript.frameworks.ui5.Bindings
import advanced_security.javascript.frameworks.ui5.Fragment
@@ -224,24 +225,6 @@ private UI5ExternalModel getExternalModelNode(UI5BindingPath bindingPath) {
inSameWebApp(bindingPath.getLocation().getFile(), result.getFile())
}
-class XmlControlProperty extends XmlAttribute {
- XmlControlProperty() { exists(UI5Control control | this.getElement() = control.asXmlControl()) }
-}
-
-bindingset[qualifiedTypeUri]
-predicate isBuiltInControl(string qualifiedTypeUri) {
- exists(string namespace |
- namespace =
- [
- "sap\\.m.*", // https://sapui5.hana.ondemand.com/#/api/sap.m: The main UI5 control library, with responsive controls that can be used in touch devices as well as desktop browsers.
- "sap\\.f.*", // https://sapui5.hana.ondemand.com/#/api/sap.f: SAPUI5 library with controls specialized for SAP Fiori apps.
- "sap\\.ui.*" // https://sapui5.hana.ondemand.com/#/api/sap.ui: The sap.ui namespace is the central OpenAjax compliant entry point for UI related JavaScript functionality provided by SAP.
- ]
- |
- qualifiedTypeUri.regexpMatch(namespace)
- )
-}
-
/**
* A UI5 View that might include XSS sources and sinks in standard controls.
*/
@@ -811,299 +794,3 @@ class XmlFragment extends UI5View instanceof XmlFile {
)
}
}
-
-private newtype TUI5Control =
- TXmlControl(XmlElement control) {
- control
- .(Locatable)
- .getFile()
- .getBaseName()
- .matches(["%.view.xml", "%.view.html", "%.fragment.xml"])
- } or
- TJsonControl(JsonObject control) {
- exists(JsonView view | control.getParent() = view.getRoot().getPropValue("content"))
- } or
- TJsControl(NewNode control) {
- exists(JsView view |
- control.asExpr().getParentExpr() =
- view.getRoot()
- .getArgument(1)
- .getALocalSource()
- .(ObjectLiteralNode)
- .getAPropertyWrite("createContent")
- .getRhs()
- .(FunctionNode)
- .getReturnNode()
- .getALocalSource()
- .(ArrayLiteralNode)
- .asExpr()
- )
- or
- control = ModelOutput::getATypeNode("Control").getAnInvocation()
- }
-
-class UI5Control extends TUI5Control {
- XmlElement asXmlControl() { this = TXmlControl(result) }
-
- JsonObject asJsonControl() { this = TJsonControl(result) }
-
- NewNode asJsControl() { this = TJsControl(result) }
-
- string toString() {
- result = this.asXmlControl().toString()
- or
- result = this.asJsonControl().toString()
- or
- result = this.asJsControl().toString()
- }
-
- predicate hasLocationInfo(
- string filepath, int startcolumn, int startline, int endcolumn, int endline
- ) {
- this.asXmlControl().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- or
- /* Since JsonValue does not implement `hasLocationInfo`, we use `getLocation` instead. */
- exists(Location location | location = this.asJsonControl().getLocation() |
- location.getFile().getAbsolutePath() = filepath and
- location.getStartColumn() = startcolumn and
- location.getStartLine() = startline and
- location.getEndColumn() = endcolumn and
- location.getEndLine() = endline
- )
- or
- this.asJsControl().hasLocationInfo(filepath, startcolumn, startline, endcolumn, endline)
- }
-
- /**
- * Gets the qualified type string, e.g. `sap.m.SearchField`.
- */
- string getQualifiedType() {
- exists(XmlElement control | control = this.asXmlControl() |
- result = control.getNamespace().getUri() + "." + control.getName()
- )
- or
- exists(JsonObject control | control = this.asJsonControl() |
- result = control.getPropStringValue("Type")
- )
- or
- exists(NewNode control | control = this.asJsControl() |
- result = control.asExpr().getAChildExpr().(PropAccess).getQualifiedName()
- or
- control = API::moduleImport(result).getAnInvocation()
- )
- }
-
- File getFile() {
- result = this.asXmlControl().getFile() or
- result = this.asJsonControl().getFile() or
- result = this.asJsControl().getFile()
- }
-
- /**
- * Gets the `id` property of this control.
- */
- string getId() { result = this.getProperty("id").getValue() }
-
- /**
- * Gets the qualified type name, e.g. `sap/m/SearchField`.
- */
- string getImportPath() { result = this.getQualifiedType().replaceAll(".", "/") }
-
- /**
- * Gets the definition of this control if this is a custom one.
- */
- CustomControl getDefinition() {
- result.getName() = this.getQualifiedType() and
- inSameWebApp(this.getFile(), result.getFile())
- }
-
- /**
- * Gets a reference to this control. Currently supports only such references made through `byId`.
- * Handles both:
- * - `this.byId("controlId")` or `this.getView().byId("controlId")` - ID in argument 0
- * - `Fragment.byId(viewId, "controlId")` - ID in argument 1
- */
- ControlReference getAReference() {
- result.getMethodName() = "byId" and
- (
- // Standard byId: ID in first argument
- result.getNumArgument() = 1 and
- result.getArgument(0).getALocalSource().asExpr().(StringLiteral).getValue() =
- this.getProperty("id").getValue()
- or
- // Fragment.byId: ID in second argument
- result.getNumArgument() = 2 and
- result.getArgument(1).getALocalSource().asExpr().(StringLiteral).getValue() =
- this.getProperty("id").getValue()
- )
- }
-
- /** Gets a property of this control having the name. */
- UI5ControlProperty getProperty(string propName) {
- result.asXmlControlProperty() = this.asXmlControl().getAttribute(propName)
- or
- result.asJsonControlProperty() = this.asJsonControl().getPropValue(propName)
- or
- result.asJsControlProperty() =
- this.asJsControl()
- .getArgument(0)
- .getALocalSource()
- .asExpr()
- .(ObjectExpr)
- .getPropertyByName(propName)
- .getAChildExpr()
- .flow() and
- not exists(Property property | result.asJsControlProperty() = property.getNameExpr().flow())
- }
-
- /** Gets a property of this control. */
- UI5ControlProperty getAProperty() { result = this.getProperty(_) }
-
- bindingset[propName]
- MethodCallNode getARead(string propName) {
- // TODO: in same view
- inSameWebApp(this.getFile(), result.getFile()) and
- result.getMethodName() = "get" + capitalize(propName)
- }
-
- bindingset[propName]
- MethodCallNode getAWrite(string propName) {
- // TODO: in same view
- inSameWebApp(this.getFile(), result.getFile()) and
- result.getMethodName() = "set" + capitalize(propName)
- }
-
- /** Holds if this control reads from or writes to a model. */
- predicate accessesModel(UI5Model model) { this.accessesModel(model, _) }
-
- /** Holds if this control reads from or writes to a model with regards to a binding path. */
- predicate accessesModel(UI5Model model, XmlBindingPath bindingPath) {
- // Verify that the controller's model has the referenced property
- exists(XmlView view |
- // Both this control and the model belong to the same view
- this = view.getControl() and
- model = view.getController().getModel() and
- model.(UI5InternalModel).getPathString() = bindingPath.getPath() and
- bindingPath.getBindingTarget() = this.asXmlControl().getAnAttribute()
- )
- }
-
- /** Get the view that this control is part of. */
- UI5View getView() { result = this.asXmlControl().getFile() }
-
- /** Get the controller that manages this control. */
- CustomController getController() { result = this.getView().getController() }
-
- /**
- * Gets the full import path of the associated control.
- */
- string getControlTypeName() { result = this.getQualifiedType().replaceAll(".", "/") }
-
- /**
- * Holds if the control content is sanitized for HTML
- * 'sap/ui/core/HTML' sanitized using the property 'sanitizeContent'
- * 'sap/ui/richttexteditor/RichTextEditor' sanitized using the property 'sanitizeValue'
- */
- predicate isHTMLSanitized() {
- this.getControlTypeName() = "sap/ui/richtexteditor/RichTextEditor" and
- not this.isSanitizePropertySetTo("sanitizeValue", false)
- or
- this.getControlTypeName() = "sap/ui/core/HTML" and
- this.isSanitizePropertySetTo("sanitizeContent", true) and
- not this.isSanitizePropertySetTo("sanitizeContent", false)
- }
-
- bindingset[propName, val]
- private predicate isSanitizePropertySetTo(string propName, boolean val) {
- /* 1. `sanitizeContent` attribute is set declaratively. */
- this.getProperty(propName).toString() = val.toString()
- or
- /* 2. `sanitizeContent` attribute is set programmatically using setProperty(). */
- exists(CallNode node | node = this.getAReference().getAMemberCall("setProperty") |
- node.getArgument(0).getStringValue() = propName and
- not node.getArgument(1).mayHaveBooleanValue(val.booleanNot())
- )
- or
- /* 3. `sanitizeContent` attribute is set programmatically using a setter. */
- 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)
- )
- }
-}
-
-private newtype TUI5ControlProperty =
- TXmlControlProperty(XmlAttribute property) or
- TJsonControlProperty(JsonValue property) or
- TJsControlProperty(ValueNode property)
-
-class UI5ControlProperty extends TUI5ControlProperty {
- XmlAttribute asXmlControlProperty() { this = TXmlControlProperty(result) }
-
- JsonValue asJsonControlProperty() { this = TJsonControlProperty(result) }
-
- ValueNode asJsControlProperty() { this = TJsControlProperty(result) }
-
- string toString() {
- result = this.asXmlControlProperty().getValue().toString() or
- result = this.asJsonControlProperty().toString() or
- result = this.asJsControlProperty().toString()
- }
-
- UI5Control getControl() {
- result.asXmlControl() = this.asXmlControlProperty().getElement() or
- result.asJsonControl() = this.asJsonControlProperty().getParent() or
- result.asJsControl().getArgument(0).asExpr() = this.asJsControlProperty().getEnclosingExpr()
- }
-
- string getName() {
- result = this.asXmlControlProperty().getName()
- or
- exists(JsonValue parent | parent.getPropValue(result) = this.asJsonControlProperty())
- or
- exists(Property property |
- property.getAChildExpr() = this.asJsControlProperty().asExpr() and result = property.getName()
- )
- }
-
- string getValue() {
- result = this.asXmlControlProperty().getValue() or
- result = this.asJsonControlProperty().getStringValue() or
- result = this.asJsControlProperty().asExpr().(StringLiteral).getValue()
- }
-}
-
-/**
- * Utility predicate capturing the handler name.
- */
-bindingset[notation]
-private string handlerNotationCaptureName(string notation) {
- result =
- notation.replaceAll(" ", "").regexpCapture("\\.([\\w-]+)(?:\\([^)]*\\$(\\{[^}]+}).*)?", 1)
-}
-
-/**
- * A function mentioned in a property of a UI5Control, usually an event handler.
- *
- * e.g. The function referred to by `doSomething()` as in ``.
- */
-class UI5Handler extends FunctionNode {
- UI5Control control;
-
- UI5Handler() {
- this = control.getController().getAMethod() and
- handlerNotationCaptureName(control.getProperty(_).getValue()) = this.getName()
- }
-
- UI5BindingPath getBindingPath() {
- exists(string propName |
- handlerNotationCaptureName(control.getProperty(propName).getValue()) = this.getName() and
- result.getLiteralRepr() = control.getProperty(result.getPropertyName()).getValue()
- )
- }
-
- UI5Control getControl() { result = control }
-}
diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5XssQuery.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5XssQuery.qll
index 3e257a589..6fc7bf0eb 100644
--- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5XssQuery.qll
+++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5XssQuery.qll
@@ -1,5 +1,5 @@
import javascript
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow as UI5DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow
import advanced_security.javascript.frameworks.ui5.UI5View
private import semmle.javascript.frameworks.data.internal.ApiGraphModelsExtensions
private import semmle.javascript.security.dataflow.DomBasedXssQuery as DomBasedXss
diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/dataflow/TypeTrackers.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/dataflow/TypeTrackers.qll
index 4d2ed9c3a..fdfe2b7d3 100644
--- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/dataflow/TypeTrackers.qll
+++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/dataflow/TypeTrackers.qll
@@ -1,5 +1,5 @@
import javascript
-import DataFlow
+import UI5DataFlow
module TypeTrackers {
private SourceNode hasDependency(TypeTracker t, string dependencyPath) {
diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/dataflow/DataFlow.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/dataflow/UI5DataFlow.qll
similarity index 100%
rename from javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/dataflow/DataFlow.qll
rename to javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/dataflow/UI5DataFlow.qll
diff --git a/javascript/frameworks/ui5/src/Diagnostics/ListHtmlSinks.ql b/javascript/frameworks/ui5/src/Diagnostics/ListHtmlSinks.ql
index 7f5e4fe6e..a21e1f39c 100644
--- a/javascript/frameworks/ui5/src/Diagnostics/ListHtmlSinks.ql
+++ b/javascript/frameworks/ui5/src/Diagnostics/ListHtmlSinks.ql
@@ -9,7 +9,7 @@
*/
import javascript
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow
from DataFlow::Node sink, string kind
where
diff --git a/javascript/frameworks/ui5/src/Diagnostics/ListRemoteFlowSources.ql b/javascript/frameworks/ui5/src/Diagnostics/ListRemoteFlowSources.ql
index 2f2a21fbb..4792d5fa0 100644
--- a/javascript/frameworks/ui5/src/Diagnostics/ListRemoteFlowSources.ql
+++ b/javascript/frameworks/ui5/src/Diagnostics/ListRemoteFlowSources.ql
@@ -9,7 +9,7 @@
*/
import javascript
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow
from RemoteFlowSource source, string type
where type = source.getSourceType()
diff --git a/javascript/frameworks/ui5/src/UI5FormulaInjection/UI5FormulaInjection.ql b/javascript/frameworks/ui5/src/UI5FormulaInjection/UI5FormulaInjection.ql
index ca60a3f73..93fe3088b 100644
--- a/javascript/frameworks/ui5/src/UI5FormulaInjection/UI5FormulaInjection.ql
+++ b/javascript/frameworks/ui5/src/UI5FormulaInjection/UI5FormulaInjection.ql
@@ -12,7 +12,6 @@
*/
import javascript
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
import advanced_security.javascript.frameworks.ui5.UI5FormulaInjectionQuery
module UI5FormulaInjectionFlow = TaintTracking::Global;
diff --git a/javascript/frameworks/ui5/src/UI5LogInjection/UI5LogInjection.ql b/javascript/frameworks/ui5/src/UI5LogInjection/UI5LogInjection.ql
index 22602be21..a2cce42a7 100644
--- a/javascript/frameworks/ui5/src/UI5LogInjection/UI5LogInjection.ql
+++ b/javascript/frameworks/ui5/src/UI5LogInjection/UI5LogInjection.ql
@@ -13,7 +13,6 @@
import javascript
import advanced_security.javascript.frameworks.ui5.UI5LogInjectionQuery
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
module UI5LogInjectionFlow = TaintTracking::Global;
diff --git a/javascript/frameworks/ui5/src/UI5LogInjection/UI5LogsToHttp.ql b/javascript/frameworks/ui5/src/UI5LogInjection/UI5LogsToHttp.ql
index d95dbf013..cfe621b0e 100644
--- a/javascript/frameworks/ui5/src/UI5LogInjection/UI5LogsToHttp.ql
+++ b/javascript/frameworks/ui5/src/UI5LogInjection/UI5LogsToHttp.ql
@@ -13,7 +13,6 @@
import javascript
import advanced_security.javascript.frameworks.ui5.UI5LogsToHttpQuery
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
module UI5LogsToHttpFlow = TaintTracking::GlobalWithState;
diff --git a/javascript/frameworks/ui5/src/UI5LogInjection/UI5UnsafeLogAccess.ql b/javascript/frameworks/ui5/src/UI5LogInjection/UI5UnsafeLogAccess.ql
index 23340cac2..51112979d 100644
--- a/javascript/frameworks/ui5/src/UI5LogInjection/UI5UnsafeLogAccess.ql
+++ b/javascript/frameworks/ui5/src/UI5LogInjection/UI5UnsafeLogAccess.ql
@@ -11,7 +11,6 @@
*/
import javascript
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
import advanced_security.javascript.frameworks.ui5.UI5UnsafeLogAccessQuery
module UI5UnsafeLogAccessFlow = TaintTracking::Global;
diff --git a/javascript/frameworks/ui5/src/UI5PathInjection/UI5PathInjection.ql b/javascript/frameworks/ui5/src/UI5PathInjection/UI5PathInjection.ql
index 15cd4bafb..b063e0055 100644
--- a/javascript/frameworks/ui5/src/UI5PathInjection/UI5PathInjection.ql
+++ b/javascript/frameworks/ui5/src/UI5PathInjection/UI5PathInjection.ql
@@ -13,7 +13,7 @@
*/
import javascript
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow
import advanced_security.javascript.frameworks.ui5.UI5PathInjectionQuery
module UI5PathInjectionFlow = TaintTracking::Global;
diff --git a/javascript/frameworks/ui5/src/UI5Xss/UI5Xss.ql b/javascript/frameworks/ui5/src/UI5Xss/UI5Xss.ql
index d787b81a9..695a98fb4 100644
--- a/javascript/frameworks/ui5/src/UI5Xss/UI5Xss.ql
+++ b/javascript/frameworks/ui5/src/UI5Xss/UI5Xss.ql
@@ -13,7 +13,7 @@
*/
import javascript
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow
import advanced_security.javascript.frameworks.ui5.UI5XssQuery
module UI5XssFlow = TaintTracking::Global;
diff --git a/javascript/frameworks/ui5/test/models/sink/formulaSinkTest.ql b/javascript/frameworks/ui5/test/models/sink/formulaSinkTest.ql
index fa7e1c426..09d48f095 100644
--- a/javascript/frameworks/ui5/test/models/sink/formulaSinkTest.ql
+++ b/javascript/frameworks/ui5/test/models/sink/formulaSinkTest.ql
@@ -7,7 +7,7 @@
import javascript
import advanced_security.javascript.frameworks.ui5.UI5FormulaInjectionQuery
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow
from DataFlow::Node sink
where UI5FormulaInjection::isSink(sink)
diff --git a/javascript/frameworks/ui5/test/models/sink/pathSinkTest.ql b/javascript/frameworks/ui5/test/models/sink/pathSinkTest.ql
index c1e4f8beb..da2048970 100644
--- a/javascript/frameworks/ui5/test/models/sink/pathSinkTest.ql
+++ b/javascript/frameworks/ui5/test/models/sink/pathSinkTest.ql
@@ -7,7 +7,7 @@
import javascript
import semmle.javascript.security.dataflow.TaintedPathQuery
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow as UI5DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow as UI5DataFlow
class UI5ExtPathISink extends DataFlow::Node {
UI5ExtPathISink() { this = ModelOutput::getASinkNode("ui5-path-injection").asSink() }
diff --git a/javascript/frameworks/ui5/test/models/sink/xssSinkTest.ql b/javascript/frameworks/ui5/test/models/sink/xssSinkTest.ql
index 89395ff49..6d7858444 100644
--- a/javascript/frameworks/ui5/test/models/sink/xssSinkTest.ql
+++ b/javascript/frameworks/ui5/test/models/sink/xssSinkTest.ql
@@ -7,7 +7,7 @@
import javascript
import semmle.javascript.security.dataflow.DomBasedXssQuery as DomBasedXss
-import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow as UI5DataFlow
+import advanced_security.javascript.frameworks.ui5.dataflow.UI5DataFlow as UI5DataFlow
class UI5ExtHtmlISink extends DomBasedXss::Sink {
UI5ExtHtmlISink() { this = ModelOutput::getASinkNode("ui5-html-injection").asSink() }