Skip to content

Commit f375d9a

Browse files
Merge branch 'main' into dependabot/npm_and_yarn/javascript/frameworks/ui5/test/queries/UI5Xss/xss-book-example/npm_and_yarn-775d964dd3
2 parents 189abc2 + fa204ec commit f375d9a

File tree

117 files changed

+2421
-2341
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

117 files changed

+2421
-2341
lines changed

.github/workflows/javascript.sarif.expected

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
library: true
33
name: advanced-security/javascript-sap-cap-models
4-
version: 2.0.0
4+
version: 2.2.0
55
extensionTargets:
66
codeql/javascript-all: "^2.4.0"

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,16 +166,14 @@ class CqlClauseParserCallWithStringConcat instanceof CqlClauseParserCall {
166166
* instead (notice the lack of parentheses around the template literal), then the `where` call
167167
* becomes a parser call of the template literal following it and thus acts as a sanitizer.
168168
*/
169-
class CqlInjectionConfiguration extends TaintTracking::Configuration {
170-
CqlInjectionConfiguration() { this = "CQL injection from untrusted data" }
169+
module CqlInjectionConfiguration implements DataFlow::ConfigSig {
170+
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
171171

172-
override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
172+
predicate isSink(DataFlow::Node node) { node instanceof CqlInjectionSink }
173173

174-
override predicate isSink(DataFlow::Node node) { node instanceof CqlInjectionSink }
174+
predicate isBarrier(DataFlow::Node node) { node instanceof SqlInjection::Sanitizer }
175175

176-
override predicate isSanitizer(DataFlow::Node node) { node instanceof SqlInjection::Sanitizer }
177-
178-
override predicate isAdditionalTaintStep(DataFlow::Node start, DataFlow::Node end) {
176+
predicate isAdditionalFlowStep(DataFlow::Node start, DataFlow::Node end) {
179177
/*
180178
* 1. Given a call to a CQL parser, jump from the argument to the parser call itself.
181179
*/

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

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ class CdsLogger extends MethodCallNode {
2222
string getName() { result = name }
2323
}
2424

25+
/**
26+
* A template literal that is not interpolated. It is basically a string literal
27+
* defined in backticks (\`\`).
28+
*/
2529
class ConstantOnlyTemplateLiteral extends TemplateLiteral {
2630
ConstantOnlyTemplateLiteral() {
2731
forall(Expr e | e = this.getAnElement() | e instanceof TemplateElement)
@@ -43,19 +47,36 @@ class CdsLogSink extends DataFlow::Node {
4347
}
4448
}
4549

46-
class CAPLogInjectionConfiguration extends LogInjectionConfiguration {
47-
override predicate isSource(DataFlow::Node start) {
48-
super.isSource(start)
50+
module CAPLogInjectionConfiguration implements DataFlow::ConfigSig {
51+
predicate isSource(DataFlow::Node start) {
52+
LogInjectionConfig::isSource(start)
4953
or
5054
start instanceof RemoteFlowSource
5155
}
5256

53-
override predicate isBarrier(DataFlow::Node node) {
57+
predicate isBarrier(DataFlow::Node node) {
58+
/*
59+
* This predicate includes cases such as:
60+
* 1. A CDS entity element lacking a type annotation.
61+
* - Possibly because it relies on a common aspect.
62+
* 2. A CDS entity element annotated with a non-string type listed above.
63+
*
64+
* Therefore, the data held by the handler parameter data (e.g. `req.data.X`)
65+
* has to be EXPLICITLY annotated as `String` or `LargeString` to be excluded
66+
* from the next condition.
67+
*/
68+
5469
exists(HandlerParameterData handlerParameterData |
5570
node = handlerParameterData and
56-
not handlerParameterData.getType() = ["cds.String", "cds.LargeString"]
71+
/* Note the use of `.. != ..` instead of `not .. = ..` below. */
72+
exists(string handlerParameterDataType |
73+
handlerParameterDataType = handlerParameterData.getType()
74+
|
75+
handlerParameterDataType != "cds.String" and
76+
handlerParameterDataType != "cds.LargeString"
77+
)
5778
)
5879
}
5980

60-
override predicate isSink(DataFlow::Node end) { end instanceof CdsLogSink }
81+
predicate isSink(DataFlow::Node end) { end instanceof CdsLogSink }
6182
}

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

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,21 @@
77
import javascript
88
import advanced_security.javascript.frameworks.cap.CDSUtils
99

10-
abstract class UtilsAccessedPathSink extends DataFlow::Node { }
10+
abstract class UtilsSink extends DataFlow::Node {
11+
abstract string sinkType();
12+
}
1113

12-
abstract class UtilsControlledDataSink extends DataFlow::Node { }
14+
abstract class UtilsAccessedPathSink extends UtilsSink {
15+
override string sinkType() { result = "unrestricted file read" }
16+
}
1317

14-
abstract class UtilsControlledPathSink extends DataFlow::Node { }
18+
abstract class UtilsControlledDataSink extends UtilsSink {
19+
override string sinkType() { result = "tainted data being written to a file" }
20+
}
1521

16-
abstract class UtilsExtraFlow extends DataFlow::Node { }
22+
abstract class UtilsControlledPathSink extends UtilsSink {
23+
override string sinkType() { result = "unrestricted file operations" }
24+
}
1725

1826
/**
1927
* This represents the data in calls as follows:
@@ -67,21 +75,3 @@ class ControlledInputPath extends UtilsControlledPathSink {
6775
exists(DirectoryReaders dr | dr.getPath() = this)
6876
}
6977
}
70-
71-
/**
72-
* This represents calls where the taint flows through the call. e.g.
73-
* ```javascript
74-
* let dir = isdir ('app')
75-
* ```
76-
*/
77-
class AdditionalFlowStep extends UtilsExtraFlow {
78-
AdditionalFlowStep() {
79-
exists(PathConverters pc | pc.getPath() = this)
80-
or
81-
exists(PathPredicates pr | pr.getPath() = this)
82-
}
83-
84-
DataFlow::CallNode getOutgoingNode() { result = this }
85-
86-
DataFlow::Node getIngoingNode() { result = this.(DataFlow::CallNode).getAnArgument() }
87-
}

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

Lines changed: 122 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,27 @@ class ServiceInstanceFromConstructor extends ServiceInstance {
177177
}
178178

179179
/**
180-
* A read to `this` variable which represents the service whose definition encloses this variable access.
180+
* A read to `this` variable which represents the service whose definition encloses
181+
* this variable access.
182+
* e.g.1. Given this code:
183+
* ``` javascript
184+
* const cds = require("@sap/cds");
185+
* module.exports = class SomeService extends cds.ApplicationService {
186+
* init() {
187+
* this.on("SomeEvent", (req) => { ... } )
188+
* }
189+
* }
190+
* ```
191+
* This class captures the access to the `this` variable as in `this.on(...)`.
192+
*
193+
* e.g.2. Given this code:
194+
* ``` javascript
195+
* const cds = require('@sap/cds');
196+
* module.exports = cds.service.impl (function() {
197+
* this.on("SomeEvent", (req) => { ... })
198+
* })
199+
* ```
200+
* This class captures the access to the `this` variable as in `this.on(...)`.
181201
*/
182202
class ServiceInstanceFromThisNode extends ServiceInstance, ThisNode {
183203
UserDefinedApplicationService userDefinedApplicationService;
@@ -294,12 +314,56 @@ class DbServiceInstanceFromCdsConnectTo extends ServiceInstanceFromCdsConnectTo,
294314
override UserDefinedApplicationService getDefinition() { none() }
295315
}
296316

317+
/**
318+
* The 0-th parameter of an exported closure that represents the service being implemented. e.g.
319+
* ``` javascript
320+
* const cds = require('@sap/cds')
321+
* module.exports = (srv) => {
322+
* srv.on("SomeEvent1", (req) => { ... })
323+
* }
324+
* ```
325+
* This class captures the `srv` parameter of the exported arrow function. Also see
326+
* `ServiceInstanceFromImplMethodCallClosureParameter` which is similar to this.
327+
*/
328+
class ServiceInstanceFromExportedClosureParameter extends ServiceInstance, ParameterNode {
329+
ExportedClosureApplicationServiceDefinition exportedClosure;
330+
331+
ServiceInstanceFromExportedClosureParameter() { this = exportedClosure.getParameter(0) }
332+
333+
override UserDefinedApplicationService getDefinition() { result = exportedClosure }
334+
}
335+
336+
/**
337+
* The 0-th parameter of a callback (usually an arrow function) passed to `cds.service.impl` that
338+
* represents the service being implemented. e.g.
339+
* ``` javascript
340+
* const cds = require('@sap/cds')
341+
* module.exports = cds.service.impl((srv) => {
342+
* srv.on("SomeEvent1", (req) => { ... })
343+
* })
344+
* ```
345+
* This class captures the `srv` parameter of the exported arrow function. Also see
346+
* `ServiceInstanceFromExportedClosureParameter` which is similar to this.
347+
*/
348+
class ServiceInstanceFromImplMethodCallClosureParameter extends ServiceInstance, ParameterNode {
349+
ImplMethodCallApplicationServiceDefinition implMethodCallApplicationServiceDefinition;
350+
351+
ServiceInstanceFromImplMethodCallClosureParameter() {
352+
this = implMethodCallApplicationServiceDefinition.getInitFunction().getParameter(0)
353+
}
354+
355+
override UserDefinedApplicationService getDefinition() {
356+
result = implMethodCallApplicationServiceDefinition
357+
}
358+
}
359+
297360
/**
298361
* A call to `before`, `on`, or `after` on an `cds.ApplicationService`.
299362
* It registers an handler to be executed when an event is fired,
300363
* to do something with the incoming request or event as its parameter.
301364
*/
302365
class HandlerRegistration extends MethodCallNode {
366+
/** The instance of the service a handler is registered on. */
303367
ServiceInstance srv;
304368
string methodName;
305369

@@ -308,6 +372,9 @@ class HandlerRegistration extends MethodCallNode {
308372
methodName = ["before", "on", "after"]
309373
}
310374

375+
/**
376+
* Gets the instance of the service a handler is registered on.
377+
*/
311378
ServiceInstance getService() { result = srv }
312379

313380
/**
@@ -347,7 +414,7 @@ class HandlerRegistration extends MethodCallNode {
347414

348415
/**
349416
* The first parameter of a handler, representing the request object received either directly
350-
* from a user, or from another service that may be internal (defined in the same application)
417+
* from a user, or from another service that may be internal (defined in the same application)
351418
* or external (defined in another application, or even served from a different server).
352419
* e.g.
353420
* ``` javascript
@@ -356,14 +423,15 @@ class HandlerRegistration extends MethodCallNode {
356423
* this.before("SomeEvent", "SomeEntity", (req, next) => { ... });
357424
* this.after("SomeEvent", "SomeEntity", (req, next) => { ... });
358425
* }
359-
* ```
360-
* All parameters named `req` above are captured. Also see `HandlerParameterOfExposedService`
426+
* ```
427+
* All parameters named `req` above are captured. Also see
428+
* `RemoteflowSources::HandlerParameterOfExposedService`
361429
* for a subset of this class that is only about handlers exposed to some protocol.
362430
*/
363431
class HandlerParameter extends ParameterNode {
364432
Handler handler;
365433

366-
HandlerParameter() { this = handler.getParameter(0) }
434+
HandlerParameter() { this = isHandlerParameter(handler) }
367435

368436
Handler getHandler() { result = handler }
369437
}
@@ -506,7 +574,7 @@ abstract class UserDefinedApplicationService extends UserDefinedService {
506574
/**
507575
* Holds if this service supports access from the outside through any kind of protocol.
508576
*/
509-
predicate isExposed() { not this.isInternal() }
577+
predicate isExposed() { exists(this.getCdsDeclaration()) and not this.isInternal() }
510578

511579
/**
512580
* Holds if this service does not support access from the outside through any kind of protocol, thus being internal only.
@@ -539,9 +607,21 @@ class ES6ApplicationServiceDefinition extends ClassNode, UserDefinedApplicationS
539607

540608
/**
541609
* Subclassing `cds.ApplicationService` via a call to `cds.service.impl`.
542-
* ```js
610+
* e.g.1. Given this code:
611+
* ``` javascript
612+
* const cds = require('@sap/cds')
613+
* module.exports = cds.service.impl (function() {
614+
* this.on("SomeEvent1", (req) => { ... })
615+
* })
616+
* ```
617+
* This class captures the call `cds.service.impl (function() { ... })`.
618+
*
619+
* e.g.2. Given this code:
620+
* ``` javascript
543621
* const cds = require('@sap/cds')
544-
* module.exports = cds.service.impl (function() { ... })
622+
* module.exports = cds.service.impl ((srv) => {
623+
* srv.on("SomeEvent1", (req) => { ... })
624+
* })
545625
* ```
546626
*/
547627
class ImplMethodCallApplicationServiceDefinition extends MethodCallNode,
@@ -554,6 +634,40 @@ class ImplMethodCallApplicationServiceDefinition extends MethodCallNode,
554634
override FunctionNode getInitFunction() { result = this.getArgument(0) }
555635
}
556636

637+
/**
638+
* A user-defined application service that comes in a form of an exported
639+
* closure. e.g. Given the below code,
640+
* ``` javascript
641+
* const cds = require("@sap/cds");
642+
*
643+
* module.exports = (srv) => {
644+
* srv.before("SomeEvent1", "SomeEntity", (req, res) => { ... })
645+
* srv.on("SomeEvent2", (req) => { ... } )
646+
* srv.after("SomeEvent3", (req) => { ... } )
647+
* }
648+
* ```
649+
* This class captures the entire `(srv) => { ... }` function that is
650+
* exported.
651+
*/
652+
class ExportedClosureApplicationServiceDefinition extends FunctionNode,
653+
UserDefinedApplicationService
654+
{
655+
ExportedClosureApplicationServiceDefinition() {
656+
/*
657+
* ==================== HACK ====================
658+
* See issue #221.
659+
*/
660+
661+
exists(PropWrite moduleExports |
662+
moduleExports.getBase().asExpr().(VarAccess).getName() = "module" and
663+
moduleExports.getPropertyName() = "exports" and
664+
this = moduleExports.getRhs()
665+
)
666+
}
667+
668+
override FunctionNode getInitFunction() { result = this }
669+
}
670+
557671
abstract class InterServiceCommunicationMethodCall extends MethodCallNode {
558672
string name;
559673
ServiceInstance recipient;

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ class HandlerParameterOfExposedService extends HandlerParameter {
3030
* is known.
3131
*/
3232

33-
not exists(this.getHandler().getHandlerRegistration().getService().getDefinition())
33+
not exists(
34+
this.getHandler().getHandlerRegistration().getService().getDefinition().getCdsDeclaration()
35+
)
3436
}
3537
}
3638

@@ -54,7 +56,9 @@ class UserProvidedPropertyReadOfHandlerParameterOfExposedService extends RemoteF
5456

5557
UserProvidedPropertyReadOfHandlerParameterOfExposedService() {
5658
/* 1. `req.(data|params|headers|id)` */
57-
this = handlerParameterOfExposedService.getAPropertyRead(["data", "params", "headers", "id"])
59+
this =
60+
handlerParameterOfExposedService
61+
.getAPropertyRead(["data", "params", "headers", "id", "_queryOptions"])
5862
or
5963
/* 2. APIs stemming from `req.http.req`: Defined by Express.js */
6064
exists(PropRead reqHttpReq |

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,12 @@ private SourceNode cdsApplicationServiceInstantiation(TypeTracker t) {
3939
SourceNode cdsApplicationServiceInstantiation() {
4040
result = cdsApplicationServiceInstantiation(TypeTracker::end())
4141
}
42+
43+
private SourceNode isHandlerParameter(TypeTracker t, Handler handler) {
44+
result = handler.getParameter(0) or
45+
exists(TypeTracker t2 | result = isHandlerParameter(t, handler).track(t2, t))
46+
}
47+
48+
SourceNode isHandlerParameter(Handler handler) {
49+
result = isHandlerParameter(TypeTracker::end(), handler)
50+
}

0 commit comments

Comments
 (0)