Skip to content

Conversation

@mbaluda
Copy link
Contributor

@mbaluda mbaluda commented Nov 19, 2025

Key changes:

UI5 Control and Sanitization Logic

  • Extracted UI5Control.qll from UI5View.qll. Adds the predicate isSanitizedControl to identify controls where sanitization is enabled or disabled via the sanitizeContent property. Works with all kinds of view.
  • Updated the detection logic for HTML injection sinks to exclude controls where sanitization is enabled

UI5 Control and Sanitization Logic

  • Augmented existing tests to only flag HTML injection sinks when the control is not sanitized (models/sink xss-html-control xss-js-view xss-json-view README)

CodeQL Workflow Updates

  • Upgraded the CodeQL GitHub Actions

@jeongsoolee09

This comment has been minimized.

@mbaluda

This comment has been minimized.

@mbaluda mbaluda requested a review from Copilot November 21, 2025 23:16
Copilot finished reviewing on behalf of mbaluda November 21, 2025 23:19
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the CodeQL analysis for UI5 applications by ensuring the sanitizeContent attribute is properly respected when detecting HTML injection vulnerabilities. The changes extract control-related logic into a dedicated module and improve the detection of sanitized vs. unsanitized HTML controls across all view types (XML, JSON, and JavaScript).

Key changes:

  • Extracted UI5Control and UI5ControlProperty classes from UI5View.qll into a new UI5Control.qll file
  • Introduced isSanitizedControl() predicate to identify controls with sanitization enabled/disabled both declaratively and programmatically
  • Updated HTML injection sink detection to exclude sanitized controls from vulnerability reports
  • Enhanced test coverage with scenarios for declarative and programmatic sanitization control
  • Upgraded CodeQL GitHub Actions from v3 to v4

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5Control.qll New file containing extracted UI5Control and UI5ControlProperty classes with isSanitizedControl() predicate for sanitization detection
javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5View.qll Removed UI5Control and UI5ControlProperty class definitions (moved to UI5Control.qll) and removed XML-specific sanitization checks
javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll Added import for new UI5Control module
javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/dataflow/DataFlow.qll Added sanitization check to filter sanitized controls from HTML injection sinks
javascript/frameworks/ui5/test/models/sink/UI5ViewSinkTest.ql Updated query to exclude sanitized controls from sink detection
javascript/frameworks/ui5/test/models/sink/sink1.xml Added test case for sanitized HTML control
javascript/frameworks/ui5/test/queries/UI5Xss/xss-html-control/webapp/view/app.view.xml Added comprehensive test cases for declarative and programmatic sanitization scenarios
javascript/frameworks/ui5/test/queries/UI5Xss/xss-html-control/webapp/controller/app.controller.js Added controller logic to test programmatic sanitization control via setProperty()
javascript/frameworks/ui5/test/queries/UI5Xss/xss-html-control/UI5Xss.expected Updated expected results to reflect proper sanitization detection
javascript/frameworks/ui5/test/queries/UI5Xss/xss-json-view/webapp/view/app.view.json Added test case for sanitized HTML control in JSON view
javascript/frameworks/ui5/test/queries/UI5Xss/xss-json-view/UI5Xss.expected Updated expected results for JSON view sanitization
javascript/frameworks/ui5/test/queries/UI5Xss/xss-js-view/webapp/view/app.view.js Added test case for sanitized HTML control in JS view
javascript/frameworks/ui5/test/queries/UI5Xss/xss-js-view/UI5Xss.expected Updated expected results for JS view sanitization
javascript/frameworks/ui5/test/README.md Updated documentation to describe sanitization testing scenarios
.github/workflows/code_scanning.yml Upgraded CodeQL Actions from v3 to v4

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mbaluda mbaluda marked this pull request as ready for review November 22, 2025 00:12
Comment on lines 188 to 193
//or
// `sanitizeContent` attribute is set programmatically (not sufficient)
//result
// .getAReference()
// .hasPropertyWrite("sanitizeContent",
// any(DataFlow::Node n | not n.mayHaveBooleanValue(val.booleanNot())))
Copy link
Contributor

@jeongsoolee09 jeongsoolee09 Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should bring this back (possibly refined), as I guess it's to cover cases where the property is directly written to using assignment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a test for that and the HTML is not sanitized

@data-douser data-douser mentioned this pull request Nov 25, 2025
18 tasks
// enable sanitization programmatically
this.getView().byId("htmlJsSanitized2").setProperty("sanitizeContent", true);

// setting the property directly is not sufficient
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I ask you what you mean by not sufficient?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running that example, the XSS still works

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I guess the sap.ui.base.ManagedObject class wraps a plain JS object in a way that disallows direct setting of values using direct assignment.

So, I suggest two things:

  • Let's remove this case, and
  • Add a case using setSanitizeContent: It's a special case of generated setters (that we already model for user-defined properties). UI5 automatically generates getters and setters for all properties defined for a ManagedObject.

Comment on lines +855 to +865
bindingset[val]
private UI5Control sanitizeContentSetTo(boolean val) {
/* 1. `sanitizeContent` attribute is set declaratively. */
result.getProperty("sanitizeContent").toString() = val.toString()
or
/* 2. `sanitizeContent` attribute is set programmatically using setProperty(). */
exists(CallNode node | node = result.getAReference().getAMemberCall("setProperty") |
node.getArgument(0).getStringValue() = "sanitizeContent" and
not node.getArgument(1).mayHaveBooleanValue(val.booleanNot())
)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be rewritten to:

Suggested change
bindingset[val]
private UI5Control sanitizeContentSetTo(boolean val) {
/* 1. `sanitizeContent` attribute is set declaratively. */
result.getProperty("sanitizeContent").toString() = val.toString()
or
/* 2. `sanitizeContent` attribute is set programmatically using setProperty(). */
exists(CallNode node | node = result.getAReference().getAMemberCall("setProperty") |
node.getArgument(0).getStringValue() = "sanitizeContent" and
not node.getArgument(1).mayHaveBooleanValue(val.booleanNot())
)
}
private predicate sanitizeContentSetToTrue() {
this.getAReference().getAPropertyWrite("sanitizeContent").getRhs().mayHaveBooleanValue(true)
or
exists(CallNode setPropertyCall |
setPropertyCall = this.getAReference().getAMemberCall("setProperty")
|
setPropertyCall.getArgument(0).getStringValue() = "sanitizeContent" and
setPropertyCall.getArgument(1).mayHaveBooleanValue(true)
)
}
  1. Regarding the first disjunct,DataFlow::PropWrite class provides a clean way to describe property writes in general.
  2. The second clause of the second disjunct (the exists clause) contains a double negation. We can straighten it out by negating both.
  3. Since we're describing an instance of this class, we can rewrite it to a result-less predicate and use this in place of result. This predicate also does not bound this to a value.
  4. Also, the sanitizeContent property defaults to false. So, we're only interested in cases where it's explicitly set to true. So, we can confine the concern of this predicate to only checking if it's set to true. This does away with the need for bindingset[val].

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, taking this into account, we can change the first disjunct:

Suggested change
bindingset[val]
private UI5Control sanitizeContentSetTo(boolean val) {
/* 1. `sanitizeContent` attribute is set declaratively. */
result.getProperty("sanitizeContent").toString() = val.toString()
or
/* 2. `sanitizeContent` attribute is set programmatically using setProperty(). */
exists(CallNode node | node = result.getAReference().getAMemberCall("setProperty") |
node.getArgument(0).getStringValue() = "sanitizeContent" and
not node.getArgument(1).mayHaveBooleanValue(val.booleanNot())
)
}
private predicate sanitizeContentSetToTrue() {
this.getAReference().getAMemberCall("setSanitizeContent").getArgument(0).mayHaveBooleanValue(true)
or
exists(CallNode setPropertyCall |
setPropertyCall = this.getAReference().getAMemberCall("setProperty")
|
setPropertyCall.getArgument(0).getStringValue() = "sanitizeContent" and
setPropertyCall.getArgument(1).mayHaveBooleanValue(true)
)
}

Copy link
Contributor

@jeongsoolee09 jeongsoolee09 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second round!

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