Skip to content

Conversation

@jeongsoolee09
Copy link
Contributor

@jeongsoolee09 jeongsoolee09 commented Oct 1, 2025

What This PR Contributes

The points are listed in the order of importance.

1. Cover XSS happening in dynamically instantiated Input and HTML

Consider this UI5 application fitting in a single index.html file:

<script>
    sap.ui.getCore().attachInit(() => {
        sap.ui.require([
            "sap/m/Input",
            "sap/m/Button",
            "sap/m/VBox",
            "sap/ui/core/HTML"
        ], (Input, Button, VBox, HTML) => {
            "use strict";

            // Create the VBox with Input and Button
            new VBox({
                items: [
                    new Input({
                        placeholder: "Enter your name",
                        id: "userInput"
                    }),
                    new Button({
                        text: "Greet",
                        press: () => {
                            const userInput = sap.ui.getCore().byId("userInput").getValue();
                            const htmlControl = new HTML({
                                content: `<div>${userInput}</div>`
                            });
                            htmlControl.placeAt("dynamicContent");
                        }
                    })
                ]
            }).placeAt("ui5Container"); // Place the VBox in a dedicated container
        });
    });
</script>

When a button is pressed, a user-provided value is read from the Input control and is fed to an HTML control. Usually the three controls,Input, Button, and HTML would all be defined in a view file (e.g. App.view.xml) and the user-provided data would be wired together using a model (e.g. JSON model) attached to the view via a binding path, and controller holding them altogether (e.g. App.controller.js). Our query was geared towards this "full application" with the standard MVC parts, and was failing to capture the XSS happening as in above. This PR aims to cover cases such as above where XSS happens without the use of model + binding paths.

Bifurcate UI5Control into UI5InputControl (controls capable of receiving user input) and UI5HTMLControl (sap.m.HTML)

The previous MaD models did not differentiate UI5 library controls that are capable of receiving user input (various input controls) and one that displays them (sap.m.HTML). This created problems trying to model an instantiation of sap.m.HTML (HTMLControlInstantiation), and those of various input controls (InputControlInstantiation). Therefore, we differentiate them.

Add an auxiliary data flow configuration TrackPlaceAtCallConfig

This is a helper configuration later used in DataFromInstantiatedAndPlacedAtControl (a remote flow source) and DynamicallySetElementValueOfInstantiatedHTMLControlPlacedAtDom (a new type of HTML sink we add in this PR).

If there are no calls to placeAt in the above code, XSS cannot happen because those controls won't be shown in the DOM and be presented to the user in the browser screen in the first place. Therefore, in addition to capturing instantiations to the input controls and HTML control, we check if the instantiated controls are place in a DOM by looking for a placeAt method called on them.

Add a unit test for DynamicallySetElementValueOfInstantiatedHTMLControlPlacedAtDom

In the sample controller javascript/frameworks/ui5/test/models/dynamic_write_to_html_content/webapp/controller/app.controller.js, we record what we aim to find and to not find. All the unsafe code locations have one thing in common: the untrusted data is set without any use of model + binding paths.

Add DynamicallySetElementValueOfInstantiatedHTMLControlPlacedAtDom

This class aims to find writes to the content property of an instantiated HTML control. It corresponds to the locations found in the doSomething1 method in the sample controller.

Add DynamicallySetElementValueOfHTMLControlReference

This class aims to find write to the content property of a reference to an HTML control that is statically declared in the XML view. Similar to the above, it does not invovle any model + binding paths.

2. Add UI5 customizations for the default query XssThroughDom

There may be UI5 applications that write to unsafe property to certain default JavaScript sinks, such as Element.innerHTML (for some testing reasons). These are sinks of the default query XssThroughDom that belongs to the javascript/security-extended suite. However, for that query to run smoothly on UI5 applications it needs some UI5-specific customizations, mainly (1) remote flow sources and (2) summary steps.

This PR defines them in the ui5.model.yml extension file, in the section that contributes to the sourceModel and summaryModel of codeql/javascript-all. The definitions are provisioned to the default queries by virtue of them contributed directly to codeql/javascript-all.

3. Improvements to QL code

There are two major anti-patterns that are replaced. Rewriting these makes the models cover equivalent cases that are equivalent in terms of data flow, yet different in terms of AST.

1. flowsTo / getALocalSource + getMethodName

These show up in the (mutually equivalent) form of:

A.getReceiver().getALocalSource() = B and A.methodName() = "someMethod"

or

A.methodName() = "someMethod" and B.flowsTo(A)

All of the above can be rewritten as

A = B.getAMemberCall("someMethod")

2. A.getArgument(0).getALocalSource().asExpr().(StringLiteral).getValue()

This pattern demands that the expression the argument originates from is a string literal. However, the string argument can be dynamically computed (e.g. concatenation). These string operations are covered via DataFlow::Node.getStringValue/0, so use that:

A.getArgument(0).getStringValue()

It is much easier to read and understand than the original code.

4. Code cleanup

Remove unused type TSapElement and SapElement; they only caused confusion and served no particular function.

5. Stylistic improvments

Reflow comments and change inline ones to module-level QLDocs.

Future Works

Document more parts of the code as the PR review proceeds.

@jeongsoolee09 jeongsoolee09 self-assigned this Oct 1, 2025
@jeongsoolee09 jeongsoolee09 marked this pull request as ready for review October 29, 2025 23:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants