-
Notifications
You must be signed in to change notification settings - Fork 2
Add CDS Utils path injection query #224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
5253def
4472828
ff4834c
3f63cb3
d4743c2
234884e
f35d885
73a9c5b
1ddb097
4c3f564
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# CAP CDS Utils used with user-controlled sources | ||
|
||
If a path is constructed from user-provided input without sufficient sanitization, a malicious user may be able to manipulate the contents of the filesystem without proper authorization. | ||
|
||
Additionally if user-provided input is used to create file contents this can also result in a malicious user manipulating the filesystem in an unchecked way. | ||
|
||
## Recommendation | ||
|
||
CAP applications using CDS Utils should not use user-provided input without sanitization. | ||
|
||
## Examples | ||
|
||
This CAP service directly uses user-provided input to construct a path. | ||
|
||
``` javascript | ||
const cds = require("@sap/cds"); | ||
const { rm } = cds.utils | ||
|
||
module.exports = class Service1 extends cds.ApplicationService { | ||
|
||
init() { | ||
this.on("send1", async (req) => { | ||
let userinput = req.data | ||
await rm(userinput, 'db', 'data') // Path injection alert | ||
} | ||
} | ||
} | ||
``` | ||
|
||
This CAP service directly uses user-provided input to add content to a file. | ||
|
||
``` javascript | ||
const cds = require("@sap/cds"); | ||
const { rm } = cds.utils | ||
|
||
module.exports = class Service1 extends cds.ApplicationService { | ||
|
||
init() { | ||
this.on("send1", async (req) => { | ||
let userinput = req.data | ||
await write(userinput).to('db/data') // Path injection alert | ||
} | ||
} | ||
} | ||
|
||
``` | ||
|
||
## References | ||
knewbury01 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- OWASP 2021: [Injection](https://owasp.org/Top10/A03_2021-Injection/). | ||
- SAP CAP CDS Utils : [Documentation](https://cap.cloud.sap/docs/node.js/cds-utils). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/** | ||
* @name Use of user controlled input in CAP CDS file system utilies | ||
knewbury01 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @description Using unchecked user controlled values can allow an | ||
* attacker to affect paths constructed and accessed in | ||
* the filesystem. | ||
* @kind path-problem | ||
* @problem.severity warning | ||
* @security-severity 7.5 | ||
* @precision medium | ||
* @id js/cap-path-injection | ||
* @tags security | ||
* external/cwe/cwe-020 | ||
* external/cwe/cwe-022 | ||
*/ | ||
|
||
import javascript | ||
import advanced_security.javascript.frameworks.cap.CAPPathInjectionQuery | ||
import advanced_security.javascript.frameworks.cap.RemoteFlowSources | ||
|
||
module PathInjectionConfig implements DataFlow::ConfigSig { | ||
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource } | ||
|
||
predicate isSink(DataFlow::Node sink) { sink instanceof UtilsSink } | ||
|
||
predicate isAdditionalFlowStep(DataFlow::Node nodein, DataFlow::Node nodeout) { | ||
exists(CDSAdditionalFlowStep step | | ||
step.getIngoingNode() = nodein and | ||
step.getOutgoingNode() = nodeout | ||
) | ||
} | ||
} | ||
|
||
module PathInjectionConfigFlow = TaintTracking::Global<PathInjectionConfig>; | ||
|
||
import PathInjectionConfigFlow::PathGraph | ||
|
||
from PathInjectionConfigFlow::PathNode source, PathInjectionConfigFlow::PathNode sink | ||
where PathInjectionConfigFlow::flowPath(source, sink) | ||
select sink, source, sink, | ||
"This CDS utils usage relies on user-provided value and can result in " + | ||
sink.getNode().(UtilsSink).sinkType() + "." |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
edges | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:31:26:31:34 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:33:38:33:46 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:34:24:34:32 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:36:44:36:52 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:38:25:38:33 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:40:26:40:34 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:41:26:41:34 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:43:25:43:33 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:44:25:44:33 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:46:26:46:34 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:47:26:47:34 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:49:22:49:30 | userinput | provenance | | | ||
| pathinjection.js:8:19:8:38 | userinput | pathinjection.js:50:22:50:30 | userinput | provenance | | | ||
| pathinjection.js:8:31:8:38 | req.data | pathinjection.js:8:19:8:38 | userinput | provenance | | | ||
| pathinjection.js:9:19:9:44 | userinputtwo | pathinjection.js:37:25:37:36 | userinputtwo | provenance | | | ||
| pathinjection.js:9:34:9:44 | req.headers | pathinjection.js:9:19:9:44 | userinputtwo | provenance | | | ||
| pathinjection.js:10:19:10:45 | userinputthree | pathinjection.js:12:38:12:51 | userinputthree | provenance | | | ||
| pathinjection.js:10:19:10:45 | userinputthree | pathinjection.js:14:47:14:60 | userinputthree | provenance | | | ||
| pathinjection.js:10:19:10:45 | userinputthree | pathinjection.js:16:34:16:47 | userinputthree | provenance | | | ||
| pathinjection.js:10:19:10:45 | userinputthree | pathinjection.js:18:34:18:47 | userinputthree | provenance | | | ||
| pathinjection.js:10:19:10:45 | userinputthree | pathinjection.js:20:35:20:48 | userinputthree | provenance | | | ||
| pathinjection.js:10:36:10:45 | req.params | pathinjection.js:10:19:10:45 | userinputthree | provenance | | | ||
| pathinjection.js:12:19:12:52 | taint1 | pathinjection.js:22:36:22:41 | taint1 | provenance | | | ||
| pathinjection.js:12:28:12:52 | decodeU ... tthree) | pathinjection.js:12:19:12:52 | taint1 | provenance | | | ||
| pathinjection.js:12:38:12:51 | userinputthree | pathinjection.js:12:28:12:52 | decodeU ... tthree) | provenance | Config | | ||
| pathinjection.js:14:19:14:61 | taint2 | pathinjection.js:24:40:24:45 | taint2 | provenance | | | ||
| pathinjection.js:14:28:14:61 | decodeU ... tthree) | pathinjection.js:14:19:14:61 | taint2 | provenance | | | ||
| pathinjection.js:14:47:14:60 | userinputthree | pathinjection.js:14:28:14:61 | decodeU ... tthree) | provenance | Config | | ||
| pathinjection.js:16:19:16:48 | taint3 | pathinjection.js:26:34:26:39 | taint3 | provenance | | | ||
| pathinjection.js:16:28:16:48 | local(u ... tthree) | pathinjection.js:16:19:16:48 | taint3 | provenance | | | ||
| pathinjection.js:16:34:16:47 | userinputthree | pathinjection.js:16:28:16:48 | local(u ... tthree) | provenance | Config | | ||
| pathinjection.js:18:19:18:48 | taint4 | pathinjection.js:28:34:28:39 | taint4 | provenance | | | ||
| pathinjection.js:18:28:18:48 | isdir(u ... tthree) | pathinjection.js:18:19:18:48 | taint4 | provenance | | | ||
| pathinjection.js:18:34:18:47 | userinputthree | pathinjection.js:18:28:18:48 | isdir(u ... tthree) | provenance | Config | | ||
| pathinjection.js:20:19:20:49 | taint5 | pathinjection.js:30:40:30:45 | taint5 | provenance | | | ||
| pathinjection.js:20:28:20:49 | isfile( ... tthree) | pathinjection.js:20:19:20:49 | taint5 | provenance | | | ||
| pathinjection.js:20:35:20:48 | userinputthree | pathinjection.js:20:28:20:49 | isfile( ... tthree) | provenance | Config | | ||
nodes | ||
| pathinjection.js:8:19:8:38 | userinput | semmle.label | userinput | | ||
| pathinjection.js:8:31:8:38 | req.data | semmle.label | req.data | | ||
| pathinjection.js:9:19:9:44 | userinputtwo | semmle.label | userinputtwo | | ||
| pathinjection.js:9:34:9:44 | req.headers | semmle.label | req.headers | | ||
| pathinjection.js:10:19:10:45 | userinputthree | semmle.label | userinputthree | | ||
| pathinjection.js:10:36:10:45 | req.params | semmle.label | req.params | | ||
| pathinjection.js:12:19:12:52 | taint1 | semmle.label | taint1 | | ||
| pathinjection.js:12:28:12:52 | decodeU ... tthree) | semmle.label | decodeU ... tthree) | | ||
| pathinjection.js:12:38:12:51 | userinputthree | semmle.label | userinputthree | | ||
| pathinjection.js:14:19:14:61 | taint2 | semmle.label | taint2 | | ||
| pathinjection.js:14:28:14:61 | decodeU ... tthree) | semmle.label | decodeU ... tthree) | | ||
| pathinjection.js:14:47:14:60 | userinputthree | semmle.label | userinputthree | | ||
| pathinjection.js:16:19:16:48 | taint3 | semmle.label | taint3 | | ||
| pathinjection.js:16:28:16:48 | local(u ... tthree) | semmle.label | local(u ... tthree) | | ||
| pathinjection.js:16:34:16:47 | userinputthree | semmle.label | userinputthree | | ||
| pathinjection.js:18:19:18:48 | taint4 | semmle.label | taint4 | | ||
| pathinjection.js:18:28:18:48 | isdir(u ... tthree) | semmle.label | isdir(u ... tthree) | | ||
| pathinjection.js:18:34:18:47 | userinputthree | semmle.label | userinputthree | | ||
| pathinjection.js:20:19:20:49 | taint5 | semmle.label | taint5 | | ||
| pathinjection.js:20:28:20:49 | isfile( ... tthree) | semmle.label | isfile( ... tthree) | | ||
| pathinjection.js:20:35:20:48 | userinputthree | semmle.label | userinputthree | | ||
| pathinjection.js:22:36:22:41 | taint1 | semmle.label | taint1 | | ||
| pathinjection.js:24:40:24:45 | taint2 | semmle.label | taint2 | | ||
| pathinjection.js:26:34:26:39 | taint3 | semmle.label | taint3 | | ||
| pathinjection.js:28:34:28:39 | taint4 | semmle.label | taint4 | | ||
| pathinjection.js:30:40:30:45 | taint5 | semmle.label | taint5 | | ||
| pathinjection.js:31:26:31:34 | userinput | semmle.label | userinput | | ||
| pathinjection.js:33:38:33:46 | userinput | semmle.label | userinput | | ||
| pathinjection.js:34:24:34:32 | userinput | semmle.label | userinput | | ||
| pathinjection.js:36:44:36:52 | userinput | semmle.label | userinput | | ||
| pathinjection.js:37:25:37:36 | userinputtwo | semmle.label | userinputtwo | | ||
| pathinjection.js:38:25:38:33 | userinput | semmle.label | userinput | | ||
| pathinjection.js:40:26:40:34 | userinput | semmle.label | userinput | | ||
| pathinjection.js:41:26:41:34 | userinput | semmle.label | userinput | | ||
| pathinjection.js:43:25:43:33 | userinput | semmle.label | userinput | | ||
| pathinjection.js:44:25:44:33 | userinput | semmle.label | userinput | | ||
| pathinjection.js:46:26:46:34 | userinput | semmle.label | userinput | | ||
| pathinjection.js:47:26:47:34 | userinput | semmle.label | userinput | | ||
| pathinjection.js:49:22:49:30 | userinput | semmle.label | userinput | | ||
| pathinjection.js:50:22:50:30 | userinput | semmle.label | userinput | | ||
subpaths | ||
#select | ||
| pathinjection.js:22:36:22:41 | taint1 | pathinjection.js:10:36:10:45 | req.params | pathinjection.js:22:36:22:41 | taint1 | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:24:40:24:45 | taint2 | pathinjection.js:10:36:10:45 | req.params | pathinjection.js:24:40:24:45 | taint2 | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:26:34:26:39 | taint3 | pathinjection.js:10:36:10:45 | req.params | pathinjection.js:26:34:26:39 | taint3 | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:28:34:28:39 | taint4 | pathinjection.js:10:36:10:45 | req.params | pathinjection.js:28:34:28:39 | taint4 | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:30:40:30:45 | taint5 | pathinjection.js:10:36:10:45 | req.params | pathinjection.js:30:40:30:45 | taint5 | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:31:26:31:34 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:31:26:31:34 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:33:38:33:46 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:33:38:33:46 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:34:24:34:32 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:34:24:34:32 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file read. | | ||
| pathinjection.js:36:44:36:52 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:36:44:36:52 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:37:25:37:36 | userinputtwo | pathinjection.js:9:34:9:44 | req.headers | pathinjection.js:37:25:37:36 | userinputtwo | This CDS utils usage relies on user-provided value and can result in tainted data being written to a file. | | ||
| pathinjection.js:38:25:38:33 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:38:25:38:33 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:40:26:40:34 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:40:26:40:34 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:41:26:41:34 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:41:26:41:34 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:43:25:43:33 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:43:25:43:33 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:44:25:44:33 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:44:25:44:33 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:46:26:46:34 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:46:26:46:34 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:47:26:47:34 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:47:26:47:34 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:49:22:49:30 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:49:22:49:30 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | | ||
| pathinjection.js:50:22:50:30 | userinput | pathinjection.js:8:31:8:38 | req.data | pathinjection.js:50:22:50:30 | userinput | This CDS utils usage relies on user-provided value and can result in unrestricted file operations. | |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
const cds = require("@sap/cds"); | ||
const { decodeURI, decodeURIComponent, local, isdir, isfile, read, readdir, append, write, copy, stat, find, mkdirp, rmdir, rimraf, rm } = cds.utils | ||
|
||
module.exports = class Service1 extends cds.ApplicationService { | ||
|
||
init() { | ||
this.on("send1", async (req) => { | ||
const userinput = req.data | ||
const userinputtwo = req.headers | ||
const userinputthree = req.params | ||
|
||
const taint1 = decodeURI(userinputthree) // taint step | ||
|
||
const taint2 = decodeURIComponent(userinputthree) // taint step | ||
|
||
const taint3 = local(userinputthree) // taint step | ||
|
||
const taint4 = isdir(userinputthree) // taint step | ||
|
||
const taint5 = isfile(userinputthree) // taint step | ||
|
||
const pkg = await read(taint1) // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
|
||
const pdir = await readdir(taint2) // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
|
||
const s = await stat(taint3) // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
|
||
const f = await find(taint4) // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
|
||
await append('db/data').to(taint5) // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
await append(userinput, 'dist/db/data') // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
|
||
await copy('db/data').to(userinput) // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
await copy(userinput, 'dist/db/data') // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file read.
|
||
|
||
await write({ foo: 'bar' }).to(userinput) // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
await write(userinputtwo).to('db/data') // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in tainted data being written to a file.
|
||
await write(userinput, { foo: 'bar' }) // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
|
||
await mkdirp(userinput, 'db', 'data') // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
await mkdirp(userinput) // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
|
||
await rmdir(userinput, 'db', 'data') // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
await rmdir(userinput) // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
|
||
await rimraf(userinput, 'db', 'data') // sink | ||
Check failureCode scanning / CodeQL Use of user controlled input in CAP CDS file system utilities High test
This CDS utils usage relies on user-provided value and can result in unrestricted file operations.
|
||
await rimraf(userinput) // sink | ||
|
||
|
||
await rm(userinput, 'db', 'data') // sink | ||
|
||
await rm(userinput) // sink | ||
|
||
}); | ||
|
||
super.init(); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
path-traversal/PathInjection.ql |
Uh oh!
There was an error while loading. Please reload this page.