Merge pull request #9708 from github/nickrolfe/pathname

Ruby: model the standard library's `Pathname` class
This commit is contained in:
Nick Rolfe
2022-07-18 11:29:30 +01:00
committed by GitHub
8 changed files with 756 additions and 0 deletions

View File

@@ -4,3 +4,4 @@
import stdlib.Open3
import stdlib.Logger
import stdlib.Pathname

View File

@@ -0,0 +1,188 @@
/** Modeling of the `Pathname` class from the Ruby standard library. */
private import codeql.ruby.AST
private import codeql.ruby.ApiGraphs
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.dataflow.internal.DataFlowDispatch
private import codeql.ruby.frameworks.data.ModelsAsData
/**
* Modeling of the `Pathname` class from the Ruby standard library.
*
* https://docs.ruby-lang.org/en/3.1/Pathname.html
*/
module Pathname {
/**
* An instance of the `Pathname` class. For example, in
*
* ```rb
* pn = Pathname.new "foo.txt'"
* puts pn.read
* ```
*
* there are three `PathnameInstance`s - the call to `Pathname.new`, the
* assignment `pn = ...`, and the read access to `pn` on the second line.
*
* Every `PathnameInstance` is considered to be a `FileNameSource`.
*/
class PathnameInstance extends FileNameSource, DataFlow::Node {
PathnameInstance() { this = pathnameInstance() }
}
private DataFlow::Node pathnameInstance() {
// A call to `Pathname.new`.
result = API::getTopLevelMember("Pathname").getAnInstantiation()
or
// Class methods on `Pathname` that return a new `Pathname`.
result = API::getTopLevelMember("Pathname").getAMethodCall(["getwd", "pwd",])
or
// Instance methods on `Pathname` that return a new `Pathname`.
exists(DataFlow::CallNode c | result = c |
c.getReceiver() = pathnameInstance() and
c.getMethodName() =
[
"+", "/", "basename", "cleanpath", "expand_path", "join", "realpath",
"relative_path_from", "sub", "sub_ext", "to_path"
]
)
or
exists(DataFlow::Node inst |
inst = pathnameInstance() and
inst.(DataFlow::LocalSourceNode).flowsTo(result)
)
}
/** A call where the receiver is a `Pathname`. */
class PathnameCall extends DataFlow::CallNode {
PathnameCall() { this.getReceiver() instanceof PathnameInstance }
}
/**
* A call to `Pathname#open` or `Pathname#opendir`, considered as a
* `FileSystemAccess`.
*/
class PathnameOpen extends FileSystemAccess::Range, PathnameCall {
PathnameOpen() { this.getMethodName() = ["open", "opendir"] }
override DataFlow::Node getAPathArgument() { result = this.getReceiver() }
}
/** A call to `Pathname#read`, considered as a `FileSystemReadAccess`. */
class PathnameRead extends FileSystemReadAccess::Range, PathnameCall {
PathnameRead() { this.getMethodName() = "read" }
// The path is the receiver (the `Pathname` object).
override DataFlow::Node getAPathArgument() { result = this.getReceiver() }
// The read data is the return value of the call.
override DataFlow::Node getADataNode() { result = this }
}
/** A call to `Pathname#write`, considered as a `FileSystemWriteAccess`. */
class PathnameWrite extends FileSystemWriteAccess::Range, PathnameCall {
PathnameWrite() { this.getMethodName() = "write" }
// The path is the receiver (the `Pathname` object).
override DataFlow::Node getAPathArgument() { result = this.getReceiver() }
// The data to write is the 0th argument.
override DataFlow::Node getADataNode() { result = this.getArgument(0) }
}
/** A call to `Pathname#to_s`, considered as a `FileNameSource`. */
class PathnameToSFilenameSource extends FileNameSource, PathnameCall {
PathnameToSFilenameSource() { this.getMethodName() = "to_s" }
}
private class PathnamePermissionModification extends FileSystemPermissionModification::Range,
PathnameCall {
private DataFlow::Node permissionArg;
PathnamePermissionModification() {
exists(string methodName | this.getMethodName() = methodName |
methodName = ["chmod", "mkdir"] and permissionArg = this.getArgument(0)
or
methodName = "mkpath" and permissionArg = this.getKeywordArgument("mode")
or
methodName = "open" and permissionArg = this.getArgument(1)
// TODO: defaults for optional args? This may depend on the umask
)
}
override DataFlow::Node getAPermissionNode() { result = permissionArg }
}
/**
* Type summaries for the `Pathname` class, i.e. method calls that produce new
* `Pathname` instances.
*/
private class PathnameTypeSummary extends ModelInput::TypeModelCsv {
override predicate row(string row) {
// package1;type1;package2;type2;path
row =
[
// Pathname.new : Pathname
";Pathname;;;Member[Pathname].Instance",
// Pathname#+(path) : Pathname
";Pathname;;Pathname;Method[+].ReturnValue",
// Pathname#/(path) : Pathname
";Pathname;;Pathname;Method[/].ReturnValue",
// Pathname#basename(path) : Pathname
";Pathname;;Pathname;Method[basename].ReturnValue",
// Pathname#cleanpath(path) : Pathname
";Pathname;;Pathname;Method[cleanpath].ReturnValue",
// Pathname#expand_path(path) : Pathname
";Pathname;;Pathname;Method[expand_path].ReturnValue",
// Pathname#join(path) : Pathname
";Pathname;;Pathname;Method[join].ReturnValue",
// Pathname#realpath(path) : Pathname
";Pathname;;Pathname;Method[realpath].ReturnValue",
// Pathname#relative_path_from(path) : Pathname
";Pathname;;Pathname;Method[relative_path_from].ReturnValue",
// Pathname#sub(path) : Pathname
";Pathname;;Pathname;Method[sub].ReturnValue",
// Pathname#sub_ext(path) : Pathname
";Pathname;;Pathname;Method[sub_ext].ReturnValue",
// Pathname#to_path(path) : Pathname
";Pathname;;Pathname;Method[to_path].ReturnValue",
]
}
}
/** Taint flow summaries for the `Pathname` class. */
private class PathnameTaintSummary extends ModelInput::SummaryModelCsv {
override predicate row(string row) {
row =
[
// Pathname.new(path)
";;Member[Pathname].Method[new];Argument[0];ReturnValue;taint",
// Pathname#dirname
";Pathname;Method[dirname];Argument[self];ReturnValue;taint",
// Pathname#each_filename
";Pathname;Method[each_filename];Argument[self];Argument[block].Parameter[0];taint",
// Pathname#expand_path
";Pathname;Method[expand_path];Argument[self];ReturnValue;taint",
// Pathname#join
";Pathname;Method[join];Argument[self,any];ReturnValue;taint",
// Pathname#parent
";Pathname;Method[parent];Argument[self];ReturnValue;taint",
// Pathname#realpath
";Pathname;Method[realpath];Argument[self];ReturnValue;taint",
// Pathname#relative_path_from
";Pathname;Method[relative_path_from];Argument[self];ReturnValue;taint",
// Pathname#to_path
";Pathname;Method[to_path];Argument[self];ReturnValue;taint",
// Pathname#basename
";Pathname;Method[basename];Argument[self];ReturnValue;taint",
// Pathname#cleanpath
";Pathname;Method[cleanpath];Argument[self];ReturnValue;taint",
// Pathname#sub
";Pathname;Method[sub];Argument[self];ReturnValue;taint",
// Pathname#sub_ext
";Pathname;Method[sub_ext];Argument[self];ReturnValue;taint",
]
}
}
}

View File

@@ -0,0 +1,219 @@
failures
edges
| pathname_flow.rb:4:10:4:33 | call to new : | pathname_flow.rb:5:10:5:11 | pn |
| pathname_flow.rb:4:23:4:32 | call to source : | pathname_flow.rb:4:10:4:33 | call to new : |
| pathname_flow.rb:9:7:9:30 | call to new : | pathname_flow.rb:11:8:11:12 | ... + ... |
| pathname_flow.rb:9:20:9:29 | call to source : | pathname_flow.rb:9:7:9:30 | call to new : |
| pathname_flow.rb:10:7:10:30 | call to new : | pathname_flow.rb:11:8:11:12 | ... + ... |
| pathname_flow.rb:10:20:10:29 | call to source : | pathname_flow.rb:10:7:10:30 | call to new : |
| pathname_flow.rb:15:8:15:31 | call to new : | pathname_flow.rb:16:8:16:9 | pn : |
| pathname_flow.rb:15:21:15:30 | call to source : | pathname_flow.rb:15:8:15:31 | call to new : |
| pathname_flow.rb:16:8:16:9 | pn : | pathname_flow.rb:16:8:16:17 | call to dirname |
| pathname_flow.rb:20:7:20:30 | call to new : | pathname_flow.rb:21:3:21:3 | a : |
| pathname_flow.rb:20:20:20:29 | call to source : | pathname_flow.rb:20:7:20:30 | call to new : |
| pathname_flow.rb:21:3:21:3 | a : | pathname_flow.rb:21:23:21:23 | x : |
| pathname_flow.rb:21:23:21:23 | x : | pathname_flow.rb:22:10:22:10 | x |
| pathname_flow.rb:27:7:27:30 | call to new : | pathname_flow.rb:28:8:28:8 | a : |
| pathname_flow.rb:27:20:27:29 | call to source : | pathname_flow.rb:27:7:27:30 | call to new : |
| pathname_flow.rb:28:8:28:8 | a : | pathname_flow.rb:28:8:28:22 | call to expand_path |
| pathname_flow.rb:32:7:32:30 | call to new : | pathname_flow.rb:35:8:35:8 | a : |
| pathname_flow.rb:32:20:32:29 | call to source : | pathname_flow.rb:32:7:32:30 | call to new : |
| pathname_flow.rb:34:7:34:30 | call to new : | pathname_flow.rb:35:18:35:18 | c : |
| pathname_flow.rb:34:20:34:29 | call to source : | pathname_flow.rb:34:7:34:30 | call to new : |
| pathname_flow.rb:35:8:35:8 | a : | pathname_flow.rb:35:8:35:19 | call to join |
| pathname_flow.rb:35:18:35:18 | c : | pathname_flow.rb:35:8:35:19 | call to join |
| pathname_flow.rb:39:7:39:30 | call to new : | pathname_flow.rb:40:8:40:8 | a : |
| pathname_flow.rb:39:20:39:29 | call to source : | pathname_flow.rb:39:7:39:30 | call to new : |
| pathname_flow.rb:40:8:40:8 | a : | pathname_flow.rb:40:8:40:17 | call to parent |
| pathname_flow.rb:44:7:44:30 | call to new : | pathname_flow.rb:45:8:45:8 | a : |
| pathname_flow.rb:44:20:44:29 | call to source : | pathname_flow.rb:44:7:44:30 | call to new : |
| pathname_flow.rb:45:8:45:8 | a : | pathname_flow.rb:45:8:45:19 | call to realpath |
| pathname_flow.rb:49:7:49:30 | call to new : | pathname_flow.rb:50:8:50:8 | a : |
| pathname_flow.rb:49:20:49:29 | call to source : | pathname_flow.rb:49:7:49:30 | call to new : |
| pathname_flow.rb:50:8:50:8 | a : | pathname_flow.rb:50:8:50:39 | call to relative_path_from |
| pathname_flow.rb:54:7:54:30 | call to new : | pathname_flow.rb:55:8:55:8 | a : |
| pathname_flow.rb:54:20:54:29 | call to source : | pathname_flow.rb:54:7:54:30 | call to new : |
| pathname_flow.rb:55:8:55:8 | a : | pathname_flow.rb:55:8:55:16 | call to to_path |
| pathname_flow.rb:59:7:59:30 | call to new : | pathname_flow.rb:60:8:60:8 | a : |
| pathname_flow.rb:59:20:59:29 | call to source : | pathname_flow.rb:59:7:59:30 | call to new : |
| pathname_flow.rb:60:8:60:8 | a : | pathname_flow.rb:60:8:60:13 | call to to_s |
| pathname_flow.rb:64:7:64:30 | call to new : | pathname_flow.rb:66:8:66:8 | b |
| pathname_flow.rb:64:20:64:29 | call to source : | pathname_flow.rb:64:7:64:30 | call to new : |
| pathname_flow.rb:70:7:70:30 | call to new : | pathname_flow.rb:72:8:72:8 | b |
| pathname_flow.rb:70:20:70:29 | call to source : | pathname_flow.rb:70:7:70:30 | call to new : |
| pathname_flow.rb:76:7:76:30 | call to new : | pathname_flow.rb:77:7:77:7 | a : |
| pathname_flow.rb:76:20:76:29 | call to source : | pathname_flow.rb:76:7:76:30 | call to new : |
| pathname_flow.rb:77:7:77:7 | a : | pathname_flow.rb:77:7:77:16 | call to basename : |
| pathname_flow.rb:77:7:77:16 | call to basename : | pathname_flow.rb:78:8:78:8 | b |
| pathname_flow.rb:82:7:82:30 | call to new : | pathname_flow.rb:83:7:83:7 | a : |
| pathname_flow.rb:82:20:82:29 | call to source : | pathname_flow.rb:82:7:82:30 | call to new : |
| pathname_flow.rb:83:7:83:7 | a : | pathname_flow.rb:83:7:83:17 | call to cleanpath : |
| pathname_flow.rb:83:7:83:17 | call to cleanpath : | pathname_flow.rb:84:8:84:8 | b |
| pathname_flow.rb:88:7:88:30 | call to new : | pathname_flow.rb:89:7:89:7 | a : |
| pathname_flow.rb:88:20:88:29 | call to source : | pathname_flow.rb:88:7:88:30 | call to new : |
| pathname_flow.rb:89:7:89:7 | a : | pathname_flow.rb:89:7:89:25 | call to sub : |
| pathname_flow.rb:89:7:89:25 | call to sub : | pathname_flow.rb:90:8:90:8 | b |
| pathname_flow.rb:94:7:94:30 | call to new : | pathname_flow.rb:95:7:95:7 | a : |
| pathname_flow.rb:94:20:94:29 | call to source : | pathname_flow.rb:94:7:94:30 | call to new : |
| pathname_flow.rb:95:7:95:7 | a : | pathname_flow.rb:95:7:95:23 | call to sub_ext : |
| pathname_flow.rb:95:7:95:23 | call to sub_ext : | pathname_flow.rb:96:8:96:8 | b |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:104:8:104:8 | b : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:107:8:107:8 | c : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:109:7:109:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:112:7:112:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:115:7:115:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:118:7:118:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:121:7:121:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:124:7:124:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:127:7:127:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:130:7:130:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:133:7:133:7 | a : |
| pathname_flow.rb:101:20:101:29 | call to source : | pathname_flow.rb:101:7:101:30 | call to new : |
| pathname_flow.rb:104:8:104:8 | b : | pathname_flow.rb:104:8:104:17 | call to realpath |
| pathname_flow.rb:107:8:107:8 | c : | pathname_flow.rb:107:8:107:17 | call to realpath |
| pathname_flow.rb:109:7:109:7 | a : | pathname_flow.rb:109:7:109:16 | call to basename : |
| pathname_flow.rb:109:7:109:16 | call to basename : | pathname_flow.rb:110:8:110:8 | d : |
| pathname_flow.rb:110:8:110:8 | d : | pathname_flow.rb:110:8:110:17 | call to realpath |
| pathname_flow.rb:112:7:112:7 | a : | pathname_flow.rb:112:7:112:17 | call to cleanpath : |
| pathname_flow.rb:112:7:112:17 | call to cleanpath : | pathname_flow.rb:113:8:113:8 | e : |
| pathname_flow.rb:113:8:113:8 | e : | pathname_flow.rb:113:8:113:17 | call to realpath |
| pathname_flow.rb:115:7:115:7 | a : | pathname_flow.rb:115:7:115:19 | call to expand_path : |
| pathname_flow.rb:115:7:115:19 | call to expand_path : | pathname_flow.rb:116:8:116:8 | f : |
| pathname_flow.rb:116:8:116:8 | f : | pathname_flow.rb:116:8:116:17 | call to realpath |
| pathname_flow.rb:118:7:118:7 | a : | pathname_flow.rb:118:7:118:19 | call to join : |
| pathname_flow.rb:118:7:118:19 | call to join : | pathname_flow.rb:119:8:119:8 | g : |
| pathname_flow.rb:119:8:119:8 | g : | pathname_flow.rb:119:8:119:17 | call to realpath |
| pathname_flow.rb:121:7:121:7 | a : | pathname_flow.rb:121:7:121:16 | call to realpath : |
| pathname_flow.rb:121:7:121:16 | call to realpath : | pathname_flow.rb:122:8:122:8 | h : |
| pathname_flow.rb:122:8:122:8 | h : | pathname_flow.rb:122:8:122:17 | call to realpath |
| pathname_flow.rb:124:7:124:7 | a : | pathname_flow.rb:124:7:124:38 | call to relative_path_from : |
| pathname_flow.rb:124:7:124:38 | call to relative_path_from : | pathname_flow.rb:125:8:125:8 | i : |
| pathname_flow.rb:125:8:125:8 | i : | pathname_flow.rb:125:8:125:17 | call to realpath |
| pathname_flow.rb:127:7:127:7 | a : | pathname_flow.rb:127:7:127:25 | call to sub : |
| pathname_flow.rb:127:7:127:25 | call to sub : | pathname_flow.rb:128:8:128:8 | j : |
| pathname_flow.rb:128:8:128:8 | j : | pathname_flow.rb:128:8:128:17 | call to realpath |
| pathname_flow.rb:130:7:130:7 | a : | pathname_flow.rb:130:7:130:23 | call to sub_ext : |
| pathname_flow.rb:130:7:130:23 | call to sub_ext : | pathname_flow.rb:131:8:131:8 | k : |
| pathname_flow.rb:131:8:131:8 | k : | pathname_flow.rb:131:8:131:17 | call to realpath |
| pathname_flow.rb:133:7:133:7 | a : | pathname_flow.rb:133:7:133:15 | call to to_path : |
| pathname_flow.rb:133:7:133:15 | call to to_path : | pathname_flow.rb:134:8:134:8 | l : |
| pathname_flow.rb:134:8:134:8 | l : | pathname_flow.rb:134:8:134:17 | call to realpath |
nodes
| pathname_flow.rb:4:10:4:33 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:4:23:4:32 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:5:10:5:11 | pn | semmle.label | pn |
| pathname_flow.rb:9:7:9:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:9:20:9:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:10:7:10:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:10:20:10:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:11:8:11:12 | ... + ... | semmle.label | ... + ... |
| pathname_flow.rb:15:8:15:31 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:15:21:15:30 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:16:8:16:9 | pn : | semmle.label | pn : |
| pathname_flow.rb:16:8:16:17 | call to dirname | semmle.label | call to dirname |
| pathname_flow.rb:20:7:20:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:20:20:20:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:21:3:21:3 | a : | semmle.label | a : |
| pathname_flow.rb:21:23:21:23 | x : | semmle.label | x : |
| pathname_flow.rb:22:10:22:10 | x | semmle.label | x |
| pathname_flow.rb:27:7:27:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:27:20:27:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:28:8:28:8 | a : | semmle.label | a : |
| pathname_flow.rb:28:8:28:22 | call to expand_path | semmle.label | call to expand_path |
| pathname_flow.rb:32:7:32:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:32:20:32:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:34:7:34:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:34:20:34:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:35:8:35:8 | a : | semmle.label | a : |
| pathname_flow.rb:35:8:35:19 | call to join | semmle.label | call to join |
| pathname_flow.rb:35:18:35:18 | c : | semmle.label | c : |
| pathname_flow.rb:39:7:39:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:39:20:39:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:40:8:40:8 | a : | semmle.label | a : |
| pathname_flow.rb:40:8:40:17 | call to parent | semmle.label | call to parent |
| pathname_flow.rb:44:7:44:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:44:20:44:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:45:8:45:8 | a : | semmle.label | a : |
| pathname_flow.rb:45:8:45:19 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:49:7:49:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:49:20:49:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:50:8:50:8 | a : | semmle.label | a : |
| pathname_flow.rb:50:8:50:39 | call to relative_path_from | semmle.label | call to relative_path_from |
| pathname_flow.rb:54:7:54:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:54:20:54:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:55:8:55:8 | a : | semmle.label | a : |
| pathname_flow.rb:55:8:55:16 | call to to_path | semmle.label | call to to_path |
| pathname_flow.rb:59:7:59:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:59:20:59:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:60:8:60:8 | a : | semmle.label | a : |
| pathname_flow.rb:60:8:60:13 | call to to_s | semmle.label | call to to_s |
| pathname_flow.rb:64:7:64:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:64:20:64:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:66:8:66:8 | b | semmle.label | b |
| pathname_flow.rb:70:7:70:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:70:20:70:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:72:8:72:8 | b | semmle.label | b |
| pathname_flow.rb:76:7:76:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:76:20:76:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:77:7:77:7 | a : | semmle.label | a : |
| pathname_flow.rb:77:7:77:16 | call to basename : | semmle.label | call to basename : |
| pathname_flow.rb:78:8:78:8 | b | semmle.label | b |
| pathname_flow.rb:82:7:82:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:82:20:82:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:83:7:83:7 | a : | semmle.label | a : |
| pathname_flow.rb:83:7:83:17 | call to cleanpath : | semmle.label | call to cleanpath : |
| pathname_flow.rb:84:8:84:8 | b | semmle.label | b |
| pathname_flow.rb:88:7:88:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:88:20:88:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:89:7:89:7 | a : | semmle.label | a : |
| pathname_flow.rb:89:7:89:25 | call to sub : | semmle.label | call to sub : |
| pathname_flow.rb:90:8:90:8 | b | semmle.label | b |
| pathname_flow.rb:94:7:94:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:94:20:94:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:95:7:95:7 | a : | semmle.label | a : |
| pathname_flow.rb:95:7:95:23 | call to sub_ext : | semmle.label | call to sub_ext : |
| pathname_flow.rb:96:8:96:8 | b | semmle.label | b |
| pathname_flow.rb:101:7:101:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:101:20:101:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:104:8:104:8 | b : | semmle.label | b : |
| pathname_flow.rb:104:8:104:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:107:8:107:8 | c : | semmle.label | c : |
| pathname_flow.rb:107:8:107:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:109:7:109:7 | a : | semmle.label | a : |
| pathname_flow.rb:109:7:109:16 | call to basename : | semmle.label | call to basename : |
| pathname_flow.rb:110:8:110:8 | d : | semmle.label | d : |
| pathname_flow.rb:110:8:110:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:112:7:112:7 | a : | semmle.label | a : |
| pathname_flow.rb:112:7:112:17 | call to cleanpath : | semmle.label | call to cleanpath : |
| pathname_flow.rb:113:8:113:8 | e : | semmle.label | e : |
| pathname_flow.rb:113:8:113:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:115:7:115:7 | a : | semmle.label | a : |
| pathname_flow.rb:115:7:115:19 | call to expand_path : | semmle.label | call to expand_path : |
| pathname_flow.rb:116:8:116:8 | f : | semmle.label | f : |
| pathname_flow.rb:116:8:116:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:118:7:118:7 | a : | semmle.label | a : |
| pathname_flow.rb:118:7:118:19 | call to join : | semmle.label | call to join : |
| pathname_flow.rb:119:8:119:8 | g : | semmle.label | g : |
| pathname_flow.rb:119:8:119:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:121:7:121:7 | a : | semmle.label | a : |
| pathname_flow.rb:121:7:121:16 | call to realpath : | semmle.label | call to realpath : |
| pathname_flow.rb:122:8:122:8 | h : | semmle.label | h : |
| pathname_flow.rb:122:8:122:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:124:7:124:7 | a : | semmle.label | a : |
| pathname_flow.rb:124:7:124:38 | call to relative_path_from : | semmle.label | call to relative_path_from : |
| pathname_flow.rb:125:8:125:8 | i : | semmle.label | i : |
| pathname_flow.rb:125:8:125:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:127:7:127:7 | a : | semmle.label | a : |
| pathname_flow.rb:127:7:127:25 | call to sub : | semmle.label | call to sub : |
| pathname_flow.rb:128:8:128:8 | j : | semmle.label | j : |
| pathname_flow.rb:128:8:128:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:130:7:130:7 | a : | semmle.label | a : |
| pathname_flow.rb:130:7:130:23 | call to sub_ext : | semmle.label | call to sub_ext : |
| pathname_flow.rb:131:8:131:8 | k : | semmle.label | k : |
| pathname_flow.rb:131:8:131:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:133:7:133:7 | a : | semmle.label | a : |
| pathname_flow.rb:133:7:133:15 | call to to_path : | semmle.label | call to to_path : |
| pathname_flow.rb:134:8:134:8 | l : | semmle.label | l : |
| pathname_flow.rb:134:8:134:17 | call to realpath | semmle.label | call to realpath |
subpaths
#select

View File

@@ -0,0 +1,11 @@
/**
* @kind path-problem
*/
import ruby
import TestUtilities.InlineFlowTest
import PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf
where conf.hasFlowPath(source, sink)
select sink, source, sink, "$@", source, source.toString()

View File

@@ -0,0 +1,135 @@
require 'pathname'
def m_new
pn = Pathname.new(source 'a')
sink pn # $ hasTaintFlow=a
end
def m_plus
a = Pathname.new(source 'a')
b = Pathname.new(source 'b')
sink(a + b) # $ hasTaintFlow=a $ hasTaintFlow=b
end
def m_dirname
pn = Pathname.new(source 'a')
sink pn.dirname # $ hasTaintFlow=a
end
def m_each_filename
a = Pathname.new(source 'a')
a.each_filename do |x|
sink x # $ hasTaintFlow=a
end
end
def m_expand_path
a = Pathname.new(source 'a')
sink a.expand_path() # $ hasTaintFlow=a
end
def m_join
a = Pathname.new(source 'a')
b = Pathname.new('foo')
c = Pathname.new(source 'c')
sink a.join(b, c) # $ hasTaintFlow=a $ hasTaintFlow=c
end
def m_parent
a = Pathname.new(source 'a')
sink a.parent() # $ hasTaintFlow=a
end
def m_realpath
a = Pathname.new(source 'a')
sink a.realpath() # $ hasTaintFlow=a
end
def m_relative_path_from
a = Pathname.new(source 'a')
sink a.relative_path_from('/foo/bar') # $ hasTaintFlow=a
end
def m_to_path
a = Pathname.new(source 'a')
sink a.to_path # $ hasTaintFlow=a
end
def m_to_s
a = Pathname.new(source 'a')
sink a.to_s # $ hasTaintFlow=a
end
def m_plus
a = Pathname.new(source 'a')
b = a + 'foo'
sink b # $ hasTaintFlow=a
end
def m_slash
a = Pathname.new(source 'a')
b = a / 'foo'
sink b # $ hasTaintFlow=a
end
def m_basename
a = Pathname.new(source 'a')
b = a.basename
sink b # $ hasTaintFlow=a
end
def m_cleanpath
a = Pathname.new(source 'a')
b = a.cleanpath
sink b # $ hasTaintFlow=a
end
def m_sub
a = Pathname.new(source 'a')
b = a.sub('foo', 'bar')
sink b # $ hasTaintFlow=a
end
def m_sub_ext
a = Pathname.new(source 'a')
b = a.sub_ext('.txt')
sink b # $ hasTaintFlow=a
end
# Test flow through intermediate pathnames
def intermediate_pathnames
a = Pathname.new(source 'a')
b = a + 'foo'
sink b.realpath # $ hasTaintFlow=a
c = a / 'foo'
sink c.realpath # $ hasTaintFlow=a
d = a.basename
sink d.realpath # $ hasTaintFlow=a
e = a.cleanpath
sink e.realpath # $ hasTaintFlow=a
f = a.expand_path
sink f.realpath # $ hasTaintFlow=a
g = a.join('foo')
sink g.realpath # $ hasTaintFlow=a
h = a.realpath
sink h.realpath # $ hasTaintFlow=a
i = a.relative_path_from('/foo/bar')
sink i.realpath # $ hasTaintFlow=a
j = a.sub('foo', 'bar')
sink j.realpath # $ hasTaintFlow=a
k = a.sub_ext('.txt')
sink k.realpath # $ hasTaintFlow=a
l = a.to_path
sink l.realpath # $ hasTaintFlow=a
end

View File

@@ -0,0 +1,134 @@
pathnameInstances
| Pathname.rb:2:1:2:33 | ... = ... |
| Pathname.rb:2:1:2:33 | ... = ... |
| Pathname.rb:2:12:2:33 | call to new |
| Pathname.rb:3:1:3:20 | ... = ... |
| Pathname.rb:3:13:3:20 | foo_path |
| Pathname.rb:4:1:4:8 | foo_path |
| Pathname.rb:6:1:6:29 | ... = ... |
| Pathname.rb:6:1:6:29 | ... = ... |
| Pathname.rb:6:12:6:29 | call to new |
| Pathname.rb:9:1:9:21 | ... = ... |
| Pathname.rb:9:1:9:21 | ... = ... |
| Pathname.rb:9:8:9:21 | call to getwd |
| Pathname.rb:10:1:10:21 | ... = ... |
| Pathname.rb:10:7:10:10 | pwd1 |
| Pathname.rb:10:7:10:21 | ... + ... |
| Pathname.rb:10:14:10:21 | foo_path |
| Pathname.rb:11:1:11:21 | ... = ... |
| Pathname.rb:11:1:11:21 | ... = ... |
| Pathname.rb:11:7:11:10 | pwd1 |
| Pathname.rb:11:7:11:21 | ... / ... |
| Pathname.rb:11:14:11:21 | bar_path |
| Pathname.rb:12:1:12:19 | ... = ... |
| Pathname.rb:12:7:12:10 | pwd1 |
| Pathname.rb:12:7:12:19 | call to basename |
| Pathname.rb:13:1:13:46 | ... = ... |
| Pathname.rb:13:7:13:36 | call to new |
| Pathname.rb:13:7:13:46 | call to cleanpath |
| Pathname.rb:14:1:14:26 | ... = ... |
| Pathname.rb:14:7:14:14 | foo_path |
| Pathname.rb:14:7:14:26 | call to expand_path |
| Pathname.rb:15:1:15:39 | ... = ... |
| Pathname.rb:15:7:15:10 | pwd1 |
| Pathname.rb:15:7:15:39 | call to join |
| Pathname.rb:16:1:16:23 | ... = ... |
| Pathname.rb:16:7:16:14 | foo_path |
| Pathname.rb:16:7:16:23 | call to realpath |
| Pathname.rb:17:1:17:59 | ... = ... |
| Pathname.rb:17:7:17:33 | call to new |
| Pathname.rb:17:7:17:59 | call to relative_path_from |
| Pathname.rb:18:1:18:33 | ... = ... |
| Pathname.rb:18:1:18:33 | ... = ... |
| Pathname.rb:18:7:18:10 | pwd1 |
| Pathname.rb:18:7:18:33 | call to sub |
| Pathname.rb:19:1:19:29 | ... = ... |
| Pathname.rb:19:7:19:14 | foo_path |
| Pathname.rb:19:7:19:29 | call to sub_ext |
| Pathname.rb:20:1:20:22 | ... = ... |
| Pathname.rb:20:7:20:14 | foo_path |
| Pathname.rb:20:7:20:22 | call to to_path |
| Pathname.rb:23:14:23:21 | foo_path |
| Pathname.rb:26:12:26:19 | foo_path |
| Pathname.rb:28:11:28:14 | pwd1 |
| Pathname.rb:32:12:32:19 | foo_path |
| Pathname.rb:35:1:35:8 | foo_path |
| Pathname.rb:38:1:38:8 | foo_path |
| Pathname.rb:39:12:39:19 | foo_path |
| Pathname.rb:41:1:41:3 | p08 |
| Pathname.rb:42:1:42:3 | p01 |
fileSystemAccesses
| Pathname.rb:26:12:26:24 | call to open | Pathname.rb:26:12:26:19 | foo_path |
| Pathname.rb:28:11:28:22 | call to opendir | Pathname.rb:28:11:28:14 | pwd1 |
| Pathname.rb:32:12:32:24 | call to read | Pathname.rb:32:12:32:19 | foo_path |
| Pathname.rb:35:1:35:23 | call to write | Pathname.rb:35:1:35:8 | foo_path |
| Pathname.rb:39:12:39:34 | call to open | Pathname.rb:39:12:39:19 | foo_path |
fileNameSources
| Pathname.rb:2:1:2:33 | ... = ... |
| Pathname.rb:2:1:2:33 | ... = ... |
| Pathname.rb:2:12:2:33 | call to new |
| Pathname.rb:3:1:3:20 | ... = ... |
| Pathname.rb:3:13:3:20 | foo_path |
| Pathname.rb:4:1:4:8 | foo_path |
| Pathname.rb:6:1:6:29 | ... = ... |
| Pathname.rb:6:1:6:29 | ... = ... |
| Pathname.rb:6:12:6:29 | call to new |
| Pathname.rb:9:1:9:21 | ... = ... |
| Pathname.rb:9:1:9:21 | ... = ... |
| Pathname.rb:9:8:9:21 | call to getwd |
| Pathname.rb:10:1:10:21 | ... = ... |
| Pathname.rb:10:7:10:10 | pwd1 |
| Pathname.rb:10:7:10:21 | ... + ... |
| Pathname.rb:10:14:10:21 | foo_path |
| Pathname.rb:11:1:11:21 | ... = ... |
| Pathname.rb:11:1:11:21 | ... = ... |
| Pathname.rb:11:7:11:10 | pwd1 |
| Pathname.rb:11:7:11:21 | ... / ... |
| Pathname.rb:11:14:11:21 | bar_path |
| Pathname.rb:12:1:12:19 | ... = ... |
| Pathname.rb:12:7:12:10 | pwd1 |
| Pathname.rb:12:7:12:19 | call to basename |
| Pathname.rb:13:1:13:46 | ... = ... |
| Pathname.rb:13:7:13:36 | call to new |
| Pathname.rb:13:7:13:46 | call to cleanpath |
| Pathname.rb:14:1:14:26 | ... = ... |
| Pathname.rb:14:7:14:14 | foo_path |
| Pathname.rb:14:7:14:26 | call to expand_path |
| Pathname.rb:15:1:15:39 | ... = ... |
| Pathname.rb:15:7:15:10 | pwd1 |
| Pathname.rb:15:7:15:39 | call to join |
| Pathname.rb:16:1:16:23 | ... = ... |
| Pathname.rb:16:7:16:14 | foo_path |
| Pathname.rb:16:7:16:23 | call to realpath |
| Pathname.rb:17:1:17:59 | ... = ... |
| Pathname.rb:17:7:17:33 | call to new |
| Pathname.rb:17:7:17:59 | call to relative_path_from |
| Pathname.rb:18:1:18:33 | ... = ... |
| Pathname.rb:18:1:18:33 | ... = ... |
| Pathname.rb:18:7:18:10 | pwd1 |
| Pathname.rb:18:7:18:33 | call to sub |
| Pathname.rb:19:1:19:29 | ... = ... |
| Pathname.rb:19:7:19:14 | foo_path |
| Pathname.rb:19:7:19:29 | call to sub_ext |
| Pathname.rb:20:1:20:22 | ... = ... |
| Pathname.rb:20:7:20:14 | foo_path |
| Pathname.rb:20:7:20:22 | call to to_path |
| Pathname.rb:23:14:23:21 | foo_path |
| Pathname.rb:23:14:23:26 | call to to_s |
| Pathname.rb:26:12:26:19 | foo_path |
| Pathname.rb:28:11:28:14 | pwd1 |
| Pathname.rb:32:12:32:19 | foo_path |
| Pathname.rb:35:1:35:8 | foo_path |
| Pathname.rb:38:1:38:8 | foo_path |
| Pathname.rb:39:12:39:19 | foo_path |
| Pathname.rb:41:1:41:3 | p08 |
| Pathname.rb:42:1:42:3 | p01 |
fileSystemReadAccesses
| Pathname.rb:32:12:32:24 | call to read | Pathname.rb:32:12:32:24 | call to read |
fileSystemWriteAccesses
| Pathname.rb:35:1:35:23 | call to write | Pathname.rb:35:16:35:23 | "output" |
fileSystemPermissionModifications
| Pathname.rb:38:1:38:19 | call to chmod | Pathname.rb:38:16:38:19 | 0644 |
| Pathname.rb:39:12:39:34 | call to open | Pathname.rb:39:31:39:34 | 0666 |
| Pathname.rb:41:1:41:14 | call to mkdir | Pathname.rb:41:11:41:14 | 0755 |
| Pathname.rb:42:1:42:22 | call to mkpath | Pathname.rb:42:18:42:21 | 0644 |

View File

@@ -0,0 +1,26 @@
private import ruby
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.frameworks.stdlib.Pathname
query predicate pathnameInstances(Pathname::PathnameInstance i) { any() }
query predicate fileSystemAccesses(FileSystemAccess a, DataFlow::Node p) {
p = a.getAPathArgument()
}
query predicate fileNameSources(FileNameSource s) { any() }
query predicate fileSystemReadAccesses(FileSystemReadAccess a, DataFlow::Node d) {
d = a.getADataNode()
}
query predicate fileSystemWriteAccesses(FileSystemWriteAccess a, DataFlow::Node d) {
d = a.getADataNode()
}
query predicate fileSystemPermissionModifications(
FileSystemPermissionModification m, DataFlow::Node p
) {
p = m.getAPermissionNode()
}

View File

@@ -0,0 +1,42 @@
foo_path = Pathname.new "foo.txt"
foo_path2 = foo_path
foo_path
bar_path = Pathname.new 'bar'
# All these calls return new `Pathname` instances
pwd1 = Pathname.getwd
p00 = pwd1 + foo_path
p01 = pwd1 / bar_path
p02 = pwd1.basename
p03 = Pathname.new('bar/../baz.txt').cleanpath
p04 = foo_path.expand_path
p05 = pwd1.join 'bar', 'baz', 'qux.txt'
p06 = foo_path.realpath
p07 = Pathname.new('foo/bar.txt').relative_path_from('foo')
p08 = pwd1.sub 'wibble', 'wobble'
p09 = foo_path.sub_ext '.log'
p10 = foo_path.to_path
# `Pathname#to_s` returns a string that we consider to be a filename source.
foo_string = foo_path.to_s
# File-system accesses
foo_file = foo_path.open
foo_file.close
pwd_dir = pwd1.opendir
pwd_dir.close
# Read from a file
foo_data = foo_path.read
# Write to a file
foo_path.write 'output'
# Permission modifications
foo_path.chmod 0644
foo_file = foo_path.open 'w', 0666
foo_file.close
p08.mkdir 0755
p01.mkpath(mode: 0644)