Skip to content

Commit f807d93

Browse files
authored
Merge branch 'main' into GeekMasher-patch-1
2 parents a297b67 + d1b77c7 commit f807d93

File tree

25 files changed

+632
-317
lines changed

25 files changed

+632
-317
lines changed

.github/workflows/cds-extractor-dist-bundle.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ jobs:
1919

2020
steps:
2121
- name: Checkout repository
22-
uses: actions/checkout@v4
22+
uses: actions/checkout@v5
2323

2424
- name: Setup Node.js
25-
uses: actions/setup-node@v4
25+
uses: actions/setup-node@v6
2626
with:
2727
node-version: '20'
2828
cache: 'npm'

.github/workflows/code_scanning.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424

2525
steps:
2626
- name: Checkout repository
27-
uses: actions/checkout@v4
27+
uses: actions/checkout@v5
2828

2929
- name: Prepare local CodeQL model packs
3030
run: |
@@ -89,7 +89,7 @@ jobs:
8989
9090
- name: Upload sarif change
9191
if: steps.validate.outcome != 'success'
92-
uses: actions/upload-artifact@v4
92+
uses: actions/upload-artifact@v5
9393
with:
9494
name: sarif
9595
path: |

.github/workflows/run-codeql-unit-tests-javascript.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
matrix: ${{ steps.export-unit-test-matrix.outputs.matrix }}
1919
steps:
2020
- name: Checkout repository
21-
uses: actions/checkout@v4
21+
uses: actions/checkout@v5
2222

2323
- name: Install QLT
2424
id: install-qlt
@@ -43,7 +43,7 @@ jobs:
4343

4444
steps:
4545
- name: Checkout repository
46-
uses: actions/checkout@v4
46+
uses: actions/checkout@v5
4747

4848
- name: Install QLT
4949
id: install-qlt
@@ -78,7 +78,7 @@ jobs:
7878
qlt query run install-packs
7979
8080
- name: Setup Node.js for CDS compilation
81-
uses: actions/setup-node@v4
81+
uses: actions/setup-node@v6
8282
with:
8383
node-version: '18'
8484
cache: 'npm'
@@ -121,7 +121,7 @@ jobs:
121121
--work-dir $RUNNER_TMP
122122
123123
- name: Upload test results
124-
uses: actions/upload-artifact@v4
124+
uses: actions/upload-artifact@v5
125125
with:
126126
name: test-results-${{ runner.os }}-${{ matrix.codeql_cli }}-${{ matrix.codeql_standard_library_ident }}
127127
path: |
@@ -135,7 +135,7 @@ jobs:
135135
steps:
136136

137137
- name: Checkout repository
138-
uses: actions/checkout@v4
138+
uses: actions/checkout@v5
139139

140140
- name: Install QLT
141141
id: install-qlt
@@ -146,7 +146,7 @@ jobs:
146146

147147

148148
- name: Collect test results
149-
uses: actions/download-artifact@v4
149+
uses: actions/download-artifact@v6
150150

151151
- name: Validate test results
152152
run: |

.github/workflows/update-codeql.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717

1818
steps:
1919
- name: Checkout repository
20-
uses: actions/checkout@v4
20+
uses: actions/checkout@v5
2121

2222
- name: Check latest CodeQL CLI version and update qlt.conf.json
2323
id: check-version
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
// This file is included for use in custom CodeQL bundles (https://github.com/advanced-security/codeql-bundle).
2-
// The contents of this file will be included in the standard library `Customizations.qll`, and will therefore
3-
// be included in the out-of-the-box security queries.
4-
//
5-
// We import under alias to avoid any potential naming conflicts
1+
/**
2+
* This file is included for use in custom CodeQL bundles (https://github.com/advanced-security/codeql-bundle).
3+
* The contents of this file will be included in the standard library `Customizations.qll`, and will therefore
4+
* be included in the out-of-the-box security queries.
5+
*/
6+
7+
/* We import under alias to avoid any potential naming conflicts */
68
import advanced_security.javascript.frameworks.cap.RemoteFlowSources as CAPRemoteFlowSources

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

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ extensions:
1515
- ["RenderManager", "Renderer", "Member[render].Parameter[0]"]
1616
- ["Patcher", "Patcher", "Instance"]
1717
- ["Patcher", "sap/ui/core/Patcher", ""]
18-
- ["HTMLControl", "HTMLControl", "Instance"]
19-
- ["HTMLControl", "sap/ui/core/HTML", ""]
20-
- ["HTMLControl", "global", "Member[sap].Member[ui].Member[core].Member[HTML]"]
18+
- ["UI5HTMLControl", "UI5HTMLControl", "Instance"]
19+
- ["UI5HTMLControl", "sap/ui/core/HTML", ""]
20+
- ["UI5HTMLControl", "global", "Member[sap].Member[ui].Member[core].Member[HTML]"]
2121
- ["Assert", "global", "Member[sap].Member[base].Member[assert]"]
2222
- ["Assert", "global", "Member[jQuery].Member[sap].Member[assert]"]
2323
- ["SapLogger", "sap/base/Log", ""]
@@ -41,21 +41,27 @@ extensions:
4141
- ["SapUIDom", "sap/ui/dom", ""]
4242
- ["SapUIDom", "global", "Member[sap].Member[ui].Member[dom]"]
4343
# model Controls inheritance
44-
- ["UI5Control", "UI5Control", "Instance"]
45-
- ["UI5Control", "sap/m/InputBase", ""]
46-
- ["UI5Control", "sap/m/Input", ""]
47-
- ["UI5Control", "sap/ui/webc/main/Input", ""]
48-
- ["UI5Control", "sap/ui/commons/TextField", ""]
49-
- ["UI5Control", "sap/m/ComboBoxTextField", ""]
50-
- ["UI5Control", "sap/m/MaskEnabler", ""]
51-
- ["UI5Control", "sap/m/MaskInput", ""]
52-
- ["UI5Control", "sap/m/TextArea", ""]
53-
- ["UI5Control", "sap/m/ComboBoxBase", ""]
54-
- ["UI5Control", "sap/m/MultiInput", ""]
55-
- ["UI5Control", "sap/ui/webc/main/MultiInput", ""]
56-
- ["UI5Control", "sap/m/SearchField", ""]
57-
- ["UI5Control", "sap/m/FeedInput", ""]
58-
- ["UI5Control", "sap/ui/richtexteditor/RichTextEditor", ""]
44+
- ["UI5InputControl", "UI5InputControl", "Instance"]
45+
- ["UI5InputControl", "sap/m/InputBase", ""]
46+
- ["UI5InputControl", "sap/m/Input", ""]
47+
- ["UI5InputControl", "sap/ui/webc/main/Input", ""]
48+
- ["UI5InputControl", "sap/ui/commons/TextField", ""]
49+
- ["UI5InputControl", "sap/ui/commons/PasswordField", ""]
50+
- ["UI5InputControl", "sap/ui/commons/ValueHelpField", ""]
51+
- ["UI5InputControl", "sap/ui/commons/SearchField", ""]
52+
- ["UI5InputControl", "sap/ui/commons/ComboBox", ""]
53+
- ["UI5InputControl", "sap/ui/commons/TextArea", ""]
54+
- ["UI5InputControl", "sap/ui/webc/main/TextArea", ""]
55+
- ["UI5InputControl", "sap/m/ComboBoxTextField", ""]
56+
- ["UI5InputControl", "sap/m/MaskEnabler", ""]
57+
- ["UI5InputControl", "sap/m/MaskInput", ""]
58+
- ["UI5InputControl", "sap/m/TextArea", ""]
59+
- ["UI5InputControl", "sap/m/ComboBoxBase", ""]
60+
- ["UI5InputControl", "sap/m/MultiInput", ""]
61+
- ["UI5InputControl", "sap/ui/webc/main/MultiInput", ""]
62+
- ["UI5InputControl", "sap/m/SearchField", ""]
63+
- ["UI5InputControl", "sap/m/FeedInput", ""]
64+
- ["UI5InputControl", "sap/ui/richtexteditor/RichTextEditor", ""]
5965
- ["UI5CodeEditor", "UI5CodeEditor", "Instance"]
6066
- ["UI5CodeEditor", "sap/ui/codeeditor/CodeEditor", ""]
6167
# Classes that provide Path injection APIs
@@ -69,8 +75,8 @@ extensions:
6975
pack: codeql/javascript-all
7076
extensible: "sourceModel"
7177
data:
72-
- ["UI5Control", "Member[value]", "remote"]
73-
- ["UI5Control", "Member[getValue].ReturnValue", "remote"]
78+
- ["UI5InputControl", "Member[value]", "remote"]
79+
- ["UI5InputControl", "Member[getValue].ReturnValue", "remote"]
7480
- ["UI5CodeEditor", "Member[value]", "remote"]
7581
- ["UI5CodeEditor", "Member[getCurrentValue].ReturnValue", "remote"]
7682
- ["global", "Member[jQuery].Member[sap].Member[syncHead,syncGet,syncGetText,syncPost,syncPostText].ReturnValue", "remote"]
@@ -83,9 +89,9 @@ extensions:
8389
data:
8490
# html-injection
8591
- ["global", "Member[jQuery].Member[sap].Member[globalEval].Argument[0]", "ui5-html-injection"]
86-
- ["HTMLControl", "Argument[0].Member[content]", "ui5-html-injection"]
87-
- ["HTMLControl", "Member[content]", "ui5-html-injection"]
88-
- ["HTMLControl", "Member[setContent].Argument[0]", "ui5-html-injection"]
92+
- ["UI5HTMLControl", "Argument[0].Member[content]", "ui5-html-injection"]
93+
- ["UI5HTMLControl", "Member[content]", "ui5-html-injection"]
94+
- ["UI5HTMLControl", "Member[setContent].Argument[0]", "ui5-html-injection"]
8995
- ["Patcher", "Member[unsafeHtml].Argument[0..]", "ui5-html-injection"]
9096
- ["RenderManager", "Member[write,unsafeHtml].Argument[0..]", "ui5-html-injection"]
9197
- ["RenderManager", "Member[writeAttribute,addStyle].Argument[0..1]", "ui5-html-injection"]
@@ -131,3 +137,5 @@ extensions:
131137
- ["sap/base/security/encodeURL", "", "Argument[0]", "ReturnValue", "taint"]
132138
- ["sap/base/security/encodeURLParameters", "", "Argument[0]", "ReturnValue", "taint"]
133139
- ["sap/base/security/encodeXML", "", "Argument[0]", "ReturnValue", "taint"]
140+
- ["UI5HTMLControl", "", "Argument[0].Member[content]", "ReturnValue.Member[content]", "taint"]
141+
- ["UI5HTMLControl", "Instance.Member[getContent]", "Argument[this].Member[content]", "ReturnValue", "taint"]

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

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,54 @@
11
import javascript
22
import advanced_security.javascript.frameworks.ui5.UI5
33
import advanced_security.javascript.frameworks.ui5.UI5View
4-
private import semmle.javascript.frameworks.data.internal.ApiGraphModelsExtensions as ApiGraphModelsExtensions
4+
import semmle.javascript.security.dataflow.XssThroughDomCustomizations
5+
private import semmle.javascript.frameworks.data.internal.ApiGraphModelsExtensions
56

6-
private class DataFromRemoteControlReference extends RemoteFlowSource, MethodCallNode {
7+
private class DataFromRemoteControlReference extends RemoteFlowSource {
78
DataFromRemoteControlReference() {
89
exists(UI5Control sourceControl, string typeAlias, ControlReference controlReference |
9-
ApiGraphModelsExtensions::typeModel(typeAlias, sourceControl.getImportPath(), _) and
10-
ApiGraphModelsExtensions::sourceModel(typeAlias, _, "remote", _) and
10+
typeModel(typeAlias, sourceControl.getImportPath(), _) and
11+
sourceModel(typeAlias, _, "remote", _) and
1112
sourceControl.getAReference() = controlReference and
12-
controlReference.flowsTo(this.getReceiver()) and
13-
this.getMethodName() = "getValue"
13+
(
14+
this = controlReference.getAMemberCall("getValue") or
15+
this = controlReference.getAPropertyRead("value")
16+
)
1417
)
1518
}
1619

1720
override string getSourceType() { result = "Data from a remote control" }
1821
}
1922

23+
private class InputControlInstantiation extends ElementInstantiation {
24+
InputControlInstantiation() { typeModel("UI5InputControl", this.getImportPath(), _) }
25+
}
26+
27+
private module TrackPlaceAtCallConfigFlow = TaintTracking::Global<TrackPlaceAtCallConfig>;
28+
29+
class DataFromInstantiatedAndPlacedAtControl extends RemoteFlowSource, XssThroughDom::Source {
30+
InputControlInstantiation controlInstantiation;
31+
ControlPlaceAtCall placeAtCall;
32+
33+
DataFromInstantiatedAndPlacedAtControl() {
34+
exists(string typeAlias, ControlReference controlReference |
35+
/* Double check that the type derives a remote flow source. */
36+
typeModel(typeAlias, controlInstantiation.getImportPath(), _) and
37+
sourceModel(typeAlias, _, "remote", _) and
38+
controlInstantiation.getId() = controlReference.getId() and
39+
(
40+
this = controlReference.getAMemberCall("getValue") or
41+
this = controlReference.getAPropertyRead("value")
42+
)
43+
) and
44+
TrackPlaceAtCallConfigFlow::flow(controlInstantiation, placeAtCall)
45+
}
46+
47+
override string getSourceType() {
48+
result = "Data from an instantiated control placed in a DOM tree"
49+
}
50+
}
51+
2052
class LocalModelContentBoundBidirectionallyToSourceControl extends RemoteFlowSource {
2153
UI5BindingPath bindingPath;
2254
UI5Control controlDeclaration;
@@ -60,31 +92,37 @@ class ODataServiceModel extends UI5ExternalModel {
6092
ODataServiceModel() {
6193
exists(MethodCallNode setModelCall, CustomController controller |
6294
/*
63-
* 1. This flows from a DF node corresponding to the parent component's model to the `this.setModel` call
64-
* i.e. Aims to capture something like `this.getOwnerComponent().getModel("someModelName")` as in
65-
* `this.getView().setModel(this.getOwnerComponent().getModel("someModelName"))`
95+
* 1. This flows from a DF node corresponding to the parent component's model
96+
* to the `this.setModel` call. e.g.
97+
*
98+
* `this.getOwnerComponent().getModel("someModelName")` as in
99+
* `this.getView().setModel(this.getOwnerComponent().getModel("someModelName"))`.
66100
*/
67101

68102
modelName = this.getArgument(0).getALocalSource().asExpr().(StringLiteral).getValue() and
69103
this.getCalleeName() = "getModel" and
70104
controller.getOwnerComponentRef().flowsTo(this.(MethodCallNode).getReceiver()) and
71105
this.flowsTo(setModelCall.getArgument(0)) and
72-
setModelCall.getMethodName() = "setModel" and
73-
setModelCall.getReceiver() = controller.getAViewReference() and
74-
/* 2. The component's manifest.json declares the DataSource as being of OData type */
106+
setModelCall = controller.getAViewReference().getAMemberCall("setModel") and
107+
/*
108+
* 2. The component's `manifest.json` declares the DataSource as being of OData type.
109+
*/
110+
75111
controller.getOwnerComponent().getExternalModelDef(modelName).getDataSource() instanceof
76112
ODataDataSourceManifest
77113
)
78114
or
79115
/*
80-
* A constructor call to sap.ui.model.odata.v2.ODataModel.
116+
* A constructor call to sap.ui.model.odata.v2.ODataModel or sap.ui.model.odata.v4.ODataModel.
81117
*/
82118

83119
this instanceof NewNode and
84120
(
85121
exists(RequiredObject oDataModel |
86122
oDataModel.asSourceNode().flowsTo(this.getCalleeNode()) and
87-
oDataModel.getDependency() = "sap/ui/model/odata/v2/ODataModel"
123+
oDataModel.getDependency() in [
124+
"sap/ui/model/odata/v2/ODataModel", "sap/ui/model/odata/v4/ODataModel"
125+
]
88126
)
89127
or
90128
this.getCalleeName() = "ODataModel"
@@ -99,21 +137,17 @@ private class RouteParameterAccess extends RemoteFlowSource instanceof PropRead
99137
override string getSourceType() { result = "RouteParameterAccess" }
100138

101139
RouteParameterAccess() {
102-
exists(
103-
ControllerHandler handler, RouteManifest routeManifest, ParameterNode handlerParameter,
104-
MethodCallNode getParameterCall
105-
|
140+
exists(ControllerHandler handler, RouteManifest routeManifest, MethodCallNode getParameterCall |
106141
handler.isAttachedToRoute(routeManifest.getName()) and
107142
this.asExpr().getEnclosingFunction() = handler.getFunction() and
108-
handlerParameter = handler.getParameter(0) and
109-
getParameterCall.getMethodName() = "getParameter" and
110-
getParameterCall.getReceiver().getALocalSource() = handlerParameter and
143+
getParameterCall = handler.getParameter(0).getAMemberCall("getParameter") and
111144
(
112-
routeManifest.matchesPathString(this.getPropertyName()) and
113-
this.getBase().getALocalSource() = getParameterCall
145+
exists(string path |
146+
this = getParameterCall.getAPropertyRead(path) and
147+
routeManifest.matchesPathString(path)
148+
)
114149
or
115-
/* TODO: Why does `routeManifest.matchesPathString` not work for propertyName?? */
116-
this.getBase().(PropRead).getBase().getALocalSource() = getParameterCall
150+
this = getParameterCall.getAPropertyRead().getAPropertyRead()
117151
)
118152
)
119153
}
@@ -123,10 +157,8 @@ private class DisplayEventHandlerParameterAccess extends RemoteFlowSource instan
123157
override string getSourceType() { result = "DisplayEventHandlerParameterAccess" }
124158

125159
DisplayEventHandlerParameterAccess() {
126-
exists(DisplayEventHandler handler, MethodCallNode getParameterCall |
127-
getParameterCall.getMethodName() = "getParameter" and
128-
this.getBase().getALocalSource() = getParameterCall and
129-
handler.getParameter(0) = getParameterCall.getReceiver().getALocalSource()
160+
exists(DisplayEventHandler handler |
161+
this = handler.getParameter(0).getAMemberCall("getParameter").getAPropertyRead()
130162
)
131163
}
132164
}

0 commit comments

Comments
 (0)