Skip to content

Commit f4d9332

Browse files
authored
Merge branch 'advanced-security:main' into data-douser/markdownlint
2 parents 5347cc1 + df92915 commit f4d9332

File tree

13 files changed

+166
-10
lines changed

13 files changed

+166
-10
lines changed

.github/workflows/javascript.sarif.expected

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

javascript/frameworks/cap/lib/advanced_security/javascript/frameworks/cap/CAPLogInjectionQuery.qll

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class ConstantOnlyTemplateLiteral extends TemplateLiteral {
2929
}
3030

3131
/**
32-
* Arguments of calls to `cds.log.{trace, debug, info, log, warn, error}`
32+
* Arguments of calls to `cds.log.{trace, debug, info, log, warn, error}`.
3333
*/
3434
class CdsLogSink extends DataFlow::Node {
3535
CdsLogSink() {
@@ -49,5 +49,12 @@ class CAPLogInjectionConfiguration extends LogInjectionConfiguration {
4949
start instanceof RemoteFlowSource
5050
}
5151

52+
override predicate isBarrier(DataFlow::Node node) {
53+
exists(HandlerParameterData handlerParameterData |
54+
node = handlerParameterData and
55+
not handlerParameterData.getType() = ["cds.String", "cds.LargeString"]
56+
)
57+
}
58+
5259
override predicate isSink(DataFlow::Node end) { end instanceof CdsLogSink }
5360
}

javascript/frameworks/cap/lib/advanced_security/javascript/frameworks/cap/CDL.qll

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ class CdlService extends CdlElement {
181181

182182
CdlEntity getAnEntity() { result = this.getEntity(_) }
183183

184+
CdlActionOrFunction getAnActionOrFunction() {
185+
result = this.getAnEvent() or result = this.getAnAction()
186+
}
187+
184188
CdlEvent getEvent(string eventName) {
185189
result.getName() = eventName and this.getName() = result.getName().splitAt(".", 0)
186190
}
@@ -232,13 +236,27 @@ class CdlEntity extends CdlElement {
232236
}
233237
}
234238

235-
class CdlEvent extends CdlElement {
239+
abstract class CdlActionOrFunction extends CdlElement {
240+
/**
241+
* Gets a parameter definition of this action with a given name.
242+
*/
243+
CdlAttribute getParameter(string paramName) {
244+
result = this.getPropValue("params").getPropValue(paramName)
245+
}
246+
247+
/**
248+
* Gets a parameter definition of this action.
249+
*/
250+
CdlAttribute getAParameter() { result = this.getParameter(_) }
251+
}
252+
253+
class CdlEvent extends CdlActionOrFunction {
236254
CdlEvent() { kind = CdlEventKind(this.getPropStringValue("kind")) }
237255

238256
string getBasename() { result = name.splitAt(".", count(name.indexOf("."))) }
239257
}
240258

241-
class CdlAction extends CdlElement {
259+
class CdlAction extends CdlActionOrFunction {
242260
CdlAction() { kind = CdlActionKind(this.getPropStringValue("kind")) }
243261

244262
predicate belongsToServiceWithNoAuthn() {
@@ -260,7 +278,10 @@ class CdlAttribute extends CdlObject {
260278
string name;
261279

262280
CdlAttribute() {
263-
exists(CdlElement entity | this = entity.getPropValue("elements").getPropValue(name))
281+
exists(CdlElement entity | this = entity.getPropValue("elements").getPropValue(name)) or
282+
exists(CdlActionOrFunction actionOrFunction |
283+
this = actionOrFunction.getPropValue("params").getPropValue(name)
284+
)
264285
}
265286

266287
override string getObjectLocationName() { result = getName() }

javascript/frameworks/cap/lib/advanced_security/javascript/frameworks/cap/CDS.qll

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import advanced_security.javascript.frameworks.cap.TypeTrackers
44
import advanced_security.javascript.frameworks.cap.PackageJson
55
import advanced_security.javascript.frameworks.cap.CDL
66
import advanced_security.javascript.frameworks.cap.CQL
7+
import advanced_security.javascript.frameworks.cap.RemoteFlowSources
78

89
/**
910
* ```js
@@ -701,3 +702,48 @@ class EntityReferenceFromCqlClause extends EntityReference, ExprNode {
701702

702703
override CdlEntity getCqlDefinition() { result = cql.getAccessingEntityDefinition() }
703704
}
705+
706+
/**
707+
* The `"data"` property of the handler's parameter that represents the request or message passed to this handler.
708+
* This property carries the user-provided payload provided to the CAP application. e.g.
709+
* ``` javascript
710+
* srv.on("send", async (msg) => {
711+
* const { payload } = msg.data;
712+
* })
713+
* ```
714+
* The `payload` carries the data that is sent to this application on the action or event named `send`.
715+
*/
716+
class HandlerParameterData instanceof PropRead {
717+
HandlerParameter handlerParameter;
718+
string dataName;
719+
720+
HandlerParameterData() {
721+
this = handlerParameter.getAPropertyRead("data").getAPropertyRead(dataName)
722+
}
723+
724+
/**
725+
* Gets the type of this handler parameter data.
726+
*/
727+
string getType() {
728+
/*
729+
* The result string is the type of this parameter if:
730+
* - There is an actionName which is this HandlerRegistration's action/function name, and
731+
* - The actionName in the CDS declaration has params with the same name of this parameter, and
732+
* - The result is the name of the type of the parameter.
733+
*/
734+
735+
exists(
736+
UserDefinedApplicationService userDefinedApplicationService,
737+
CdlActionOrFunction cdlActionOrFunction, HandlerRegistration handlerRegistration
738+
|
739+
handlerRegistration = userDefinedApplicationService.getAHandlerRegistration() and
740+
handlerRegistration = handlerParameter.getHandlerRegistration() and
741+
handlerRegistration.getAnEventName() = cdlActionOrFunction.getUnqualifiedName() and
742+
cdlActionOrFunction =
743+
userDefinedApplicationService.getCdsDeclaration().getAnActionOrFunction() and
744+
result = cdlActionOrFunction.getParameter(dataName).getType()
745+
)
746+
}
747+
748+
string toString() { result = this.(PropRead).toString() }
749+
}

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ import advanced_security.javascript.frameworks.cap.CDS
1212
* All the parameters named `req` and `msg` are captured in the above example.
1313
*/
1414
class HandlerParameter extends ParameterNode, RemoteFlowSource {
15+
Handler handler;
16+
HandlerRegistration handlerRegistration;
17+
1518
HandlerParameter() {
16-
exists(
17-
Handler handler, HandlerRegistration handlerRegistration,
18-
UserDefinedApplicationService service
19-
|
19+
exists(UserDefinedApplicationService service |
2020
handler = handlerRegistration.getHandler() and
2121
this = handler.getParameter(0) and
2222
service.getAHandlerRegistration() = handlerRegistration and
@@ -27,13 +27,26 @@ class HandlerParameter extends ParameterNode, RemoteFlowSource {
2727
override string getSourceType() {
2828
result = "Parameter of an event handler belonging to an exposed service"
2929
}
30+
31+
/**
32+
* Gets the handler this is a parameter of.
33+
*/
34+
Handler getHandler() { result = handler }
35+
36+
/**
37+
* Gets the handler registration registering the handler it is a parameter of.
38+
*/
39+
HandlerRegistration getHandlerRegistration() { result = handlerRegistration }
3040
}
3141

3242
/**
3343
* A service may be described only in a CDS file, but event handlers may still be registered in a format such as:
3444
* ```javascript
3545
* module.exports = srv => {
36-
* srv.before('CREATE', 'Media', req => { //an entity name is used to describe which to register this handler to
46+
* srv.before('CREATE', 'Media', req => { // an entity name is used to describe which to register this handler to.
47+
* ...
48+
* });
49+
* }
3750
* ```
3851
* parameters named `req` are captured in the above example.
3952
*/
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Sanitized Log Injection
2+
3+
This application demonstrates how a potential injection vulnerability is not reported if the data type definied in the service description is not strings.
4+
5+
## It _is_ a false positive case
6+
7+
Service responds to a Received event and logs the data. However, the type of the message (Integer) does not allow for the injection to succeed.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace advanced_security.log_injection.sample_entities;
2+
3+
entity Entity1 {
4+
Attribute1 : String(100);
5+
Attribute2 : String(100)
6+
}
7+
8+
entity Entity2 {
9+
Attribute3 : String(100);
10+
Attribute4 : String(100)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
nodes
2+
edges
3+
#select
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
loginjection/LogInjection.ql
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "@advanced-security/log-injection",
3+
"version": "1.0.0",
4+
"dependencies": {
5+
"@cap-js/sqlite": "*",
6+
"@sap/cds": "^7.9.5",
7+
"@sap/cds-dk": "^8.6.1",
8+
"express": "^4.17.1"
9+
},
10+
"scripts": {
11+
"start": "cds-serve",
12+
"watch": "cds watch"
13+
},
14+
"cds": {
15+
"requires": {
16+
"service": {
17+
"impl": "srv/service.js"
18+
}
19+
}
20+
}
21+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const cds = require('@sap/cds');
2+
const app = require('express')();
3+
4+
cds.serve('all').in(app);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using { advanced_security.log_injection.sample_entities as db_schema } from '../db/schema';
2+
3+
service Service @(path: '/service') {
4+
/* Entity to send READ/GET about. */
5+
entity ServiceEntity as projection on db_schema.Entity2 excluding { Attribute4 }
6+
7+
/* API to talk to Service. */
8+
action send (
9+
messageToPass: Integer
10+
) returns String;
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const cds = require("@sap/cds");
2+
const LOG = cds.log("logger");
3+
4+
module.exports = cds.service.impl(function() {
5+
/* Log upon receiving an "send" event. */
6+
this.on("send", async (msg) => {
7+
const { messageToPass } = msg.data;
8+
/* A log injection sink. */
9+
LOG.info("Received: ", messageToPass); // messageToPass is Integer, not a log injection!
10+
});
11+
})

0 commit comments

Comments
 (0)