mirror of
https://github.com/github/codeql.git
synced 2026-04-27 09:45:15 +02:00
Merge pull request #11136 from hmac/json-flow-summaries
Ruby: JSON flow summaries
This commit is contained in:
4
ruby/ql/lib/change-notes/2022-11-24-json-taint-flow.md
Normal file
4
ruby/ql/lib/change-notes/2022-11-24-json-taint-flow.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Taint flow is now tracked through many common JSON parsing and generation methods.
|
||||
@@ -24,3 +24,4 @@ private import codeql.ruby.frameworks.XmlParsing
|
||||
private import codeql.ruby.frameworks.ActionDispatch
|
||||
private import codeql.ruby.frameworks.PosixSpawn
|
||||
private import codeql.ruby.frameworks.StringFormatters
|
||||
private import codeql.ruby.frameworks.Json
|
||||
|
||||
@@ -104,6 +104,17 @@ module ActiveSupport {
|
||||
|
||||
override predicate runsArbitraryCode() { none() }
|
||||
}
|
||||
|
||||
/** Flow summary for `Object#to_json`, which serializes the receiver as a JSON string. */
|
||||
private class ToJsonSummary extends SimpleSummarizedCallable {
|
||||
ToJsonSummary() { this = "to_json" }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = ["Argument[self]", "Argument[self].Element[any]"] and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,4 +385,17 @@ module ActiveSupport {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** `ActiveSupport::JSON` */
|
||||
module Json {
|
||||
private class JsonSummary extends ModelInput::SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"activesupport;;Member[ActiveSupport].Member[JSON].Method[encode,dump];Argument[0];ReturnValue;taint",
|
||||
"activesupport;;Member[ActiveSupport].Member[JSON].Method[decode,load];Argument[0];ReturnValue;taint",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
ruby/ql/lib/codeql/ruby/frameworks/Json.qll
Normal file
22
ruby/ql/lib/codeql/ruby/frameworks/Json.qll
Normal file
@@ -0,0 +1,22 @@
|
||||
/** Provides modeling for the `json` gem. */
|
||||
|
||||
private import codeql.ruby.frameworks.data.ModelsAsData
|
||||
|
||||
/** Provides modeling for the `json` gem. */
|
||||
module Json {
|
||||
/**
|
||||
* Flow summaries for common `JSON` methods.
|
||||
* Not all of these methods are strictly defined in the `json` gem.
|
||||
* The `JSON` namespace is heavily overloaded by other JSON parsing gems such as `oj`, `json_pure`, `multi_json` etc.
|
||||
* This summary covers common methods we've seen called on `JSON` in the wild.
|
||||
*/
|
||||
private class JsonSummary extends ModelInput::SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"json;;Member[JSON].Method[parse,parse!,load,restore];Argument[0];ReturnValue;taint",
|
||||
"json;;Member[JSON].Method[generate,fast_generate,pretty_generate,dump,unparse,fast_unparse];Argument[0];ReturnValue;taint",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
*
|
||||
* Example for a test.ql:
|
||||
* ```ql
|
||||
* *import codeql.ruby.AST
|
||||
* import TestUtilities.InlineFlowTest
|
||||
* import PathGraph
|
||||
*
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
| file://:0:0:0:0 | parameter position 0 of activesupport;;Member[ActionView].Member[SafeBuffer].Instance.Method[safe_concat] | file://:0:0:0:0 | [summary] to write: argument self in activesupport;;Member[ActionView].Member[SafeBuffer].Instance.Method[safe_concat] |
|
||||
| file://:0:0:0:0 | parameter position 0 of activesupport;;Member[ActionView].Member[SafeBuffer].Instance.Method[safe_concat] | file://:0:0:0:0 | [summary] to write: return (return) in activesupport;;Member[ActionView].Member[SafeBuffer].Instance.Method[safe_concat] |
|
||||
| file://:0:0:0:0 | parameter position 0 of activesupport;;Member[ActionView].Member[SafeBuffer].Method[new] | file://:0:0:0:0 | [summary] to write: return (return) in activesupport;;Member[ActionView].Member[SafeBuffer].Method[new] |
|
||||
| file://:0:0:0:0 | parameter position 0 of activesupport;;Member[ActiveSupport].Member[JSON].Method[decode,load] | file://:0:0:0:0 | [summary] to write: return (return) in activesupport;;Member[ActiveSupport].Member[JSON].Method[decode,load] |
|
||||
| file://:0:0:0:0 | parameter position 0 of activesupport;;Member[ActiveSupport].Member[JSON].Method[encode,dump] | file://:0:0:0:0 | [summary] to write: return (return) in activesupport;;Member[ActiveSupport].Member[JSON].Method[encode,dump] |
|
||||
| file://:0:0:0:0 | parameter position 0 of json;;Member[JSON].Method[generate,fast_generate,pretty_generate,dump,unparse,fast_unparse] | file://:0:0:0:0 | [summary] to write: return (return) in json;;Member[JSON].Method[generate,fast_generate,pretty_generate,dump,unparse,fast_unparse] |
|
||||
| file://:0:0:0:0 | parameter position 0 of json;;Member[JSON].Method[parse,parse!,load,restore] | file://:0:0:0:0 | [summary] to write: return (return) in json;;Member[JSON].Method[parse,parse!,load,restore] |
|
||||
| file://:0:0:0:0 | parameter position 0.. of File.join | file://:0:0:0:0 | [summary] to write: return (return) in File.join |
|
||||
| file://:0:0:0:0 | parameter self of & | file://:0:0:0:0 | [summary] read: argument self.any element in & |
|
||||
| file://:0:0:0:0 | parameter self of * | file://:0:0:0:0 | [summary] read: argument self.any element in * |
|
||||
|
||||
@@ -189,6 +189,19 @@ edges
|
||||
| active_support.rb:303:7:303:16 | call to source : | active_support.rb:304:19:304:19 | a : |
|
||||
| active_support.rb:304:7:304:19 | call to json_escape : | active_support.rb:305:8:305:8 | b |
|
||||
| active_support.rb:304:19:304:19 | a : | active_support.rb:304:7:304:19 | call to json_escape : |
|
||||
| active_support.rb:309:9:309:18 | call to source : | active_support.rb:310:37:310:37 | x : |
|
||||
| active_support.rb:310:37:310:37 | x : | active_support.rb:310:10:310:38 | call to encode |
|
||||
| active_support.rb:314:9:314:18 | call to source : | active_support.rb:315:37:315:37 | x : |
|
||||
| active_support.rb:315:37:315:37 | x : | active_support.rb:315:10:315:38 | call to decode |
|
||||
| active_support.rb:319:9:319:18 | call to source : | active_support.rb:320:35:320:35 | x : |
|
||||
| active_support.rb:320:35:320:35 | x : | active_support.rb:320:10:320:36 | call to dump |
|
||||
| active_support.rb:324:9:324:18 | call to source : | active_support.rb:325:35:325:35 | x : |
|
||||
| active_support.rb:325:35:325:35 | x : | active_support.rb:325:10:325:36 | call to load |
|
||||
| active_support.rb:329:9:329:18 | call to source : | active_support.rb:330:10:330:10 | x : |
|
||||
| active_support.rb:329:9:329:18 | call to source : | active_support.rb:331:10:331:10 | x : |
|
||||
| active_support.rb:330:10:330:10 | x : | active_support.rb:332:10:332:10 | y [element 0] : |
|
||||
| active_support.rb:331:10:331:10 | x : | active_support.rb:331:10:331:18 | call to to_json |
|
||||
| active_support.rb:332:10:332:10 | y [element 0] : | active_support.rb:332:10:332:18 | call to to_json |
|
||||
| hash_extensions.rb:2:14:2:24 | call to source : | hash_extensions.rb:3:9:3:9 | h [element :a] : |
|
||||
| hash_extensions.rb:2:14:2:24 | call to source : | hash_extensions.rb:3:9:3:9 | h [element :a] : |
|
||||
| hash_extensions.rb:3:9:3:9 | h [element :a] : | hash_extensions.rb:3:9:3:24 | call to stringify_keys [element] : |
|
||||
@@ -539,6 +552,24 @@ nodes
|
||||
| active_support.rb:304:7:304:19 | call to json_escape : | semmle.label | call to json_escape : |
|
||||
| active_support.rb:304:19:304:19 | a : | semmle.label | a : |
|
||||
| active_support.rb:305:8:305:8 | b | semmle.label | b |
|
||||
| active_support.rb:309:9:309:18 | call to source : | semmle.label | call to source : |
|
||||
| active_support.rb:310:10:310:38 | call to encode | semmle.label | call to encode |
|
||||
| active_support.rb:310:37:310:37 | x : | semmle.label | x : |
|
||||
| active_support.rb:314:9:314:18 | call to source : | semmle.label | call to source : |
|
||||
| active_support.rb:315:10:315:38 | call to decode | semmle.label | call to decode |
|
||||
| active_support.rb:315:37:315:37 | x : | semmle.label | x : |
|
||||
| active_support.rb:319:9:319:18 | call to source : | semmle.label | call to source : |
|
||||
| active_support.rb:320:10:320:36 | call to dump | semmle.label | call to dump |
|
||||
| active_support.rb:320:35:320:35 | x : | semmle.label | x : |
|
||||
| active_support.rb:324:9:324:18 | call to source : | semmle.label | call to source : |
|
||||
| active_support.rb:325:10:325:36 | call to load | semmle.label | call to load |
|
||||
| active_support.rb:325:35:325:35 | x : | semmle.label | x : |
|
||||
| active_support.rb:329:9:329:18 | call to source : | semmle.label | call to source : |
|
||||
| active_support.rb:330:10:330:10 | x : | semmle.label | x : |
|
||||
| active_support.rb:331:10:331:10 | x : | semmle.label | x : |
|
||||
| active_support.rb:331:10:331:18 | call to to_json | semmle.label | call to to_json |
|
||||
| active_support.rb:332:10:332:10 | y [element 0] : | semmle.label | y [element 0] : |
|
||||
| active_support.rb:332:10:332:18 | call to to_json | semmle.label | call to to_json |
|
||||
| hash_extensions.rb:2:14:2:24 | call to source : | semmle.label | call to source : |
|
||||
| hash_extensions.rb:2:14:2:24 | call to source : | semmle.label | call to source : |
|
||||
| hash_extensions.rb:3:9:3:9 | h [element :a] : | semmle.label | h [element :a] : |
|
||||
|
||||
@@ -304,3 +304,30 @@ def m_json_escape
|
||||
b = json_escape a
|
||||
sink b # $hasTaintFlow=a
|
||||
end
|
||||
|
||||
def m_json_encode
|
||||
x = source "a"
|
||||
sink ActiveSupport::JSON.encode(x) # $hasTaintFlow=a
|
||||
end
|
||||
|
||||
def m_json_decode
|
||||
x = source "a"
|
||||
sink ActiveSupport::JSON.decode(x) # $hasTaintFlow=a
|
||||
end
|
||||
|
||||
def m_json_dump
|
||||
x = source "a"
|
||||
sink ActiveSupport::JSON.dump(x) # $hasTaintFlow=a
|
||||
end
|
||||
|
||||
def m_json_load
|
||||
x = source "a"
|
||||
sink ActiveSupport::JSON.load(x) # $hasTaintFlow=a
|
||||
end
|
||||
|
||||
def m_to_json
|
||||
x = source "a"
|
||||
y = [x]
|
||||
sink x.to_json # $hasTaintFlow=a
|
||||
sink y.to_json # $hasTaintFlow=a
|
||||
end
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
failures
|
||||
edges
|
||||
| json.rb:1:17:1:26 | call to source : | json.rb:1:6:1:27 | call to parse |
|
||||
| json.rb:2:18:2:27 | call to source : | json.rb:2:6:2:28 | call to parse! |
|
||||
| json.rb:3:16:3:25 | call to source : | json.rb:3:6:3:26 | call to load |
|
||||
| json.rb:4:19:4:28 | call to source : | json.rb:4:6:4:29 | call to restore |
|
||||
| json.rb:6:20:6:29 | call to source : | json.rb:6:6:6:30 | call to generate |
|
||||
| json.rb:7:25:7:34 | call to source : | json.rb:7:6:7:35 | call to fast_generate |
|
||||
| json.rb:8:27:8:36 | call to source : | json.rb:8:6:8:37 | call to pretty_generate |
|
||||
| json.rb:9:16:9:25 | call to source : | json.rb:9:6:9:26 | call to dump |
|
||||
| json.rb:10:19:10:28 | call to source : | json.rb:10:6:10:29 | call to unparse |
|
||||
| json.rb:11:24:11:33 | call to source : | json.rb:11:6:11:34 | call to fast_unparse |
|
||||
nodes
|
||||
| json.rb:1:6:1:27 | call to parse | semmle.label | call to parse |
|
||||
| json.rb:1:17:1:26 | call to source : | semmle.label | call to source : |
|
||||
| json.rb:2:6:2:28 | call to parse! | semmle.label | call to parse! |
|
||||
| json.rb:2:18:2:27 | call to source : | semmle.label | call to source : |
|
||||
| json.rb:3:6:3:26 | call to load | semmle.label | call to load |
|
||||
| json.rb:3:16:3:25 | call to source : | semmle.label | call to source : |
|
||||
| json.rb:4:6:4:29 | call to restore | semmle.label | call to restore |
|
||||
| json.rb:4:19:4:28 | call to source : | semmle.label | call to source : |
|
||||
| json.rb:6:6:6:30 | call to generate | semmle.label | call to generate |
|
||||
| json.rb:6:20:6:29 | call to source : | semmle.label | call to source : |
|
||||
| json.rb:7:6:7:35 | call to fast_generate | semmle.label | call to fast_generate |
|
||||
| json.rb:7:25:7:34 | call to source : | semmle.label | call to source : |
|
||||
| json.rb:8:6:8:37 | call to pretty_generate | semmle.label | call to pretty_generate |
|
||||
| json.rb:8:27:8:36 | call to source : | semmle.label | call to source : |
|
||||
| json.rb:9:6:9:26 | call to dump | semmle.label | call to dump |
|
||||
| json.rb:9:16:9:25 | call to source : | semmle.label | call to source : |
|
||||
| json.rb:10:6:10:29 | call to unparse | semmle.label | call to unparse |
|
||||
| json.rb:10:19:10:28 | call to source : | semmle.label | call to source : |
|
||||
| json.rb:11:6:11:34 | call to fast_unparse | semmle.label | call to fast_unparse |
|
||||
| json.rb:11:24:11:33 | call to source : | semmle.label | call to source : |
|
||||
subpaths
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import TestUtilities.InlineFlowTest
|
||||
import codeql.ruby.Frameworks
|
||||
import PathGraph
|
||||
11
ruby/ql/test/library-tests/frameworks/json/json.rb
Normal file
11
ruby/ql/test/library-tests/frameworks/json/json.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
sink JSON.parse(source "a") # $hasTaintFlow=a
|
||||
sink JSON.parse!(source "a") # $hasTaintFlow=a
|
||||
sink JSON.load(source "a") # $hasTaintFlow=a
|
||||
sink JSON.restore(source "a") # $hasTaintFlow=a
|
||||
|
||||
sink JSON.generate(source "a") # $hasTaintFlow=a
|
||||
sink JSON.fast_generate(source "a") # $hasTaintFlow=a
|
||||
sink JSON.pretty_generate(source "a") # $hasTaintFlow=a
|
||||
sink JSON.dump(source "a") # $hasTaintFlow=a
|
||||
sink JSON.unparse(source "a") # $hasTaintFlow=a
|
||||
sink JSON.fast_unparse(source "a") # $hasTaintFlow=a
|
||||
Reference in New Issue
Block a user