Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,27 @@ class ServiceInstanceFromConstructor extends ServiceInstance {
}

/**
* A read to `this` variable which represents the service whose definition encloses this variable access.
* A read to `this` variable which represents the service whose definition encloses
* this variable access.
* e.g.1. Given this code:
* ``` javascript
* const cds = require("@sap/cds");
* module.exports = class SomeService extends cds.ApplicationService {
* init() {
* this.on("SomeEvent", (req) => { ... } )
* }
* }
* ```
* This class captures the access to the `this` variable as in `this.on(...)`.
*
* e.g.2. Given this code:
* ``` javascript
* const cds = require('@sap/cds');
* module.exports = cds.service.impl (function() {
* this.on("SomeEvent", (req) => { ... })
* })
* ```
* This class captures the access to the `this` variable as in `this.on(...)`.
*/
class ServiceInstanceFromThisNode extends ServiceInstance, ThisNode {
UserDefinedApplicationService userDefinedApplicationService;
Expand Down Expand Up @@ -294,12 +314,56 @@ class DbServiceInstanceFromCdsConnectTo extends ServiceInstanceFromCdsConnectTo,
override UserDefinedApplicationService getDefinition() { none() }
}

/**
* The 0-th parameter of an exported closure that represents the service being implemented. e.g.
* ``` javascript
* const cds = require('@sap/cds')
* module.exports = (srv) => {
* srv.on("SomeEvent1", (req) => { ... })
* }
* ```
* This class captures the `srv` parameter of the exported arrow function. Also see
* `ServiceInstanceFromImplMethodCallClosureParameter` which is similar to this.
*/
class ServiceInstanceFromExportedClosureParameter extends ServiceInstance, ParameterNode {
ExportedClosureApplicationServiceDefinition exportedClosure;

ServiceInstanceFromExportedClosureParameter() { this = exportedClosure.getParameter(0) }

override UserDefinedApplicationService getDefinition() { result = exportedClosure }
}

/**
* The 0-th parameter of a callback (usually an arrow function) passed to `cds.service.impl` that
* represents the service being implemented. e.g.
* ``` javascript
* const cds = require('@sap/cds')
* module.exports = cds.service.impl((srv) => {
* srv.on("SomeEvent1", (req) => { ... })
* })
* ```
* This class captures the `srv` parameter of the exported arrow function. Also see
* `ServiceInstanceFromExportedClosureParameter` which is similar to this.
*/
class ServiceInstanceFromImplMethodCallClosureParameter extends ServiceInstance, ParameterNode {
ImplMethodCallApplicationServiceDefinition implMethodCallApplicationServiceDefinition;

ServiceInstanceFromImplMethodCallClosureParameter() {
this = implMethodCallApplicationServiceDefinition.getInitFunction().getParameter(0)
}

override UserDefinedApplicationService getDefinition() {
result = implMethodCallApplicationServiceDefinition
}
}

/**
* A call to `before`, `on`, or `after` on an `cds.ApplicationService`.
* It registers an handler to be executed when an event is fired,
* to do something with the incoming request or event as its parameter.
*/
class HandlerRegistration extends MethodCallNode {
/** The instance of the service a handler is registered on. */
ServiceInstance srv;
string methodName;

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

/**
* Gets the instance of the service a handler is registered on.
*/
ServiceInstance getService() { result = srv }

/**
Expand Down Expand Up @@ -347,7 +414,7 @@ class HandlerRegistration extends MethodCallNode {

/**
* The first parameter of a handler, representing the request object received either directly
* from a user, or from another service that may be internal (defined in the same application)
* from a user, or from another service that may be internal (defined in the same application)
* or external (defined in another application, or even served from a different server).
* e.g.
* ``` javascript
Expand All @@ -356,7 +423,7 @@ class HandlerRegistration extends MethodCallNode {
* this.before("SomeEvent", "SomeEntity", (req, next) => { ... });
* this.after("SomeEvent", "SomeEntity", (req, next) => { ... });
* }
* ```
* ```
* All parameters named `req` above are captured. Also see `HandlerParameterOfExposedService`
* for a subset of this class that is only about handlers exposed to some protocol.
*/
Expand Down Expand Up @@ -506,7 +573,7 @@ abstract class UserDefinedApplicationService extends UserDefinedService {
/**
* Holds if this service supports access from the outside through any kind of protocol.
*/
predicate isExposed() { not this.isInternal() }
predicate isExposed() { exists(this.getCdsDeclaration()) and not this.isInternal() }

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

/**
* Subclassing `cds.ApplicationService` via a call to `cds.service.impl`.
* ```js
* e.g.1. Given this code:
* ``` javascript
* const cds = require('@sap/cds')
* module.exports = cds.service.impl (function() {
* this.on("SomeEvent1", (req) => { ... })
* })
* ```
* This class captures the call `cds.service.impl (function() { ... })`.
*
* e.g.2. Given this code:
* ``` javascript
* const cds = require('@sap/cds')
* module.exports = cds.service.impl (function() { ... })
* module.exports = cds.service.impl ((srv) => {
* srv.on("SomeEvent1", (req) => { ... })
* })
* ```
*/
class ImplMethodCallApplicationServiceDefinition extends MethodCallNode,
Expand All @@ -554,6 +633,40 @@ class ImplMethodCallApplicationServiceDefinition extends MethodCallNode,
override FunctionNode getInitFunction() { result = this.getArgument(0) }
}

/**
* A user-defined application service that comes in a form of an exported
* closure. e.g. Given the below code,
* ``` javascript
* const cds = require("@sap/cds");
*
* module.exports = (srv) => {
* srv.before("SomeEvent1", "SomeEntity", (req, res) => { ... })
* srv.on("SomeEvent2", (req) => { ... } )
* srv.after("SomeEvent3", (req) => { ... } )
* }
* ```
* This class captures the entire `(srv) => { ... }` function that is
* exported.
*/
class ExportedClosureApplicationServiceDefinition extends FunctionNode,
UserDefinedApplicationService
{
ExportedClosureApplicationServiceDefinition() {
/*
* ==================== HACK ====================
* See issue #221.
*/

exists(PropWrite moduleExports |
moduleExports.getBase().asExpr().(VarAccess).getName() = "module" and
moduleExports.getPropertyName() = "exports" and
this = moduleExports.getRhs()
)
}

override FunctionNode getInitFunction() { result = this }
}

abstract class InterServiceCommunicationMethodCall extends MethodCallNode {
string name;
ServiceInstance recipient;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ class HandlerParameterOfExposedService extends HandlerParameter {
* is known.
*/

not exists(this.getHandler().getHandlerRegistration().getService().getDefinition())
not exists(
this.getHandler().getHandlerRegistration().getService().getDefinition().getCdsDeclaration()
)
}
}

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

UserProvidedPropertyReadOfHandlerParameterOfExposedService() {
/* 1. `req.(data|params|headers|id)` */
this = handlerParameterOfExposedService.getAPropertyRead(["data", "params", "headers", "id"])
this =
handlerParameterOfExposedService
.getAPropertyRead(["data", "params", "headers", "id", "_queryOptions"])
or
/* 2. APIs stemming from `req.http.req`: Defined by Express.js */
exists(PropRead reqHttpReq |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
| srv/service1.js:52:29:52:31 | req |
| srv/service1.js:58:29:58:31 | req |
| srv/service1.js:64:29:64:31 | req |
| srv/service1.js:70:30:70:32 | req |
| srv/service2.js:4:27:4:29 | msg |
| srv/service3.js:5:29:5:31 | req |
| srv/service3.js:11:29:11:31 | req |
Expand All @@ -19,3 +20,4 @@
| srv/service3.js:51:29:51:31 | req |
| srv/service3.js:57:29:57:31 | req |
| srv/service3.js:63:29:63:31 | req |
| srv/service3.js:69:30:69:32 | req |
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
| srv/service1.js:34:31:34:61 | req.htt ... eProp") |
| srv/service1.js:35:31:35:60 | req.htt ... eProp") |
| srv/service1.js:41:29:41:34 | req.id |
| srv/service1.js:47:29:47:45 | req._queryOptions |
| srv/service2.js:5:31:5:38 | msg.data |
| srv/service3.js:6:33:6:40 | req.data |
| srv/service3.js:12:33:12:42 | req.params |
Expand All @@ -29,3 +30,4 @@
| srv/service3.js:33:31:33:61 | req.htt ... eProp") |
| srv/service3.js:34:31:34:60 | req.htt ... eProp") |
| srv/service3.js:40:29:40:34 | req.id |
| srv/service3.js:46:29:46:45 | req._queryOptions |
Original file line number Diff line number Diff line change
Expand Up @@ -44,24 +44,30 @@ module.exports = class Service1 extends cds.ApplicationService {
});

this.on("send6", async (req) => {
const messageToPass = req.locale; // SAFE: Not a taint source, Exposed service
const messageToPass = req._queryOptions; // UNSAFE: Taint source, Exposed service
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
});

this.on("send7", async (req) => {
const messageToPass = req.tenant; // SAFE: Not a taint source, Exposed service
const messageToPass = req.locale; // SAFE: Not a taint source, Exposed service
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
});

this.on("send8", async (req) => {
const messageToPass = req.timestamp; // SAFE: Not a taint source, Exposed service
const messageToPass = req.tenant; // SAFE: Not a taint source, Exposed service
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
});

this.on("send9", async (req) => {
const messageToPass = req.timestamp; // SAFE: Not a taint source, Exposed service
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
});

this.on("send10", async (req) => {
const messageToPass = req.user; // SAFE: Not a taint source, Exposed service
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,30 @@ class Service3 extends cds.ApplicationService {
});

this.on("send6", async (req) => {
const messageToPass = req.locale; // SAFE: Not a taint source, Exposed service (fallback)
const messageToPass = req._queryOptions; // UNSAFE: Taint source, Exposed service
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
});

this.on("send7", async (req) => {
const messageToPass = req.tenant; // SAFE: Not a taint source, Exposed service (fallback)
const messageToPass = req.locale; // SAFE: Not a taint source, Exposed service (fallback)
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
});

this.on("send8", async (req) => {
const messageToPass = req.timestamp; // SAFE: Not a taint source, Exposed service (fallback)
const messageToPass = req.tenant; // SAFE: Not a taint source, Exposed service (fallback)
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
});

this.on("send9", async (req) => {
const messageToPass = req.timestamp; // SAFE: Not a taint source, Exposed service (fallback)
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
});

this.on("send10", async (req) => {
const messageToPass = req.user; // SAFE: Not a taint source, Exposed service (fallback)
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,11 @@ module.exports = class Service4 extends cds.ApplicationService {
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
});

this.on("send6", async (req) => {
const messageToPass = req._queryOptions; // UNSAFE: Taint source, Exposed service
const Service2 = await cds.connect.to("service-2");
Service2.send("send2", { messageToPass });
});
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const cds = require("@sap/cds");

class Service1 extends cds.ApplicationService { // ES6ApplicationServiceDefinition
init() {
return super.init()
}
}

module.exports = Service1
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const cds = require("@sap/cds");

module.exports = class LogService extends cds.Service { // UserDefinedService
init() {
return super.init()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const cds = require("@sap/cds");

module.exports = cds.service.impl(function () { // ImplMethodCallApplicationServiceDefinition
this.on("SomeEvent1", (req) => {
/* ... */
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const cds = require("@sap/cds");

module.exports = cds.service.impl((srv) => { // ImplMethodCallApplicationServiceDefinition
this.on("SomeEvent1", (req) => {
/* ... */
});
});

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const cds = require("@sap/cds");

module.exports = (srv) => { // ExportedClosureApplicationServiceDefinition
srv.on("SomeEvent1", (req) => {
/* ... */
});
};
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
| userdefinedservice.js:3:1:7:1 | class B ... )\\n }\\n} |
| userdefinedservice.js:10:18:14:1 | class L ... )\\n }\\n} |
| service1.js:3:1:7:1 | class S ... )\\n }\\n} |
| service2.js:3:18:7:1 | class L ... )\\n }\\n} |
| service3.js:3:18:7:2 | cds.ser ... });\\n}) |
| service4.js:3:18:7:2 | cds.ser ... });\\n}) |
| service5.js:3:18:7:1 | (srv) = ... });\\n} |

This file was deleted.