diff --git a/javascript/frameworks/ui5/ext/ui5.model.yml b/javascript/frameworks/ui5/ext/ui5.model.yml index 68265d47f..16e6d6a9e 100644 --- a/javascript/frameworks/ui5/ext/ui5.model.yml +++ b/javascript/frameworks/ui5/ext/ui5.model.yml @@ -57,6 +57,13 @@ extensions: - ["UI5Control", "sap/ui/richtexteditor/RichTextEditor", ""] - ["UI5CodeEditor", "UI5CodeEditor", "Instance"] - ["UI5CodeEditor", "sap/ui/codeeditor/CodeEditor", ""] + # Classes that provide Path injection APIs + - ["UI5ClientStorage", "sap/ui/util/Storage", ""] + - ["UI5ClientStorage", "global", "Member[sap].Member[ui].Member[util].Member[Storage]"] + - ["UI5ClientStorage", "global", "Member[jQuery].Member[sap].Member[storage]"] + - ["UI5ClientStorage", "sap/ui/core/util/File", ""] + - ["UI5ClientStorage", "global", "Member[sap].Member[ui].Member[core].Member[util].Member[File]"] + - addsTo: pack: codeql/javascript-all @@ -94,6 +101,8 @@ extensions: - ["ResourceBundle", "Member[create].Argument[0]", "ui5-path-injection"] - ["Properties", "Member[create].Argument[0]", "ui5-path-injection"] - ["LoaderExtensions", "Member[registerResourcePath].Argument[1]", "ui5-path-injection"] + - ["UI5ClientStorage", "Member[put].Argument[0]", "ui5-path-injection"] + - ["UI5ClientStorage", "Member[save].Argument[1]", "ui5-path-injection"] - addsTo: pack: codeql/javascript-all 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 new file mode 100644 index 000000000..8e049b42d --- /dev/null +++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5FormulaInjectionQuery.qll @@ -0,0 +1,132 @@ +import javascript +import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow + +/** + * Call to [`sap.ui.util.Storage.put`](https://sapui5.hana.ondemand.com/sdk/#/api/module:sap/ui/util/Storage%23methods/put) + * or its jQuery counterpart, [`jQuery.sap.Storage.put`](https://sapui5.hana.ondemand.com/sdk/#/api/jQuery.sap.storage%23methods/jQuery.sap.storage.put). + */ +private class StoragePutCall extends CallNode { + StoragePutCall() { + /* 1. This is a call to `sap.ui.util.Storage.put` */ + // 1-1. Required from `sap/ui/util/Storage` + exists(RequiredObject storageClass | + this.getReceiver().getALocalSource() = storageClass and + this.getCalleeName() = "put" + ) + or + // 1-2. Direct call to `sap.ui.util.Storage.put` + this = + globalVarRef("sap") + .getAPropertyRead("ui") + .getAPropertyRead("util") + .getAPropertyRead("Storage") + .getAMemberCall("put") + or + /* 2. This is a call to `jQuery.sap.storage.put` */ + this = + globalVarRef("jQuery") + .getAPropertyRead("sap") + .getAPropertyRead("storage") + .getAMemberCall("put") + } + + string getKeyName() { + result = this.getArgument(0).getALocalSource().asExpr().(StringLiteral).getValue() + } + + string getContentToBeSaved() { + result = this.getArgument(1).getALocalSource().asExpr().(StringLiteral).getValue() + } +} + +/** + * Call to [`sap.ui.core.util.File.save`](https://sapui5.hana.ondemand.com/sdk/#/api/sap.ui.core.util.File%23methods/sap.ui.core.util.File.save). + */ +private class FileSaveCall extends CallNode { + FileSaveCall() { + /* 1. Required from `sap/ui/core/util/File` */ + exists(RequiredObject fileClass | + this.getReceiver().getALocalSource() = fileClass and + this.getCalleeName() = "save" + ) + or + /* 2. Direct call to `sap.ui.core.util.File.save` */ + this = + globalVarRef("sap") + .getAPropertyRead("ui") + .getAPropertyRead("core") + .getAPropertyRead("util") + .getAPropertyRead("File") + .getAMemberCall("save") + } + + /** + * Gets the MIME type the file will saved under. + */ + string getMimeType() { + result = this.getArgument(3).getALocalSource().asExpr().(StringLiteral).getValue() + } + + /** + * Gets the file extension to be attached to the filename. + */ + string getExtension() { + result = this.getArgument(2).getALocalSource().asExpr().(StringLiteral).getValue() + } + + /** + * Holds if the file MIME type is `"text/csv"`. + */ + predicate mimeTypeIsCsv() { this.getMimeType() = "text/csv" } + + /** + * Holds if the file MIME type is `"application/json"`. + */ + predicate mimeTypeIsJson() { this.getMimeType() = "application/json" } + + /** + * Holds if the file extension is `"csv"`. It can be used as a fallback + * to detect a CSV data being written if `this.mimeTypeIsCsv()` fails. + */ + predicate extensionIsCsv() { this.getExtension() = "csv" } + + /** + * Holds if the file extension is `"json"`. It can be used as a fallback + * to detect a JSON data being written if `this.mimeTypeIsJson()` fails. + */ + predicate extensionIsJson() { this.getExtension() = "json" } + + /** + * Gets the content object to be saved into the file. + */ + DataFlow::Node getContentToBeSaved() { result = this.getArgument(0) } + + /** + * Gets the path the file will be saved under. + */ + string getPathToBeSavedUnder() { + result = this.getArgument(1).getALocalSource().asExpr().(StringLiteral).getValue() + } +} + +class UI5FormulaInjectionConfiguration extends TaintTracking::Configuration { + UI5FormulaInjectionConfiguration() { this = "UI5 Formula Injection" } + + override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node node) { + exists(StoragePutCall storagePutCall | node = storagePutCall.getArgument(1)) + or + exists(FileSaveCall fileSaveCall | + node = fileSaveCall.getArgument(0) and + ( + /* 1. Primary check: match on the MIME type */ + fileSaveCall.mimeTypeIsCsv() or + fileSaveCall.mimeTypeIsJson() or + /* 2. Fallback check: match on the file extension */ + fileSaveCall.extensionIsCsv() or + fileSaveCall.extensionIsJson() + ) + ) + } +} diff --git a/javascript/frameworks/ui5/src/UI5FormulaInjection/UI5FormulaInjection.md b/javascript/frameworks/ui5/src/UI5FormulaInjection/UI5FormulaInjection.md new file mode 100644 index 000000000..f5d868626 --- /dev/null +++ b/javascript/frameworks/ui5/src/UI5FormulaInjection/UI5FormulaInjection.md @@ -0,0 +1,39 @@ +# Formula injection + +UI5 applications that save local data, fetched from an uncontrolled remote source, into a CSV file format using generic APIs such as [`sap.ui.core.util.File.save`](https://sapui5.hana.ondemand.com/sdk/#/api/sap.ui.core.util.File%23methods/sap.ui.core.util.File.save) are vulnerable to formula injection, or CSV injection. + +## Recommendation + +### Escape the leading special characters + +CSV cells containing a leading special characters such as the equal sign (`=`) may be interpreted as spreadsheet formulas. Therefore, these prefixes should be escaped with surrounding single quotes in order to have them interpreted as literal strings. + +### Use a dedicated API function + +As in other injection attacks, it is not recommended to use string concatenation to assemble data passed into a generic file-saving function, even if sanitizers are to be used. Instead, a dedicated library function should be used. For example, if the target being exported is a [sap.m.Table](https://sapui5.hana.ondemand.com/sdk/#/api/sap.m.Table) and the resulting file is intended to be opened using a spreadsheet program, then using one of the API functions provided by [`sap.ui.export.Spreadsheet`](https://sapui5.hana.ondemand.com/#/entity/sap.ui.export.Spreadsheet) is the preferred method of achieving the same exporting functionality. + +## Example + +The following controller is exporting a CSV file obtained from an event parameter by surrounding it in a pair of semicolons (`;`) as CSV separators. + +``` javascript +sap.ui.define([ + "sap/ui/core/Controller", + "sap/ui/core/util/File" + ], function(Controller, File) { + return Controller.extend("vulnerable.controller.app", { + onSomeEvent: function(oEvent) { + let response = oEvent.getProperty("someProperty").someField; + let csvRow = ";" + response + ";"; + File.save(csvRow, "someFile", "csv", "text/csv", "utf-8"); + } + }); + }); +``` + +## References + +- OWASP: [CSV Injection](https://owasp.org/www-community/attacks/CSV_Injection) +- [CWE-1236](https://cwe.mitre.org/data/definitions/1236.html): Improper Neutralization of Formula Elements in a CSV File +- API Documentation: [`sap.ui.export.Spreadsheet`](https://sapui5.hana.ondemand.com/#/entity/sap.ui.export.Spreadsheet) +- API Documentation: [`sap.ui.core.util.File.save`](https://sapui5.hana.ondemand.com/sdk/#/api/sap.ui.core.util.File%23methods/sap.ui.core.util.File.save) diff --git a/javascript/frameworks/ui5/src/UI5FormulaInjection/UI5FormulaInjection.ql b/javascript/frameworks/ui5/src/UI5FormulaInjection/UI5FormulaInjection.ql new file mode 100644 index 000000000..1a8fb1fd0 --- /dev/null +++ b/javascript/frameworks/ui5/src/UI5FormulaInjection/UI5FormulaInjection.ql @@ -0,0 +1,26 @@ +/** + * @name UI5 Formula Injection + * @description Saving data from an uncontrolled remote source using filesystem or local storage + * leads to disclosure of sensitive information or forgery of entry. + * @kind path-problem + * @problem.severity error + * @security-severity 7.8 + * @precision medium + * @id js/ui5-formula-injection + * @tags security + * external/cwe/cwe-1236 + */ + +import javascript +import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow +import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow::UI5PathGraph +import advanced_security.javascript.frameworks.ui5.UI5FormulaInjectionQuery + +from + UI5FormulaInjectionConfiguration config, UI5PathNode source, UI5PathNode sink, + UI5PathNode primarySource +where + config.hasFlowPath(source.getPathNode(), sink.getPathNode()) and + primarySource = source.getAPrimarySource() +select sink, primarySource, sink, "The content of a saved file depends on a $@.", primarySource, + "user-provided value" diff --git a/javascript/frameworks/ui5/src/UI5PathInjection/UI5PathInjection.md b/javascript/frameworks/ui5/src/UI5PathInjection/UI5PathInjection.md new file mode 100644 index 000000000..b079bf8b6 --- /dev/null +++ b/javascript/frameworks/ui5/src/UI5PathInjection/UI5PathInjection.md @@ -0,0 +1,67 @@ +# Client-side path injection + +UI5 applications that access files using a dynamically configured path are vulnerable to injection attacks that allow an attacker to manipulate the file location. + +## Recommendation + +### Make path argument independent of the user input + +If possible, do not parameterize the path on a user input. Either hardcode the path string in the source, or use only strings that are created within the application. + +### Keep an allow-list of safe paths + +Keep a strict allow-list of safe paths to load from or send requests to. Before loading a script from a location outside the application or making an API request to a location, check if the path is contained in the list of safe paths. Also, make sure that the allow-list is kept up to date. + +### Check the script into the repository or use package managers + +Since the URL of the script may be pointing to a web server vulnerable to being hijacked, it may be a good idea to check a stable version of the script into the repository to increase the degree of control. If not possible, use a trusted package manager such as `npm`. + +## Example + +### Including scripts from an untrusted domain + +``` javascript +sap.ui.require([ + "sap/ui/dom/includeScript" + ], + function(includeScript) { + includeScript("http://some.vulnerable.domain/some-script.js"); + } +); +``` + +If the vulnerable domain is outside the organization and controlled by an untrusted third party, this may result in arbitrary code execution in the user's browser. + +### Using user input as a name of a file to be saved + +Suppose a controller is configured to receive a response from a server as follows. + +``` javascript +sap.ui.define([ + "sap/ui/core/mvc/Controller", + "sap/ui/core/util/File" + ], + function(Controller, File) { + return Controller.extend("vulnerable.controller.app", { + onInit: function() { + let oDataV2Model = this.getOwnerComponent().getModel("some-ODatav2-model"); + this.getView().setModel(oDataV2Model); + }, + + onSomeEvent: function() { + let remoteResponse = this.getView().getModel().getProperty("someProperty"); + File.save("some-content", remoteResponse, "txt", "text/plain", "utf-8"); + } + }); + }); +``` + +Even if the server which updates the OData V2 model is in a trusted domain such as within the organization, the server may still contain tainted information if the UI5 application in question is vulnerable to other security attacks, say XSS. This may allow an attacker to save a file in the victim's local filesystem. + +## References + +- [CWE-829](https://cwe.mitre.org/data/definitions/829.html): Inclusion of Functionality from Untrusted Control Sphere +- [CWE-073](https://cwe.mitre.org/data/definitions/73.html): External Control of File Name or Path +- [API Documentation of `sap.ui.core.util.File`](https://sapui5.hana.ondemand.com/sdk/#/api/sap.ui.core.util.File%23methods/sap.ui.core.util.File.save) +- [API Documentation of `sap.ui.dom.includeScript`](https://sapui5.hana.ondemand.com/sdk/#/api/module:sap/ui/dom/includeScript) and [`sap.ui.dom.includeStyleSheet`](https://sapui5.hana.ondemand.com/sdk/#/api/module:sap/ui/dom/includeStylesheet) +- [API Documentation of `jQuery.sap.includeScript`](https://sapui5.hana.ondemand.com/sdk/#/api/module:sap/ui/dom/includeScript) and [`jQuery.sap.includeStyleSheet`](https://sapui5.hana.ondemand.com/sdk/#/api/module:sap/ui/dom/includeScript) diff --git a/javascript/frameworks/ui5/src/UI5PathInjection/UI5PathInjection.ql b/javascript/frameworks/ui5/src/UI5PathInjection/UI5PathInjection.ql new file mode 100644 index 000000000..2d370208d --- /dev/null +++ b/javascript/frameworks/ui5/src/UI5PathInjection/UI5PathInjection.ql @@ -0,0 +1,37 @@ +/** + * @name UI5 Path Injection + * @description Constructing path from an uncontrolled remote source to be passed + * to a filesystem API allows for manipulation of the local filesystem. + * @kind path-problem + * @problem.severity error + * @security-severity 7.8 + * @precision medium + * @id js/ui5-path-injection + * @tags security + * external/cwe/cwe-022 + * external/cwe/cwe-035 + */ + +import javascript +import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow +import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow::UI5PathGraph + +// import semmle.javascript.security.dataflow.TaintedPathQuery as TaintedPathQuery +class UI5PathInjectionConfiguration extends TaintTracking::Configuration { + UI5PathInjectionConfiguration() { this = "UI5 Path Injection" } + + override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node node) { + node = ModelOutput::getASinkNode("ui5-path-injection").asSink() + } +} + +from + UI5PathInjectionConfiguration config, UI5PathNode source, UI5PathNode sink, + UI5PathNode primarySource +where + config.hasFlowPath(source.getPathNode(), sink.getPathNode()) and + primarySource = source.getAPrimarySource() +select sink, primarySource, sink, "The path of a saved file depends on a $@.", primarySource, + "user-provided value" diff --git a/javascript/frameworks/ui5/test/models/sink/formulaSinkTest.expected b/javascript/frameworks/ui5/test/models/sink/formulaSinkTest.expected new file mode 100644 index 000000000..80218973a --- /dev/null +++ b/javascript/frameworks/ui5/test/models/sink/formulaSinkTest.expected @@ -0,0 +1,7 @@ +| sink.js:118:36:118:40 | code1 | code1 | +| sink.js:119:48:119:52 | code1 | code1 | +| sink.js:120:47:120:51 | code1 | code1 | +| sink.js:122:27:122:31 | code0 | code0 | +| sink.js:123:27:123:31 | code0 | code0 | +| sink.js:125:44:125:48 | code0 | code0 | +| sink.js:126:44:126:48 | code0 | code0 | diff --git a/javascript/frameworks/ui5/test/models/sink/formulaSinkTest.ql b/javascript/frameworks/ui5/test/models/sink/formulaSinkTest.ql new file mode 100644 index 000000000..12eb7d9a9 --- /dev/null +++ b/javascript/frameworks/ui5/test/models/sink/formulaSinkTest.ql @@ -0,0 +1,14 @@ +/** + * @id formula-injection-sinks + * @name Formula injection sinks + * @kind problem + * @problem.severity error + */ + +import javascript +import advanced_security.javascript.frameworks.ui5.UI5FormulaInjectionQuery +import advanced_security.javascript.frameworks.ui5.dataflow.DataFlow as UI5DataFlow + +from UI5FormulaInjectionConfiguration config, DataFlow::Node sink +where config.isSink(sink) +select sink, sink.toString() diff --git a/javascript/frameworks/ui5/test/models/sink/logSinkTest.expected b/javascript/frameworks/ui5/test/models/sink/logSinkTest.expected index d96f4a032..94f160116 100644 --- a/javascript/frameworks/ui5/test/models/sink/logSinkTest.expected +++ b/javascript/frameworks/ui5/test/models/sink/logSinkTest.expected @@ -1,56 +1,56 @@ -| sink.js:20:38:20:42 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:20:45:20:49 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:20:52:20:56 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:21:38:21:42 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:21:45:21:49 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:21:52:21:56 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:23:40:23:44 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:23:47:23:51 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:23:54:23:58 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:25:37:25:41 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:25:44:25:48 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:25:51:25:55 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:27:38:27:42 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:27:45:27:49 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:27:52:27:56 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:29:38:29:42 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:29:45:29:49 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:29:52:29:56 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:33:27:33:31 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:33:34:33:38 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:33:41:33:45 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:35:27:35:31 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:35:34:35:38 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:35:41:35:45 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:37:29:37:33 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:37:36:37:40 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:37:43:37:47 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:39:26:39:30 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:39:33:39:37 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:39:40:39:44 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:41:27:41:31 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:41:34:41:38 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:41:41:41:45 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:43:27:43:31 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:43:34:43:38 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:43:41:43:45 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:45:42:45:46 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:74:36:74:40 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:74:43:74:47 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:74:50:74:54 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:75:36:75:40 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:75:43:75:47 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:75:50:75:54 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:76:38:76:42 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:76:45:76:49 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:76:52:76:56 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:77:35:77:39 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:77:42:77:46 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:77:49:77:53 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:24:38:24:42 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:24:45:24:49 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:24:52:24:56 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:25:38:25:42 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:25:45:25:49 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:25:52:25:56 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:27:40:27:44 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:27:47:27:51 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:27:54:27:58 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:29:37:29:41 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:29:44:29:48 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:29:51:29:55 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:31:38:31:42 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:31:45:31:49 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:31:52:31:56 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:33:38:33:42 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:33:45:33:49 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:33:52:33:56 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:37:27:37:31 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:37:34:37:38 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:37:41:37:45 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:39:27:39:31 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:39:34:39:38 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:39:41:39:45 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:41:29:41:33 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:41:36:41:40 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:41:43:41:47 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:43:26:43:30 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:43:33:43:37 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:43:40:43:44 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:45:27:45:31 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:45:34:45:38 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:45:41:45:45 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:47:27:47:31 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:47:34:47:38 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:47:41:47:45 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:49:42:49:46 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | | sink.js:78:36:78:40 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | | sink.js:78:43:78:47 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | | sink.js:78:50:78:54 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | | sink.js:79:36:79:40 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | | sink.js:79:43:79:47 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | | sink.js:79:50:79:54 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | -| sink.js:86:40:86:44 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:80:38:80:42 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:80:45:80:49 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:80:52:80:56 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:81:35:81:39 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:81:42:81:46 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:81:49:81:53 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:82:36:82:40 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:82:43:82:47 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:82:50:82:54 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:83:36:83:40 | code0 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:83:43:83:47 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:83:50:83:54 | code2 | SAP UI5 log injection sink with kind: ui5-log-injection | +| sink.js:90:40:90:44 | code1 | SAP UI5 log injection sink with kind: ui5-log-injection | diff --git a/javascript/frameworks/ui5/test/models/sink/pathSinkTest.expected b/javascript/frameworks/ui5/test/models/sink/pathSinkTest.expected index 006d38e92..843e4285d 100644 --- a/javascript/frameworks/ui5/test/models/sink/pathSinkTest.expected +++ b/javascript/frameworks/ui5/test/models/sink/pathSinkTest.expected @@ -1,22 +1,31 @@ -| sink.js:47:54:47:58 | code1 | code1 | -| sink.js:49:56:49:60 | code1 | code1 | -| sink.js:51:36:51:40 | code0 | code0 | -| sink.js:53:42:53:46 | code0 | code0 | -| sink.js:55:46:55:50 | code0 | code0 | -| sink.js:59:39:59:43 | code0 | code0 | -| sink.js:61:38:61:42 | code0 | code0 | -| sink.js:63:33:63:37 | code0 | code0 | -| sink.js:65:37:65:41 | code0 | code0 | -| sink.js:67:36:67:40 | code0 | code0 | -| sink.js:67:43:67:47 | code1 | code1 | +| sink.js:51:54:51:58 | code1 | code1 | +| sink.js:53:56:53:60 | code1 | code1 | +| sink.js:55:36:55:40 | code0 | code0 | +| sink.js:57:42:57:46 | code0 | code0 | +| sink.js:59:46:59:50 | code0 | code0 | +| sink.js:63:39:63:43 | code0 | code0 | +| sink.js:65:38:65:42 | code0 | code0 | +| sink.js:67:33:67:37 | code0 | code0 | | sink.js:69:37:69:41 | code0 | code0 | -| sink.js:69:44:69:48 | code1 | code1 | -| sink.js:71:40:71:44 | code0 | code0 | -| sink.js:71:47:71:51 | code1 | code1 | -| sink.js:73:40:73:44 | code0 | code0 | -| sink.js:73:47:73:51 | code1 | code1 | -| sink.js:88:28:88:32 | code0 | code0 | -| sink.js:90:49:90:53 | code1 | code1 | +| sink.js:71:36:71:40 | code0 | code0 | +| sink.js:71:43:71:47 | code1 | code1 | +| sink.js:73:37:73:41 | code0 | code0 | +| sink.js:73:44:73:48 | code1 | code1 | +| sink.js:75:40:75:44 | code0 | code0 | +| sink.js:75:47:75:51 | code1 | code1 | +| sink.js:77:40:77:44 | code0 | code0 | +| sink.js:77:47:77:51 | code1 | code1 | | sink.js:92:28:92:32 | code0 | code0 | -| sink.js:111:42:111:46 | code0 | code0 | -| sink.js:112:46:112:50 | code0 | code0 | +| sink.js:94:49:94:53 | code1 | code1 | +| sink.js:96:28:96:32 | code0 | code0 | +| sink.js:115:42:115:46 | code0 | code0 | +| sink.js:116:46:116:50 | code0 | code0 | +| sink.js:118:29:118:33 | code0 | code0 | +| sink.js:119:41:119:45 | code0 | code0 | +| sink.js:120:40:120:44 | code0 | code0 | +| sink.js:122:34:122:38 | code1 | code1 | +| sink.js:123:34:123:38 | code1 | code1 | +| sink.js:124:34:124:38 | code1 | code1 | +| sink.js:125:51:125:55 | code1 | code1 | +| sink.js:126:51:126:55 | code1 | code1 | +| sink.js:127:51:127:55 | code1 | code1 | diff --git a/javascript/frameworks/ui5/test/models/sink/sink.js b/javascript/frameworks/ui5/test/models/sink/sink.js index b05bbbda4..4155fcde0 100644 --- a/javascript/frameworks/ui5/test/models/sink/sink.js +++ b/javascript/frameworks/ui5/test/models/sink/sink.js @@ -1,4 +1,4 @@ -sap.ui.require( +sap.ui.define( [ "sap/base/util/LoaderExtensions", "sap/base/i18n/ResourceBundle", @@ -7,6 +7,8 @@ sap.ui.require( "sap/ui/core/HTML", "sap/base/util/Properties", "sap/ui/core/RenderManager", + "sap/ui/util/Storage", + "sap/ui/core/util/File" ], function ( LoaderExtensions, @@ -16,6 +18,8 @@ sap.ui.require( HTML, Properties, RenderManager, + Storage, + File ) { var value = jQuery.sap.log.fatal(code0, code1, code2); var value = jQuery.sap.log.error(code0, code1, code2); @@ -110,5 +114,16 @@ sap.ui.require( var value = sap.ui.dom.includeScript(code0); var value = sap.ui.dom.includeStyleSheet(code0); + + var value = Storage.put(code0, code1); + var value = sap.ui.util.Storage.put(code0, code1); + var value = jQuery.sap.storage.put(code0, code1); + + var value = File.save(code0, code1, "csv", "text/csv", code4, code5); + var value = File.save(code0, code1, "csv", "text/plain", code4, code5); + var value = File.save(code0, code1, code2, code3, code4, code5); + var value = sap.ui.core.util.File.save(code0, code1, "csv", "text/csv", code4, code5); + var value = sap.ui.core.util.File.save(code0, code1, "csv", "text/plain", code4, code5); + var value = sap.ui.core.util.File.save(code0, code1, code2, code3, code4, code5); }, ); diff --git a/javascript/frameworks/ui5/test/models/sink/xssSinkTest.expected b/javascript/frameworks/ui5/test/models/sink/xssSinkTest.expected index 694399238..1d94aa90b 100644 --- a/javascript/frameworks/ui5/test/models/sink/xssSinkTest.expected +++ b/javascript/frameworks/ui5/test/models/sink/xssSinkTest.expected @@ -1,12 +1,12 @@ -| sink.js:57:39:57:43 | code0 | code0 | -| sink.js:93:34:93:38 | code0 | code0 | -| sink.js:94:19:94:23 | code0 | code0 | -| sink.js:95:20:95:24 | code0 | code0 | -| sink.js:97:32:97:36 | code0 | code0 | -| sink.js:99:27:99:31 | code0 | code0 | -| sink.js:101:36:101:40 | code0 | code0 | -| sink.js:101:43:101:47 | code1 | code1 | -| sink.js:105:30:105:34 | code0 | code0 | -| sink.js:105:37:105:41 | code1 | code1 | -| sink.js:107:30:107:34 | code0 | code0 | -| sink.js:109:32:109:36 | code0 | code0 | +| sink.js:61:39:61:43 | code0 | code0 | +| sink.js:97:34:97:38 | code0 | code0 | +| sink.js:98:19:98:23 | code0 | code0 | +| sink.js:99:20:99:24 | code0 | code0 | +| sink.js:101:32:101:36 | code0 | code0 | +| sink.js:103:27:103:31 | code0 | code0 | +| sink.js:105:36:105:40 | code0 | code0 | +| sink.js:105:43:105:47 | code1 | code1 | +| sink.js:109:30:109:34 | code0 | code0 | +| sink.js:109:37:109:41 | code1 | code1 | +| sink.js:111:30:111:34 | code0 | code0 | +| sink.js:113:32:113:36 | code0 | code0 | diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/UI5FormulaInjection.expected b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/UI5FormulaInjection.expected new file mode 100644 index 000000000..67dd289ba --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/UI5FormulaInjection.expected @@ -0,0 +1,17 @@ +nodes +| webapp/control/xss.js:8:23:8:37 | { type: "int" } | +| webapp/control/xss.js:17:27:17:44 | oControl.getText() | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | +| webapp/view/app.view.xml:8:5:8:38 | text={/input} | +edges +| webapp/control/xss.js:8:23:8:37 | { type: "int" } | webapp/control/xss.js:17:27:17:44 | oControl.getText() | +| webapp/control/xss.js:8:23:8:37 | { type: "int" } | webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/control/xss.js:8:23:8:37 | { type: "int" } | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/view/app.view.xml:5:5:7:28 | value={/input} | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/view/app.view.xml:8:5:8:38 | text={/input} | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:11:26:11:45 | new JSONModel(oData) | +| webapp/view/app.view.xml:8:5:8:38 | text={/input} | webapp/controller/app.controller.js:9:17:9:27 | input: null | +#select +| webapp/control/xss.js:17:27:17:44 | oControl.getText() | webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/control/xss.js:17:27:17:44 | oControl.getText() | The content of a saved file depends on a $@. | webapp/view/app.view.xml:5:5:7:28 | value={/input} | user-provided value | diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/UI5FormulaInjection.qlref b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/UI5FormulaInjection.qlref new file mode 100644 index 000000000..5d221c923 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/UI5FormulaInjection.qlref @@ -0,0 +1 @@ +UI5FormulaInjection/UI5FormulaInjection.ql \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/package-lock.json b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/package-lock.json new file mode 100644 index 000000000..ba052860f --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sap-ui5-xss", + "version": "1.0.0" + } + } +} diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/package.json b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/package.json new file mode 100644 index 000000000..9f0a4560b --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/package.json @@ -0,0 +1,5 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "main": "index.js" +} diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/ui5.yaml b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/ui5.yaml new file mode 100644 index 000000000..beb2ff69d --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/ui5.yaml @@ -0,0 +1,7 @@ +specVersion: '3.0' +metadata: + name: sap-ui5-xss +type: application +framework: + name: SAPUI5 + version: "1.115.0" diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/control/xss.js b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/control/xss.js new file mode 100644 index 000000000..d3f89ccb4 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/control/xss.js @@ -0,0 +1,21 @@ +sap.ui.define([ + "sap/ui/core/Control", + "sap/ui/core/util/File" +], function (Control, File) { + return Control.extend("codeql-sap-js.control.xss", { + metadata: { + properties: { + text: { type: "int" } + } + }, + renderer: { + apiVersion: 2, + render: function (oRm, oControl) { + /* Data is sanitized against XSS. */ + oRm.unsafeHtml(oControl.getText()); + /* Data is not sanitized against formula injection. */ + File.save(oControl.getText(), "/some/path/", "csv", "text/csv", "utf-8"); + } + } + }); +}) diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/controller/app.controller.js b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/controller/app.controller.js new file mode 100644 index 000000000..e21cb709b --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/controller/app.controller.js @@ -0,0 +1,15 @@ +sap.ui.define([ + "sap/ui/core/mvc/Controller", + "sap/ui/model/json/JSONModel" +], function (Controller, JSONModel) { + "use strict" + return Controller.extend("codeql-sap-js.controller.app", { + onInit: function () { + var oData = { + input: null + }; + var oModel = new JSONModel(oData); + this.getView().setModel(oModel); + } + }); +}) \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/index.html b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/index.html new file mode 100644 index 000000000..0d9daa387 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/index.html @@ -0,0 +1,21 @@ + + + + + + + SAPUI5 XSS + + + + + + + + \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/index.js b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/index.js new file mode 100644 index 000000000..7a66697fd --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/index.js @@ -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"); + }); + +}); \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/manifest.json b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/manifest.json new file mode 100644 index 000000000..65d4de250 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/manifest.json @@ -0,0 +1,5 @@ +{ + "sap.app": { + "id": "sap-ui5-xss" + } +} \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/view/app.view.xml b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/view/app.view.xml new file mode 100644 index 000000000..9ba075630 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-property-sanitized/webapp/view/app.view.xml @@ -0,0 +1,9 @@ + + + + diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/UI5FormulaInjection.expected b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/UI5FormulaInjection.expected new file mode 100644 index 000000000..9e34dd89e --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/UI5FormulaInjection.expected @@ -0,0 +1,29 @@ +nodes +| webapp/control/xss.js:9:23:9:40 | { type: "string" } | +| webapp/control/xss.js:15:21:15:46 | value | +| webapp/control/xss.js:15:29:15:46 | oControl.getText() | +| webapp/control/xss.js:17:21:17:59 | xssSanitized | +| webapp/control/xss.js:17:36:17:59 | encodeX ... value)) | +| webapp/control/xss.js:17:46:17:58 | String(value) | +| webapp/control/xss.js:17:53:17:57 | value | +| webapp/control/xss.js:23:27:23:38 | xssSanitized | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | +| webapp/view/app.view.xml:8:5:8:38 | text={/input} | +edges +| webapp/control/xss.js:9:23:9:40 | { type: "string" } | webapp/control/xss.js:15:29:15:46 | oControl.getText() | +| webapp/control/xss.js:9:23:9:40 | { type: "string" } | webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/control/xss.js:15:21:15:46 | value | webapp/control/xss.js:17:53:17:57 | value | +| webapp/control/xss.js:15:29:15:46 | oControl.getText() | webapp/control/xss.js:15:21:15:46 | value | +| webapp/control/xss.js:17:21:17:59 | xssSanitized | webapp/control/xss.js:23:27:23:38 | xssSanitized | +| webapp/control/xss.js:17:36:17:59 | encodeX ... value)) | webapp/control/xss.js:17:21:17:59 | xssSanitized | +| webapp/control/xss.js:17:46:17:58 | String(value) | webapp/control/xss.js:17:36:17:59 | encodeX ... value)) | +| webapp/control/xss.js:17:53:17:57 | value | webapp/control/xss.js:17:46:17:58 | String(value) | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/control/xss.js:9:23:9:40 | { type: "string" } | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/view/app.view.xml:5:5:7:28 | value={/input} | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/view/app.view.xml:8:5:8:38 | text={/input} | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:11:26:11:45 | new JSONModel(oData) | +| webapp/view/app.view.xml:8:5:8:38 | text={/input} | webapp/controller/app.controller.js:9:17:9:27 | input: null | +#select +| webapp/control/xss.js:23:27:23:38 | xssSanitized | webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/control/xss.js:23:27:23:38 | xssSanitized | The content of a saved file depends on a $@. | webapp/view/app.view.xml:5:5:7:28 | value={/input} | user-provided value | diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/UI5FormulaInjection.qlref b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/UI5FormulaInjection.qlref new file mode 100644 index 000000000..5d221c923 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/UI5FormulaInjection.qlref @@ -0,0 +1 @@ +UI5FormulaInjection/UI5FormulaInjection.ql \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/package-lock.json b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/package-lock.json new file mode 100644 index 000000000..ba052860f --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sap-ui5-xss", + "version": "1.0.0" + } + } +} diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/package.json b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/package.json new file mode 100644 index 000000000..9f0a4560b --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/package.json @@ -0,0 +1,5 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "main": "index.js" +} diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/ui5.yaml b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/ui5.yaml new file mode 100644 index 000000000..beb2ff69d --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/ui5.yaml @@ -0,0 +1,7 @@ +specVersion: '3.0' +metadata: + name: sap-ui5-xss +type: application +framework: + name: SAPUI5 + version: "1.115.0" diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/control/xss.js b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/control/xss.js new file mode 100644 index 000000000..724dee396 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/control/xss.js @@ -0,0 +1,27 @@ +sap.ui.define([ + "sap/ui/core/Control", + "sap/base/security/encodeXML", + "sap/ui/core/util/File" +], function (Control, encodeXML, File) { + return Control.extend("codeql-sap-js.control.xss", { + metadata: { + properties: { + text: { type: "string" } + } + }, + renderer: { + apiVersion: 2, + render: function (oRm, oControl) { + var value = oControl.getText(); + /* XSS sanitizer is applied. */ + var xssSanitized = encodeXML(String(value)); + oRm.openStart("div", oControl); + /* Data is sanitized against XSS. */ + oRm.unsafeHtml(xssSanitized); + oRm.close("div"); + /* Data is not sanitized against formula injection. */ + File.save(xssSanitized, "/some/path/", "csv", "text/csv", "utf-8"); + } + } + }); +}) diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/controller/app.controller.js b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/controller/app.controller.js new file mode 100644 index 000000000..e21cb709b --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/controller/app.controller.js @@ -0,0 +1,15 @@ +sap.ui.define([ + "sap/ui/core/mvc/Controller", + "sap/ui/model/json/JSONModel" +], function (Controller, JSONModel) { + "use strict" + return Controller.extend("codeql-sap-js.controller.app", { + onInit: function () { + var oData = { + input: null + }; + var oModel = new JSONModel(oData); + this.getView().setModel(oModel); + } + }); +}) \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/index.html b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/index.html new file mode 100644 index 000000000..0d9daa387 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/index.html @@ -0,0 +1,21 @@ + + + + + + + SAPUI5 XSS + + + + + + + + \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/index.js b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/index.js new file mode 100644 index 000000000..7a66697fd --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/index.js @@ -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"); + }); + +}); \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/manifest.json b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/manifest.json new file mode 100644 index 000000000..65d4de250 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/manifest.json @@ -0,0 +1,5 @@ +{ + "sap.app": { + "id": "sap-ui5-xss" + } +} \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/view/app.view.xml b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/view/app.view.xml new file mode 100644 index 000000000..9ba075630 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-custom-control-sanitized/webapp/view/app.view.xml @@ -0,0 +1,9 @@ + + + + diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/UI5FormulaInjection.expected b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/UI5FormulaInjection.expected new file mode 100644 index 000000000..feb385d6d --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/UI5FormulaInjection.expected @@ -0,0 +1,15 @@ +nodes +| webapp/controller/app.controller.js:10:17:10:27 | input: null | +| webapp/controller/app.controller.js:16:23:16:50 | oModel. ... input') | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | +| webapp/view/app.view.xml:8:5:8:49 | content={/input} | +edges +| webapp/controller/app.controller.js:10:17:10:27 | input: null | webapp/controller/app.controller.js:16:23:16:50 | 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:10:17:10:27 | input: null | webapp/view/app.view.xml:8:5:8:49 | content={/input} | +| webapp/controller/app.controller.js:12:26:12:45 | new JSONModel(oData) | webapp/view/app.view.xml:8:5:8:49 | content={/input} | +| 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:12:26:12:45 | new JSONModel(oData) | +| webapp/view/app.view.xml:8:5:8:49 | content={/input} | webapp/controller/app.controller.js:10:17:10:27 | input: null | +#select +| webapp/controller/app.controller.js:16:23:16:50 | oModel. ... input') | webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:16:23:16:50 | oModel. ... input') | The content of a saved file depends on a $@. | webapp/view/app.view.xml:5:5:7:28 | value={/input} | user-provided value | diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/UI5FormulaInjection.qlref b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/UI5FormulaInjection.qlref new file mode 100644 index 000000000..5d221c923 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/UI5FormulaInjection.qlref @@ -0,0 +1 @@ +UI5FormulaInjection/UI5FormulaInjection.ql \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/package-lock.json b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/package-lock.json new file mode 100644 index 000000000..ba052860f --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sap-ui5-xss", + "version": "1.0.0" + } + } +} diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/package.json b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/package.json new file mode 100644 index 000000000..9f0a4560b --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/package.json @@ -0,0 +1,5 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "main": "index.js" +} diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/ui5.yaml b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/ui5.yaml new file mode 100644 index 000000000..beb2ff69d --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/ui5.yaml @@ -0,0 +1,7 @@ +specVersion: '3.0' +metadata: + name: sap-ui5-xss +type: application +framework: + name: SAPUI5 + version: "1.115.0" diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/controller/app.controller.js b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/controller/app.controller.js new file mode 100644 index 000000000..c5c3bbab6 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/controller/app.controller.js @@ -0,0 +1,20 @@ +sap.ui.define([ + "sap/ui/core/mvc/Controller", + "sap/ui/model/json/JSONModel", + "sap/ui/core/util/File" +], function (Controller, JSONModel, File) { + "use strict"; + return Controller.extend("codeql-sap-js.controller.app", { + onInit: function () { + var oData = { + input: null, + }; + var oModel = new JSONModel(oData); + this.getView().setModel(oModel); + + /* Data is not sanitized against formula injection. */ + File.save(oModel.getProperty('/input'), "/some/path/", "csv", "text/csv", "utf-8"); + } + }); +} +); diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/index.html b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/index.html new file mode 100644 index 000000000..0d9daa387 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/index.html @@ -0,0 +1,21 @@ + + + + + + + SAPUI5 XSS + + + + + + + + \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/index.js b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/index.js new file mode 100644 index 000000000..7a66697fd --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/index.js @@ -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"); + }); + +}); \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/manifest.json b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/manifest.json new file mode 100644 index 000000000..65d4de250 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/manifest.json @@ -0,0 +1,5 @@ +{ + "sap.app": { + "id": "sap-ui5-xss" + } +} \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/view/app.view.xml b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/view/app.view.xml new file mode 100644 index 000000000..589fb2b64 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5FormulaInjection/formula-html-control-df/webapp/view/app.view.xml @@ -0,0 +1,9 @@ + + + + diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/UI5PathInjection.expected b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/UI5PathInjection.expected new file mode 100644 index 000000000..6a0a93b2d --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/UI5PathInjection.expected @@ -0,0 +1,17 @@ +nodes +| webapp/control/xss.js:8:23:8:37 | { type: "int" } | +| webapp/control/xss.js:17:43:17:60 | oControl.getText() | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | +| webapp/view/app.view.xml:8:5:8:38 | text={/input} | +edges +| webapp/control/xss.js:8:23:8:37 | { type: "int" } | webapp/control/xss.js:17:43:17:60 | oControl.getText() | +| webapp/control/xss.js:8:23:8:37 | { type: "int" } | webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/control/xss.js:8:23:8:37 | { type: "int" } | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/view/app.view.xml:5:5:7:28 | value={/input} | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/view/app.view.xml:8:5:8:38 | text={/input} | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:11:26:11:45 | new JSONModel(oData) | +| webapp/view/app.view.xml:8:5:8:38 | text={/input} | webapp/controller/app.controller.js:9:17:9:27 | input: null | +#select +| webapp/control/xss.js:17:43:17:60 | oControl.getText() | webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/control/xss.js:17:43:17:60 | oControl.getText() | The path of a saved file depends on a $@. | webapp/view/app.view.xml:5:5:7:28 | value={/input} | user-provided value | diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/UI5PathInjection.qlref b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/UI5PathInjection.qlref new file mode 100644 index 000000000..a8b5ee04e --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/UI5PathInjection.qlref @@ -0,0 +1 @@ +UI5PathInjection/UI5PathInjection.ql \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/package-lock.json b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/package-lock.json new file mode 100644 index 000000000..ba052860f --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sap-ui5-xss", + "version": "1.0.0" + } + } +} diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/package.json b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/package.json new file mode 100644 index 000000000..9f0a4560b --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/package.json @@ -0,0 +1,5 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "main": "index.js" +} diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/ui5.yaml b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/ui5.yaml new file mode 100644 index 000000000..beb2ff69d --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/ui5.yaml @@ -0,0 +1,7 @@ +specVersion: '3.0' +metadata: + name: sap-ui5-xss +type: application +framework: + name: SAPUI5 + version: "1.115.0" diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/control/xss.js b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/control/xss.js new file mode 100644 index 000000000..d2416ea80 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/control/xss.js @@ -0,0 +1,21 @@ +sap.ui.define([ + "sap/ui/core/Control", + "sap/ui/core/util/File" +], function (Control, File) { + return Control.extend("codeql-sap-js.control.xss", { + metadata: { + properties: { + text: { type: "int" } + } + }, + renderer: { + apiVersion: 2, + render: function (oRm, oControl) { + /* Data is sanitized against XSS. */ + oRm.unsafeHtml(oControl.getText()); + /* Data is not sanitized against formula injection. */ + File.save("some_content", oControl.getText(), "csv", "text/csv", "utf-8"); + } + } + }); +}) diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/controller/app.controller.js b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/controller/app.controller.js new file mode 100644 index 000000000..e21cb709b --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/controller/app.controller.js @@ -0,0 +1,15 @@ +sap.ui.define([ + "sap/ui/core/mvc/Controller", + "sap/ui/model/json/JSONModel" +], function (Controller, JSONModel) { + "use strict" + return Controller.extend("codeql-sap-js.controller.app", { + onInit: function () { + var oData = { + input: null + }; + var oModel = new JSONModel(oData); + this.getView().setModel(oModel); + } + }); +}) \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/index.html b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/index.html new file mode 100644 index 000000000..0d9daa387 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/index.html @@ -0,0 +1,21 @@ + + + + + + + SAPUI5 XSS + + + + + + + + \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/index.js b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/index.js new file mode 100644 index 000000000..7a66697fd --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/index.js @@ -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"); + }); + +}); \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/manifest.json b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/manifest.json new file mode 100644 index 000000000..65d4de250 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/manifest.json @@ -0,0 +1,5 @@ +{ + "sap.app": { + "id": "sap-ui5-xss" + } +} \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/view/app.view.xml b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/view/app.view.xml new file mode 100644 index 000000000..9ba075630 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-property-sanitized/webapp/view/app.view.xml @@ -0,0 +1,9 @@ + + + + diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/UI5PathInjection.expected b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/UI5PathInjection.expected new file mode 100644 index 000000000..14fd36fd5 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/UI5PathInjection.expected @@ -0,0 +1,29 @@ +nodes +| webapp/control/xss.js:9:23:9:40 | { type: "string" } | +| webapp/control/xss.js:15:21:15:46 | value | +| webapp/control/xss.js:15:29:15:46 | oControl.getText() | +| webapp/control/xss.js:17:21:17:59 | xssSanitized | +| webapp/control/xss.js:17:36:17:59 | encodeX ... value)) | +| webapp/control/xss.js:17:46:17:58 | String(value) | +| webapp/control/xss.js:17:53:17:57 | value | +| webapp/control/xss.js:23:43:23:54 | xssSanitized | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | +| webapp/view/app.view.xml:8:5:8:38 | text={/input} | +edges +| webapp/control/xss.js:9:23:9:40 | { type: "string" } | webapp/control/xss.js:15:29:15:46 | oControl.getText() | +| webapp/control/xss.js:9:23:9:40 | { type: "string" } | webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/control/xss.js:15:21:15:46 | value | webapp/control/xss.js:17:53:17:57 | value | +| webapp/control/xss.js:15:29:15:46 | oControl.getText() | webapp/control/xss.js:15:21:15:46 | value | +| webapp/control/xss.js:17:21:17:59 | xssSanitized | webapp/control/xss.js:23:43:23:54 | xssSanitized | +| webapp/control/xss.js:17:36:17:59 | encodeX ... value)) | webapp/control/xss.js:17:21:17:59 | xssSanitized | +| webapp/control/xss.js:17:46:17:58 | String(value) | webapp/control/xss.js:17:36:17:59 | encodeX ... value)) | +| webapp/control/xss.js:17:53:17:57 | value | webapp/control/xss.js:17:46:17:58 | String(value) | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/control/xss.js:9:23:9:40 | { type: "string" } | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/view/app.view.xml:5:5:7:28 | value={/input} | +| webapp/controller/app.controller.js:9:17:9:27 | input: null | webapp/view/app.view.xml:8:5:8:38 | text={/input} | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:9:17:9:27 | input: null | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:11:26:11:45 | new JSONModel(oData) | +| webapp/view/app.view.xml:8:5:8:38 | text={/input} | webapp/controller/app.controller.js:9:17:9:27 | input: null | +#select +| webapp/control/xss.js:23:43:23:54 | xssSanitized | webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/control/xss.js:23:43:23:54 | xssSanitized | The path of a saved file depends on a $@. | webapp/view/app.view.xml:5:5:7:28 | value={/input} | user-provided value | diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/UI5PathInjection.qlref b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/UI5PathInjection.qlref new file mode 100644 index 000000000..a8b5ee04e --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/UI5PathInjection.qlref @@ -0,0 +1 @@ +UI5PathInjection/UI5PathInjection.ql \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/package-lock.json b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/package-lock.json new file mode 100644 index 000000000..ba052860f --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sap-ui5-xss", + "version": "1.0.0" + } + } +} diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/package.json b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/package.json new file mode 100644 index 000000000..9f0a4560b --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/package.json @@ -0,0 +1,5 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "main": "index.js" +} diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/ui5.yaml b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/ui5.yaml new file mode 100644 index 000000000..beb2ff69d --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/ui5.yaml @@ -0,0 +1,7 @@ +specVersion: '3.0' +metadata: + name: sap-ui5-xss +type: application +framework: + name: SAPUI5 + version: "1.115.0" diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/control/xss.js b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/control/xss.js new file mode 100644 index 000000000..fd24e0222 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/control/xss.js @@ -0,0 +1,27 @@ +sap.ui.define([ + "sap/ui/core/Control", + "sap/base/security/encodeXML", + "sap/ui/core/util/File" +], function (Control, encodeXML, File) { + return Control.extend("codeql-sap-js.control.xss", { + metadata: { + properties: { + text: { type: "string" } + } + }, + renderer: { + apiVersion: 2, + render: function (oRm, oControl) { + var value = oControl.getText(); + /* XSS sanitizer is applied. */ + var xssSanitized = encodeXML(String(value)); + oRm.openStart("div", oControl); + /* Data is sanitized against XSS. */ + oRm.unsafeHtml(xssSanitized); + oRm.close("div"); + /* Data is not sanitized against formula injection. */ + File.save("some_content", xssSanitized, "csv", "text/csv", "utf-8"); + } + } + }); +}) diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/controller/app.controller.js b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/controller/app.controller.js new file mode 100644 index 000000000..e21cb709b --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/controller/app.controller.js @@ -0,0 +1,15 @@ +sap.ui.define([ + "sap/ui/core/mvc/Controller", + "sap/ui/model/json/JSONModel" +], function (Controller, JSONModel) { + "use strict" + return Controller.extend("codeql-sap-js.controller.app", { + onInit: function () { + var oData = { + input: null + }; + var oModel = new JSONModel(oData); + this.getView().setModel(oModel); + } + }); +}) \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/index.html b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/index.html new file mode 100644 index 000000000..0d9daa387 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/index.html @@ -0,0 +1,21 @@ + + + + + + + SAPUI5 XSS + + + + + + + + \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/index.js b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/index.js new file mode 100644 index 000000000..7a66697fd --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/index.js @@ -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"); + }); + +}); \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/manifest.json b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/manifest.json new file mode 100644 index 000000000..65d4de250 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/manifest.json @@ -0,0 +1,5 @@ +{ + "sap.app": { + "id": "sap-ui5-xss" + } +} \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/view/app.view.xml b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/view/app.view.xml new file mode 100644 index 000000000..9ba075630 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-custom-control-sanitized/webapp/view/app.view.xml @@ -0,0 +1,9 @@ + + + + diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/UI5PathInjection.expected b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/UI5PathInjection.expected new file mode 100644 index 000000000..0d871e07d --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/UI5PathInjection.expected @@ -0,0 +1,13 @@ +nodes +| webapp/controller/app.controller.js:10:17:10:27 | input: null | +| webapp/controller/app.controller.js:16:39:16:66 | oModel. ... input') | +| webapp/view/app.view.xml:5:5:7:28 | value={/input} | +| webapp/view/app.view.xml:8:5:8:37 | content={/output} | +edges +| webapp/controller/app.controller.js:10:17:10:27 | input: null | webapp/controller/app.controller.js:16:39:16:66 | 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:12:26:12:45 | new JSONModel(oData) | webapp/view/app.view.xml:8:5:8:37 | content={/output} | +| 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:12:26:12:45 | new JSONModel(oData) | +#select +| webapp/controller/app.controller.js:16:39:16:66 | oModel. ... input') | webapp/view/app.view.xml:5:5:7:28 | value={/input} | webapp/controller/app.controller.js:16:39:16:66 | oModel. ... input') | The path of a saved file depends on a $@. | webapp/view/app.view.xml:5:5:7:28 | value={/input} | user-provided value | diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/UI5PathInjection.qlref b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/UI5PathInjection.qlref new file mode 100644 index 000000000..a8b5ee04e --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/UI5PathInjection.qlref @@ -0,0 +1 @@ +UI5PathInjection/UI5PathInjection.ql \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/package-lock.json b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/package-lock.json new file mode 100644 index 000000000..ba052860f --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sap-ui5-xss", + "version": "1.0.0" + } + } +} diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/package.json b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/package.json new file mode 100644 index 000000000..9f0a4560b --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/package.json @@ -0,0 +1,5 @@ +{ + "name": "sap-ui5-xss", + "version": "1.0.0", + "main": "index.js" +} diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/ui5.yaml b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/ui5.yaml new file mode 100644 index 000000000..beb2ff69d --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/ui5.yaml @@ -0,0 +1,7 @@ +specVersion: '3.0' +metadata: + name: sap-ui5-xss +type: application +framework: + name: SAPUI5 + version: "1.115.0" diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/controller/app.controller.js b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/controller/app.controller.js new file mode 100644 index 000000000..0027a3b86 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/controller/app.controller.js @@ -0,0 +1,20 @@ +sap.ui.define([ + "sap/ui/core/mvc/Controller", + "sap/ui/model/json/JSONModel", + "sap/ui/core/util/File" +], function (Controller, JSONModel, File) { + "use strict"; + return Controller.extend("codeql-sap-js.controller.app", { + onInit: function () { + var oData = { + input: null, + }; + var oModel = new JSONModel(oData); + this.getView().setModel(oModel); + + /* Data is not sanitized against formula injection. */ + File.save("some_content", oModel.getProperty('/input'), "csv", "text/csv", "utf-8"); + } + }); +} +); diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/index.html b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/index.html new file mode 100644 index 000000000..0d9daa387 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/index.html @@ -0,0 +1,21 @@ + + + + + + + SAPUI5 XSS + + + + + + + + \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/index.js b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/index.js new file mode 100644 index 000000000..7a66697fd --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/index.js @@ -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"); + }); + +}); \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/manifest.json b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/manifest.json new file mode 100644 index 000000000..65d4de250 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/manifest.json @@ -0,0 +1,5 @@ +{ + "sap.app": { + "id": "sap-ui5-xss" + } +} \ No newline at end of file diff --git a/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/view/app.view.xml b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/view/app.view.xml new file mode 100644 index 000000000..148951696 --- /dev/null +++ b/javascript/frameworks/ui5/test/queries/UI5PathInjection/path-html-control-df/webapp/view/app.view.xml @@ -0,0 +1,9 @@ + + + +