From 0c1b2cf0c2a1f6527bcd37b35cb8681e05d47ee2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 02:05:35 +0000 Subject: [PATCH 1/5] Initial plan From 010c01699462b9b8c586f745db596ca2223f43ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 02:13:08 +0000 Subject: [PATCH 2/5] initial plan Agent-Logs-Url: https://github.com/advanced-security/codeql-sap-js/sessions/b8d4d32d-c84b-4174-bc1e-f00aa8b7ede4 Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com> --- .codeql-version | 1 + 1 file changed, 1 insertion(+) create mode 100644 .codeql-version diff --git a/.codeql-version b/.codeql-version new file mode 100644 index 000000000..2f1bd38e4 --- /dev/null +++ b/.codeql-version @@ -0,0 +1 @@ +v2.25.1 From 198a87f5f94e7c57bc20b01cdcf5ef60c047a57d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 02:37:16 +0000 Subject: [PATCH 3/5] Optimize UI5BindingPath.getNode() and constructPathStringInner performance - Extract getNode() disjuncts 1-1 and 1-2 into pragma[nomagic] helper predicates (getHardcodedJsonModelNode, getJsonFileModelNode) to prevent cross-product explosion on large codebases. This matches the existing pattern used for disjuncts 1-3 and 2. - Add pragma[nomagic] to both constructPathStringInner recursive predicates to prevent inlining into calling contexts. Addresses the critical UI5Xss.ql ~240x performance regression on large databases where getNode() accounted for 98.4% of evaluation time. Agent-Logs-Url: https://github.com/advanced-security/codeql-sap-js/sessions/b8d4d32d-c84b-4174-bc1e-f00aa8b7ede4 Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com> --- .../javascript/frameworks/ui5/UI5.qll | 2 + .../javascript/frameworks/ui5/UI5View.qll | 53 +++++++++++++------ 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll index 57d2e243a..a5fe7f3de 100644 --- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll +++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll @@ -1090,6 +1090,7 @@ module ManifestJson { } /** The manifest.json file serving as the app descriptor. */ + pragma[nomagic] private string constructPathStringInner(Expr object) { if not object instanceof ObjectExpr then result = "" @@ -1117,6 +1118,7 @@ module ManifestJson { ) } + pragma[nomagic] private string constructPathStringInner(Expr object, Property property) { if not object instanceof ObjectExpr then result = "" 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 688e52ccb..4662bfabc 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 @@ -147,23 +147,10 @@ abstract class UI5BindingPath extends BindingPath { */ Node getNode() { /* 1-1. Internal (Client-side) model, model hardcoded in JS code */ - exists(Property p, JsonModel model | - /* Get the property of an JS object bound to this binding path. */ - result.(DataFlow::PropWrite).getPropertyNameExpr() = p.getNameExpr() and - this.getAbsolutePath() = model.getPathString(p) and - /* Restrict search to inside the same webapp. */ - inSameWebApp(this.getLocation().getFile(), result.getFile()) - ) + result = getHardcodedJsonModelNode(this) or /* 1-2. Internal (Client-side) model, model loaded from JSON file */ - exists(string propName, JsonModel model | - /* Get the property of an JS object bound to this binding path. */ - result = model.getArgument(0).getALocalSource() and - this.getPath() = model.getPathStringPropName(propName) and - exists(JsonObject obj, JsonValue val | val = obj.getPropValue(propName)) and - /* Restrict search to inside the same webapp. */ - inSameWebApp(this.getLocation().getFile(), result.getFile()) - ) + result = getJsonFileModelNode(this) or /* 1-3. Internal (Client-side) model, content not statically visible */ result = getNonStaticJsonModelNode(this) @@ -198,6 +185,42 @@ private DefaultODataServiceModel getDefaultODataModel(UI5BindingPath bindingPath ) } +/** + * Gets the `DataFlow::Node` for a binding path whose model data is hardcoded + * in a JS object literal. Matches the property write in the object against + * the binding path's absolute path. + * + * `nomagic` to prevent the `getAbsolutePath() = model.getPathString(p)` join + * from being inlined into `getNode()`, which caused a cross-product explosion + * on large codebases. + */ +pragma[nomagic] +private Node getHardcodedJsonModelNode(UI5BindingPath bindingPath) { + exists(Property p, JsonModel model | + result.(DataFlow::PropWrite).getPropertyNameExpr() = p.getNameExpr() and + bindingPath.getAbsolutePath() = model.getPathString(p) and + inSameWebApp(bindingPath.getLocation().getFile(), result.getFile()) + ) +} + +/** + * Gets the `DataFlow::Node` for a binding path whose model data is loaded + * from a JSON file. + * + * `nomagic` to prevent the `getPath() = model.getPathStringPropName(propName)` join + * from being inlined into `getNode()`, which caused a cross-product explosion + * on large codebases. + */ +pragma[nomagic] +private Node getJsonFileModelNode(UI5BindingPath bindingPath) { + exists(string propName, JsonModel model | + result = model.getArgument(0).getALocalSource() and + bindingPath.getPath() = model.getPathStringPropName(propName) and + exists(JsonObject obj, JsonValue val | val = obj.getPropValue(propName)) and + inSameWebApp(bindingPath.getLocation().getFile(), result.getFile()) + ) +} + /** * Gets the `DataFlow::Node` for a non-statically-visible `JsonModel`. * From a1ca57a76d20e309e22d753f3dd974abc415a246 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 02:38:50 +0000 Subject: [PATCH 4/5] Remove accidentally committed .codeql-version file Agent-Logs-Url: https://github.com/advanced-security/codeql-sap-js/sessions/b8d4d32d-c84b-4174-bc1e-f00aa8b7ede4 Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com> --- .codeql-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.codeql-version b/.codeql-version index 2f1bd38e4..a4057dd6a 100644 --- a/.codeql-version +++ b/.codeql-version @@ -1 +1 @@ -v2.25.1 +v2.25.1 \ No newline at end of file From cb94e85c31d337d98e73ff6e95fa436222d48175 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 02:39:09 +0000 Subject: [PATCH 5/5] Remove .codeql-version (dynamically generated, not part of repo) Agent-Logs-Url: https://github.com/advanced-security/codeql-sap-js/sessions/b8d4d32d-c84b-4174-bc1e-f00aa8b7ede4 Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com> --- .codeql-version | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .codeql-version diff --git a/.codeql-version b/.codeql-version deleted file mode 100644 index a4057dd6a..000000000 --- a/.codeql-version +++ /dev/null @@ -1 +0,0 @@ -v2.25.1 \ No newline at end of file