|
1 | 1 | /** |
2 | | - * @name Use of `Kernel.open` or `IO.read` |
| 2 | + * @name Use of `Kernel.open` or `IO.read` with user-controlled input |
3 | 3 | * @description Using `Kernel.open` or `IO.read` may allow a malicious |
4 | 4 | * user to execute arbitrary system commands. |
5 | 5 | * @kind path-problem |
|
14 | 14 | * external/cwe/cwe-073 |
15 | 15 | */ |
16 | 16 |
|
17 | | -import codeql.ruby.AST |
18 | | -import codeql.ruby.ApiGraphs |
19 | | -import codeql.ruby.frameworks.core.Kernel::Kernel |
| 17 | +import codeql.ruby.DataFlow |
20 | 18 | import codeql.ruby.TaintTracking |
21 | | -import codeql.ruby.dataflow.BarrierGuards |
22 | 19 | import codeql.ruby.dataflow.RemoteFlowSources |
23 | | -import codeql.ruby.DataFlow |
| 20 | +import codeql.ruby.dataflow.BarrierGuards |
24 | 21 | import DataFlow::PathGraph |
25 | | - |
26 | | -/** |
27 | | - * A method call that has a suggested replacement. |
28 | | - */ |
29 | | -abstract class Replacement extends DataFlow::CallNode { |
30 | | - abstract string getFrom(); |
31 | | - |
32 | | - abstract string getTo(); |
33 | | -} |
34 | | - |
35 | | -class KernelOpenCall extends KernelMethodCall, Replacement { |
36 | | - KernelOpenCall() { this.getMethodName() = "open" } |
37 | | - |
38 | | - override string getFrom() { result = "Kernel.open" } |
39 | | - |
40 | | - override string getTo() { result = "File.open" } |
41 | | -} |
42 | | - |
43 | | -class IOReadCall extends DataFlow::CallNode, Replacement { |
44 | | - IOReadCall() { this = API::getTopLevelMember("IO").getAMethodCall("read") } |
45 | | - |
46 | | - override string getFrom() { result = "IO.read" } |
47 | | - |
48 | | - override string getTo() { result = "File.read" } |
49 | | -} |
| 22 | +import codeql.ruby.security.KernelOpenQuery |
50 | 23 |
|
51 | 24 | class Configuration extends TaintTracking::Configuration { |
52 | 25 | Configuration() { this = "KernelOpen" } |
53 | 26 |
|
54 | 27 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } |
55 | 28 |
|
56 | 29 | override predicate isSink(DataFlow::Node sink) { |
57 | | - exists(KernelOpenCall c | c.getArgument(0) = sink) |
58 | | - or |
59 | | - exists(IOReadCall c | c.getArgument(0) = sink) |
| 30 | + sink = any(AmbiguousPathCall r).getPathArgument() |
60 | 31 | } |
61 | 32 |
|
62 | 33 | override predicate isSanitizer(DataFlow::Node node) { |
|
73 | 44 | sourceNode = source.getNode() and |
74 | 45 | call.getArgument(0) = sink.getNode() |
75 | 46 | select sink.getNode(), source, sink, |
76 | | - "This call to " + call.(Replacement).getFrom() + " depends on a $@. Replace it with " + |
77 | | - call.(Replacement).getTo() + ".", source.getNode(), "user-provided value" |
| 47 | + "This call to " + call.(AmbiguousPathCall).getName() + |
| 48 | + " depends on a $@. Consider replacing it with " + call.(AmbiguousPathCall).getReplacement() + |
| 49 | + ".", source.getNode(), "user-provided value" |
0 commit comments