Skip to content

Commit 8f67f25

Browse files
Merge pull request #258 from advanced-security/jeongsoolee09/add-eventbus-example
Model `sap/ui/core/EventBus`
2 parents e09ec95 + 8a8fff2 commit 8f67f25

File tree

27 files changed

+6782
-65
lines changed

27 files changed

+6782
-65
lines changed

.github/workflows/javascript.sarif.expected

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

javascript/frameworks/ui5/ext/ui5.model.yml

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@ extensions:
33
pack: codeql/javascript-all
44
extensible: "typeModel"
55
data:
6+
- ["SapUICoreInstance", "global", "Member[sap].Member[ui].Member[getCore].ReturnValue"]
67
- ["Control", "Control", "Instance"]
78
- ["Control", "sap/ui/core/Control", ""]
89
- ["Control", "global", "Member[sap].Member[ui].Member[core].Member[Control]"]
10+
- ["Controller", "Controller", "Instance"]
11+
- ["Controller", "sap/ui/core/mvc/Controller", ""]
12+
- ["Component", "sap/ui/core/mvc/Component", ""]
13+
- ["Component", "sap/ui/core/UIComponent", ""]
914
- ["Renderer", "Control", "Member[extend].Argument[1].Member[renderer]"]
1015
- ["Renderer", "sap/ui/core/RenderManager", "Member[extend].Argument[1].Member[renderer]"]
11-
- ["Renderer", "sap/ui/core/Renderer", "Member[extend].Argument[1]"]
16+
- ["Renderer", "sap/ui/core/Renderer", "Member[extend].Argument[1]"] # ?
1217
- ["RenderManager", "RenderManager", "Instance"]
1318
- ["RenderManager", "sap/ui/core/RenderManager", ""]
1419
- ["RenderManager", "Renderer", "Parameter[0]"]
@@ -71,6 +76,31 @@ extensions:
7176
- ["UI5ClientStorage", "global", "Member[jQuery].Member[sap].Member[storage]"]
7277
- ["UI5ClientStorage", "sap/ui/core/util/File", ""]
7378
- ["UI5ClientStorage", "global", "Member[sap].Member[ui].Member[core].Member[util].Member[File]"]
79+
# Publishing and Subscribing to Events
80+
- ["UI5EventBusInstance", "sap/ui/core/EventBus", "Member[getInstance].ReturnValue"]
81+
- ["UI5EventBusPublish", "UI5EventBusInstance", "Member[publish]"]
82+
- ["UI5EventBusPublishedEventData", "UI5EventBusPublish", "Argument[2]"]
83+
- ["UI5EventBusSubscribe", "UI5EventBusInstance", "Member[subscribe,subscribeOnce]"]
84+
- ["UI5EventSubscriptionHandlerDataParameter", "UI5EventBusSubscribe", "Argument[2].Parameter[2]"]
85+
- ["SapUICoreEventBusInstance", "SapUICoreInstance", "Member[getEventBus].ReturnValue"]
86+
- ["SapUICoreEventBusPublish", "SapUICoreEventBusInstance", "Member[publish]"]
87+
- ["SapUICoreEventBusPublishedEventData", "SapUICoreEventBusPublish", "Argument[2]"]
88+
- ["SapUICoreEventBusSubscribe", "SapUICoreEventBusInstance", "Member[subscribe,subscribeOnce]"]
89+
- ["SapUICoreEventSubscriptionHandlerDataParameter", "SapUICoreEventBusSubscribe", "Argument[2].Parameter[2]"]
90+
# Extend Calls
91+
- ["CustomControl", "Control", "Member[extend]"]
92+
- ["CustomController", "Controller", "Member[extend]"]
93+
- ["CustomControllerContent", "Controller", "Member[extend].Argument[1]"]
94+
- ["CustomControllerGetOwnerComponent", "CustomControllerContent", "Fuzzy.Member[getOwnerComponent].ReturnValue"]
95+
- ["CustomControllerGetOwnerComponentEventBus", "CustomControllerGetOwnerComponent", "Member[getEventBus].ReturnValue"]
96+
- ["CustomControllerGetOwnerComponentEventBusPublish", "CustomControllerGetOwnerComponentEventBus", "Member[publish]"]
97+
- ["CustomControllerGetOwnerComponentEventBusPublishedData", "CustomControllerGetOwnerComponentEventBusPublish", "Argument[2]"]
98+
- ["CustomControllerGetOwnerComponentEventBusSubscribe", "CustomControllerGetOwnerComponentEventBus", "Member[subscribe,subscribeOnce]"]
99+
- ["CustomControllerGetOwnerComponentEventBusSubscriptionHandlerDataParameter", "CustomControllerGetOwnerComponentEventBusSubscribe", "Argument[2].Parameter[2]"]
100+
- ["CustomComponent", "Component", "Member[extend]"]
101+
- ["CustomRenderer", "Renderer", "Member[extend]"]
102+
- ["ViewReference", "CustomController", "Member[getView].ReturnValue"]
103+
- ["ControlReference", "ViewReference", "Member[byId].ReturnValue"]
74104

75105
- addsTo:
76106
pack: codeql/javascript-all

javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/RemoteFlowSources.qll

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,47 @@ import advanced_security.javascript.frameworks.ui5.UI5View
44
import semmle.javascript.security.dataflow.XssThroughDomCustomizations
55
private import semmle.javascript.frameworks.data.internal.ApiGraphModelsExtensions
66

7-
private class DataFromRemoteControlReference extends RemoteFlowSource {
8-
DataFromRemoteControlReference() {
9-
exists(UI5Control sourceControl, string typeAlias, ControlReference controlReference |
7+
abstract private class RemoteControlAPISource extends SourceNode { }
8+
9+
private class RemoteControlReference extends RemoteControlAPISource, ControlReference {
10+
RemoteControlReference() {
11+
exists(UI5Control sourceControl, string typeAlias |
1012
typeModel(typeAlias, sourceControl.getImportPath(), _) and
1113
sourceModel(typeAlias, _, "remote", _) and
12-
sourceControl.getAReference() = controlReference and
13-
(
14-
this = controlReference.getAMemberCall("getValue") or
15-
this = controlReference.getAPropertyRead("value")
16-
)
14+
sourceControl.getAReference() = this
15+
)
16+
}
17+
}
18+
19+
private class RemoteControlHandlerParameter extends RemoteControlAPISource, CallNode {
20+
RemoteControlHandlerParameter() {
21+
exists(UI5Control sourceControl, string typeAlias, UI5Handler handler |
22+
typeModel(typeAlias, sourceControl.getImportPath(), _) and
23+
sourceModel(typeAlias, _, "remote", _) and
24+
handler.getControl() = sourceControl and
25+
this = handler.getParameter(0).getAMemberCall("getSource")
26+
)
27+
}
28+
}
29+
30+
private class UserDataFromRemoteControlAPISource extends RemoteFlowSource {
31+
UserDataFromRemoteControlAPISource() {
32+
exists(RemoteControlAPISource remoteControlAPISource |
33+
/*
34+
* 1. The `value` or its getter of `HTML` control reference, `CodeEditor` control reference,
35+
* or handler parameters.
36+
*/
37+
38+
this = remoteControlAPISource.getAPropertyRead("value") or
39+
this = remoteControlAPISource.getAMemberCall("getValue") or
40+
/* 2. The `getCurrentValue` method call on `CodeEditor` control reference. */
41+
this = remoteControlAPISource.getAMemberCall("getCurrentValue")
1742
)
1843
}
1944

20-
override string getSourceType() { result = "Data from a remote control" }
45+
override string getSourceType() {
46+
result = "User-provided data fetched from an input control via JavaScript API"
47+
}
2148
}
2249

2350
private class InputControlInstantiation extends ElementInstantiation {

javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/UI5.qll

Lines changed: 181 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import javascript
22
import DataFlow
33
import advanced_security.javascript.frameworks.ui5.JsonParser
4+
import advanced_security.javascript.frameworks.ui5.dataflow.TypeTrackers
45
import semmle.javascript.security.dataflow.DomBasedXssCustomizations
56
import advanced_security.javascript.frameworks.ui5.UI5View
67
import advanced_security.javascript.frameworks.ui5.UI5HTML
@@ -138,6 +139,15 @@ class WebApp extends HTML::HtmlFile {
138139
* by a call to `sap.ui.getCore()`.
139140
*/
140141
class SapUiCore extends MethodCallNode {
142+
/*
143+
* NOTE: Ideally, we'd like to use `ModelOutput::getATypeNode("SapUICore").asSource()` to
144+
* take advantage of the inter-procedural flexibility of MaD, but doing so causes
145+
* non-monotomic recursion.
146+
*
147+
* So, we opt to use `SourceNode.getAPropertyRead/1` and `SourceNode.getAMethodCall/1`
148+
* instead and get away with local flow tracking they provide.
149+
*/
150+
141151
SapUiCore() { this = globalVarRef("sap").getAPropertyRead("ui").getAMethodCall("getCore") }
142152
}
143153

@@ -247,9 +257,8 @@ class Renderer extends SapExtendCall {
247257

248258
class CustomControl extends SapExtendCall {
249259
CustomControl() {
250-
this =
251-
TypeTrackers::hasDependency(["sap/ui/core/Control", "sap.ui.core.Control"])
252-
.getAMemberCall("extend") or
260+
this = ModelOutput::getATypeNode("CustomControl").getACall()
261+
or
253262
exists(SapDefineModule sapModule | this.getDefine() = sapModule.getExtendingModule())
254263
}
255264

@@ -449,17 +458,18 @@ class ControllerReference extends Reference {
449458
}
450459

451460
class CustomController extends SapExtendCall {
461+
API::Node customController;
452462
string name;
453463

454464
CustomController() {
455-
this =
456-
TypeTrackers::hasDependency(["sap/ui/core/mvc/Controller", "sap.ui.core.mvc.Controller"])
457-
.getAMemberCall("extend") and
458-
name = this.getFile().getBaseName().regexpCapture("([a-zA-Z0-9]+).[cC]ontroller.js", 1)
465+
customController = ModelOutput::getATypeNode("CustomController") and
466+
this = customController.getACall() and
467+
name = this.getFile().getBaseName().regexpCapture("(.+).[cC]ontroller.js", 1)
459468
}
460469

461470
Component getOwnerComponent() {
462-
exists(ManifestJson manifestJson, JsonObject rootObj | manifestJson = result.getManifestJson() |
471+
exists(ManifestJson manifestJson, JsonObject rootObj |
472+
manifestJson = result.getManifestJson() and
463473
rootObj
464474
.getPropValue("targets")
465475
.(JsonObject)
@@ -474,7 +484,12 @@ class CustomController extends SapExtendCall {
474484
}
475485

476486
MethodCallNode getOwnerComponentRef() {
477-
result = this.getAThisNode().getAMemberCall("getOwnerComponent")
487+
exists(API::Node getOwnerComponent |
488+
getOwnerComponent = ModelOutput::getATypeNode("CustomControllerGetOwnerComponent")
489+
|
490+
customController.getASuccessor+() = getOwnerComponent and
491+
result = getOwnerComponent.asSource()
492+
)
478493
}
479494

480495
/**
@@ -757,14 +772,10 @@ class Component extends SapExtendCall {
757772
* It is the value flowing to a `setModel` call in a handler of a `CustomController` (which is represented by `ControllerHandler`), since it is the closest we can get to the actual model itself.
758773
*/
759774

760-
this =
761-
TypeTrackers::hasDependency([
762-
"sap/ui/core/mvc/Component", "sap.ui.core.mvc.Component", "sap/ui/core/UIComponent",
763-
"sap.ui.core.UIComponent"
764-
]).getAMemberCall("extend")
775+
this = ModelOutput::getATypeNode("CustomComponent").getACall()
765776
}
766777

767-
string getId() { result = this.getName().regexpCapture("([a-zA-Z0-9.]+).Component", 1) }
778+
string getId() { result = this.getName().regexpCapture("(.+).Component", 1) }
768779

769780
ManifestJson getManifestJson() {
770781
this.getMetadata().getAPropertySource("manifest").asExpr().(StringLiteral).getValue() = "json" and
@@ -1428,18 +1439,162 @@ class PropertyMetadata extends ObjectLiteralNode {
14281439
}
14291440
}
14301441

1431-
module TypeTrackers {
1432-
private SourceNode hasDependency(TypeTracker t, string dependencyPath) {
1433-
t.start() and
1434-
exists(UserModule d |
1435-
d.getADependency() = dependencyPath and
1436-
result = d.getRequiredObject(dependencyPath).asSourceNode()
1437-
)
1438-
or
1439-
exists(TypeTracker t2 | result = hasDependency(t2, dependencyPath).track(t2, t))
1442+
module EventBus {
1443+
abstract class EventBusPublishCall extends CallNode {
1444+
abstract EventBusSubscribeCall getAMatchingSubscribeCall();
1445+
1446+
abstract DataFlow::Node getPublishedData();
1447+
1448+
string getChannelName() { result = this.getArgument(0).getALocalSource().getStringValue() }
1449+
1450+
string getMessageType() { result = this.getArgument(1).getALocalSource().getStringValue() }
1451+
}
1452+
1453+
abstract class EventBusSubscribeCall extends CallNode {
1454+
abstract EventBusPublishCall getMatchingPublishCall();
1455+
1456+
abstract DataFlow::Node getSubscriptionData();
1457+
1458+
string getChannelName() { result = this.getArgument(0).getALocalSource().getStringValue() }
1459+
1460+
string getMessageType() { result = this.getArgument(1).getALocalSource().getStringValue() }
1461+
}
1462+
1463+
class GlobalEventBusPublishCall extends EventBusPublishCall {
1464+
API::Node publishMethod;
1465+
1466+
GlobalEventBusPublishCall() {
1467+
publishMethod = ModelOutput::getATypeNode("UI5EventBusPublish") and
1468+
this = publishMethod.getACall()
1469+
}
1470+
1471+
override GlobalEventBusSubscribeCall getAMatchingSubscribeCall() {
1472+
result.getChannelName() = this.getChannelName() and
1473+
result.getMessageType() = this.getMessageType()
1474+
}
1475+
1476+
override DataFlow::Node getPublishedData() {
1477+
exists(API::Node publishedData |
1478+
publishedData = ModelOutput::getATypeNode("UI5EventBusPublishedEventData")
1479+
|
1480+
publishMethod.getASuccessor*() = publishedData and
1481+
result = publishedData.getInducingNode()
1482+
)
1483+
}
1484+
}
1485+
1486+
class SapUICoreEventBusPublishCall extends EventBusPublishCall {
1487+
API::Node publishMethod;
1488+
1489+
SapUICoreEventBusPublishCall() {
1490+
publishMethod = ModelOutput::getATypeNode("SapUICoreEventBusPublish") and
1491+
this = publishMethod.getACall()
1492+
}
1493+
1494+
override SapUICoreEventBusSubscribeCall getAMatchingSubscribeCall() {
1495+
result.getChannelName() = this.getChannelName() and
1496+
result.getMessageType() = this.getMessageType()
1497+
}
1498+
1499+
override DataFlow::Node getPublishedData() {
1500+
exists(API::Node publishedData |
1501+
publishedData = ModelOutput::getATypeNode("SapUICoreEventBusPublishedEventData")
1502+
|
1503+
publishMethod.getASuccessor*() = publishedData and
1504+
result = publishedData.getInducingNode()
1505+
)
1506+
}
1507+
}
1508+
1509+
class ComponentEventBusPublishCall extends EventBusPublishCall {
1510+
API::Node customController;
1511+
1512+
ComponentEventBusPublishCall() {
1513+
exists(API::Node customControllerGetOwnerComponentEventBusPublish |
1514+
customControllerGetOwnerComponentEventBusPublish =
1515+
ModelOutput::getATypeNode("CustomControllerGetOwnerComponentEventBusPublish")
1516+
|
1517+
customController = ModelOutput::getATypeNode("CustomController") and
1518+
customControllerGetOwnerComponentEventBusPublish = customController.getASuccessor+() and
1519+
this = customControllerGetOwnerComponentEventBusPublish.getACall()
1520+
)
1521+
}
1522+
1523+
override ComponentEventBusSubscribeCall getAMatchingSubscribeCall() {
1524+
result.getChannelName() = this.getChannelName() and
1525+
result.getMessageType() = this.getMessageType()
1526+
}
1527+
1528+
override DataFlow::Node getPublishedData() { result = this.getArgument(2) }
14401529
}
14411530

1442-
SourceNode hasDependency(string dependencyPath) {
1443-
result = hasDependency(TypeTracker::end(), dependencyPath)
1531+
class GlobalEventBusSubscribeCall extends EventBusSubscribeCall {
1532+
API::Node subscribeMethod;
1533+
1534+
GlobalEventBusSubscribeCall() {
1535+
subscribeMethod = ModelOutput::getATypeNode("UI5EventBusSubscribe") and
1536+
this = subscribeMethod.getACall()
1537+
}
1538+
1539+
override GlobalEventBusPublishCall getMatchingPublishCall() {
1540+
result.getChannelName() = this.getChannelName() and
1541+
result.getMessageType() = this.getMessageType()
1542+
}
1543+
1544+
override DataFlow::Node getSubscriptionData() {
1545+
exists(API::Node subscribeMethodCallbackDataParameter |
1546+
subscribeMethodCallbackDataParameter =
1547+
ModelOutput::getATypeNode("UI5EventSubscriptionHandlerDataParameter")
1548+
|
1549+
subscribeMethod.getASuccessor*() = subscribeMethodCallbackDataParameter and
1550+
result = subscribeMethodCallbackDataParameter.getInducingNode()
1551+
)
1552+
}
1553+
}
1554+
1555+
class SapUICoreEventBusSubscribeCall extends EventBusSubscribeCall {
1556+
API::Node subscribeMethod;
1557+
1558+
SapUICoreEventBusSubscribeCall() {
1559+
subscribeMethod = ModelOutput::getATypeNode("SapUICoreEventBusSubscribe") and
1560+
this = subscribeMethod.getACall()
1561+
}
1562+
1563+
override SapUICoreEventBusPublishCall getMatchingPublishCall() {
1564+
result.getChannelName() = this.getChannelName() and
1565+
result.getMessageType() = this.getMessageType()
1566+
}
1567+
1568+
override DataFlow::Node getSubscriptionData() {
1569+
exists(API::Node subscribeMethodCallbackDataParameter |
1570+
subscribeMethodCallbackDataParameter =
1571+
ModelOutput::getATypeNode("SapUICoreEventSubscriptionHandlerDataParameter")
1572+
|
1573+
subscribeMethod.getASuccessor+() = subscribeMethodCallbackDataParameter and
1574+
result = subscribeMethodCallbackDataParameter.getInducingNode()
1575+
)
1576+
}
1577+
}
1578+
1579+
class ComponentEventBusSubscribeCall extends EventBusSubscribeCall {
1580+
API::Node customController;
1581+
1582+
ComponentEventBusSubscribeCall() {
1583+
exists(API::Node customControllerGetOwnerComponentEventBusSubscribe |
1584+
customControllerGetOwnerComponentEventBusSubscribe =
1585+
ModelOutput::getATypeNode("CustomControllerGetOwnerComponentEventBusSubscribe")
1586+
|
1587+
customController = ModelOutput::getATypeNode("CustomController") and
1588+
customControllerGetOwnerComponentEventBusSubscribe = customController.getASuccessor+() and
1589+
this = customControllerGetOwnerComponentEventBusSubscribe.getACall()
1590+
)
1591+
}
1592+
1593+
override ComponentEventBusPublishCall getMatchingPublishCall() {
1594+
result.getChannelName() = this.getChannelName() and
1595+
result.getMessageType() = this.getMessageType()
1596+
}
1597+
1598+
override DataFlow::Node getSubscriptionData() { result = this.getABoundCallbackParameter(2, 2) }
14441599
}
14451600
}

0 commit comments

Comments
 (0)