Merge branch 'main' into amammad-ruby-YAMLunsafeLoad

This commit is contained in:
Arthur Baars
2023-06-06 12:09:06 +02:00
committed by GitHub
628 changed files with 18100 additions and 5285 deletions

View File

@@ -1,3 +1,9 @@
## 0.6.2
### Minor Analysis Improvements
* Support for the `sqlite3` gem has been added. Method calls that execute queries against an SQLite3 database that may be vulnerable to injection attacks will now be recognized.
## 0.6.1
No user-facing changes.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Support for the `mysql2` gem has been added. Method calls that execute queries against an MySQL database that may be vulnerable to injection attacks will now be recognized.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Support for the `sequel` gem has been added. Method calls that execute queries against a database that may be vulnerable to injection attacks will now be recognized.

View File

@@ -1,4 +1,5 @@
---
category: minorAnalysis
---
## 0.6.2
### Minor Analysis Improvements
* Support for the `sqlite3` gem has been added. Method calls that execute queries against an SQLite3 database that may be vulnerable to injection attacks will now be recognized.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.1
lastReleaseVersion: 0.6.2

View File

@@ -78,6 +78,19 @@ module SqlExecution {
}
}
/**
* A data-flow node that performs SQL sanitization.
*/
class SqlSanitization extends DataFlow::Node instanceof SqlSanitization::Range { }
/** Provides a class for modeling new SQL sanitization APIs. */
module SqlSanitization {
/**
* A data-flow node that performs SQL sanitization.
*/
abstract class Range extends DataFlow::Node { }
}
/**
* A data-flow node that executes a regular expression.
*

View File

@@ -32,5 +32,7 @@ private import codeql.ruby.frameworks.Slim
private import codeql.ruby.frameworks.Sinatra
private import codeql.ruby.frameworks.Twirp
private import codeql.ruby.frameworks.Sqlite3
private import codeql.ruby.frameworks.Mysql2
private import codeql.ruby.frameworks.Pg
private import codeql.ruby.frameworks.Yaml
private import codeql.ruby.frameworks.Sequel

View File

@@ -166,28 +166,21 @@ module Public {
SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) }
}
private predicate noComponentSpecific(SummaryComponent sc) {
not exists(getComponentSpecific(sc))
}
/** Gets a textual representation of this component used for flow summaries. */
private string getComponent(SummaryComponent sc) {
result = getComponentSpecific(sc)
or
noComponentSpecific(sc) and
(
exists(ArgumentPosition pos |
sc = TParameterSummaryComponent(pos) and
result = "Parameter[" + getArgumentPosition(pos) + "]"
)
or
exists(ParameterPosition pos |
sc = TArgumentSummaryComponent(pos) and
result = "Argument[" + getParameterPosition(pos) + "]"
)
or
sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
exists(ArgumentPosition pos |
sc = TParameterSummaryComponent(pos) and
result = "Parameter[" + getArgumentPosition(pos) + "]"
)
or
exists(ParameterPosition pos |
sc = TArgumentSummaryComponent(pos) and
result = "Argument[" + getParameterPosition(pos) + "]"
)
or
sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
}
/** Gets a textual representation of this stack used for flow summaries. */

View File

@@ -0,0 +1,73 @@
/**
* Provides modeling for mysql2, a Ruby library (gem) for interacting with MySql databases.
*/
private import codeql.ruby.ApiGraphs
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.Concepts
/**
* Provides modeling for mysql2, a Ruby library (gem) for interacting with MySql databases.
*/
module Mysql2 {
/**
* Flow summary for `Mysql2::Client.new()`.
*/
private class SqlSummary extends SummarizedCallable {
SqlSummary() { this = "Mysql2::Client.new()" }
override MethodCall getACall() { result = any(Mysql2Connection c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
/** A call to Mysql2::Client.new() is used to establish a connection to a MySql database. */
private class Mysql2Connection extends DataFlow::CallNode {
Mysql2Connection() {
this = API::getTopLevelMember("Mysql2").getMember("Client").getAnInstantiation()
}
}
/** A call that executes SQL statements against a MySQL database. */
private class Mysql2Execution extends SqlExecution::Range, DataFlow::CallNode {
private DataFlow::Node query;
Mysql2Execution() {
exists(Mysql2Connection mysql2Connection |
this = mysql2Connection.getAMethodCall("query") and query = this.getArgument(0)
or
exists(DataFlow::CallNode prepareCall |
prepareCall = mysql2Connection.getAMethodCall("prepare") and
query = prepareCall.getArgument(0) and
this = prepareCall.getAMethodCall("execute")
)
)
}
override DataFlow::Node getSql() { result = query }
}
/**
* A call to `Mysql2::Client.escape`, considered as a sanitizer for SQL statements.
*/
private class Mysql2EscapeSanitization extends SqlSanitization::Range {
Mysql2EscapeSanitization() {
this = API::getTopLevelMember("Mysql2").getMember("Client").getAMethodCall("escape")
}
}
/**
* Flow summary for `Mysql2::Client.escape()`.
*/
private class EscapeSummary extends SummarizedCallable {
EscapeSummary() { this = "Mysql2::Client.escape()" }
override MethodCall getACall() { result = any(Mysql2EscapeSanitization c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
}

View File

@@ -0,0 +1,71 @@
/**
* Provides modeling for `Sequel`, the database toolkit for Ruby.
* https://github.com/jeremyevans/sequel
*/
private import ruby
private import codeql.ruby.ApiGraphs
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.Concepts
/**
* Provides modeling for `Sequel`, the database toolkit for Ruby.
* https://github.com/jeremyevans/sequel
*/
module Sequel {
/** Flow Summary for `Sequel`. */
private class SqlSummary extends SummarizedCallable {
SqlSummary() { this = "Sequel.connect" }
override MethodCall getACall() { result = any(SequelConnection c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
/** A call to establish a connection to a database */
private class SequelConnection extends DataFlow::CallNode {
SequelConnection() {
this =
API::getTopLevelMember("Sequel").getAMethodCall(["connect", "sqlite", "mysql2", "jdbc"])
}
}
/** A call that constructs SQL statements */
private class SequelConstruction extends SqlConstruction::Range, DataFlow::CallNode {
DataFlow::Node query;
SequelConstruction() {
this = API::getTopLevelMember("Sequel").getAMethodCall("cast") and query = this.getArgument(1)
or
this = API::getTopLevelMember("Sequel").getAMethodCall("function") and
query = this.getArgument(0)
}
override DataFlow::Node getSql() { result = query }
}
/** A call that executes SQL statements against a database */
private class SequelExecution extends SqlExecution::Range, DataFlow::CallNode {
SequelExecution() {
exists(SequelConnection sequelConnection |
this =
sequelConnection
.getAMethodCall([
"execute", "execute_ddl", "execute_dui", "execute_insert", "run", "<<", "fetch",
"fetch_rows", "[]", "log_connection_yield"
]) or
this =
sequelConnection
.getAMethodCall("dataset")
.getAMethodCall([
"with_sql", "with_sql_all", "with_sql_delete", "with_sql_each", "with_sql_first",
"with_sql_insert", "with_sql_single_value", "with_sql_update"
])
)
}
override DataFlow::Node getSql() { result = this.getArgument(0) }
}
}

View File

@@ -77,4 +77,26 @@ module Sqlite3 {
override DataFlow::Node getSql() { result = this.getArgument(0) }
}
/**
* A call to `SQLite3::Database.quote`, considered as a sanitizer for SQL statements.
*/
private class SQLite3QuoteSanitization extends SqlSanitization {
SQLite3QuoteSanitization() {
this = API::getTopLevelMember("SQLite3").getMember("Database").getAMethodCall("quote")
}
}
/**
* Flow summary for `SQLite3::Database.quote()`.
*/
private class QuoteSummary extends SummarizedCallable {
QuoteSummary() { this = "SQLite3::Database.quote()" }
override MethodCall getACall() { result = any(SQLite3QuoteSanitization c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
}

View File

@@ -7,6 +7,7 @@ private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.ApiGraphs
/**
* Provides default sources, sinks and sanitizers for detecting SQL injection
@@ -53,4 +54,6 @@ module SqlInjection {
class StringConstArrayInclusionCallAsSanitizer extends Sanitizer,
StringConstArrayInclusionCallBarrier
{ }
private class SqlSanitizationAsSanitizer extends Sanitizer, SqlSanitization { }
}

View File

@@ -0,0 +1,22 @@
/**
* @name Print CFG
* @description Produces a representation of a file's Control Flow Graph.
* This query is used by the VS Code extension.
* @id rb/print-cfg
* @kind graph
* @tags ide-contextual-queries/print-cfg
*/
private import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared::TestOutput
private import codeql.IDEContextual
/**
* Gets the source file to generate a CFG from.
*/
external string selectedSourceFile();
class MyRelevantNode extends RelevantNode {
MyRelevantNode() {
this.getScope().getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile())
}
}

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-all
version: 0.6.2-dev
version: 0.6.3-dev
groups: ruby
extractor: ruby
dbscheme: ruby.dbscheme

View File

@@ -1,3 +1,7 @@
## 0.6.2
No user-facing changes.
## 0.6.1
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.6.2
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.1
lastReleaseVersion: 0.6.2

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-queries
version: 0.6.2-dev
version: 0.6.3-dev
groups:
- ruby
- queries

View File

@@ -4,8 +4,7 @@
<example>
<p>Consider this regular expression:</p>
<sample language="ruby">
/^_(__|.)+_$/
</sample>
/^_(__|.)+_$/</sample>
<p>
Its sub-expression <code>"(__|.)+?"</code> can match the string
<code>"__"</code> either by the first alternative <code>"__"</code> to the
@@ -21,8 +20,7 @@
repetition:
</p>
<sample language="ruby">
/^_(__|[^_])+_$/
</sample>
/^_(__|[^_])+_$/</sample>
</example>
<include src="ReDoSReferences.inc.qhelp"/>
</qhelp>

View File

@@ -11,7 +11,7 @@
* select sink, source, sink, "$@", source, source.toString()
* ```
*
* To declare expecations, you can use the $hasTaintFlow or $hasValueFlow comments within the test source files.
* To declare expectations, you can use the $hasTaintFlow or $hasValueFlow comments within the test source files.
* Example of the corresponding test file, e.g. test.rb
* ```rb
* s = source(1)

View File

@@ -2814,7 +2814,11 @@
| file://:0:0:0:0 | parameter position 0 of File.realdirpath | file://:0:0:0:0 | [summary] to write: return (return) in File.realdirpath |
| file://:0:0:0:0 | parameter position 0 of File.realpath | file://:0:0:0:0 | [summary] to write: return (return) in File.realpath |
| file://:0:0:0:0 | parameter position 0 of Hash[] | file://:0:0:0:0 | [summary] read: argument position 0.any element in Hash[] |
| file://:0:0:0:0 | parameter position 0 of Mysql2::Client.escape() | file://:0:0:0:0 | [summary] to write: return (return) in Mysql2::Client.escape() |
| file://:0:0:0:0 | parameter position 0 of Mysql2::Client.new() | file://:0:0:0:0 | [summary] to write: return (return) in Mysql2::Client.new() |
| file://:0:0:0:0 | parameter position 0 of PG.new() | file://:0:0:0:0 | [summary] to write: return (return) in PG.new() |
| file://:0:0:0:0 | parameter position 0 of SQLite3::Database.quote() | file://:0:0:0:0 | [summary] to write: return (return) in SQLite3::Database.quote() |
| file://:0:0:0:0 | parameter position 0 of Sequel.connect | file://:0:0:0:0 | [summary] to write: return (return) in Sequel.connect |
| file://:0:0:0:0 | parameter position 0 of String.try_convert | file://:0:0:0:0 | [summary] to write: return (return) in String.try_convert |
| file://:0:0:0:0 | parameter position 0 of \| | file://:0:0:0:0 | [summary] read: argument position 0.any element in \| |
| file://:0:0:0:0 | parameter position 1.. of File.join | file://:0:0:0:0 | [summary] to write: return (return) in File.join |

View File

@@ -0,0 +1,5 @@
| Mysql2.rb:10:16:10:48 | call to query | Mysql2.rb:10:27:10:47 | "SELECT * FROM users" |
| Mysql2.rb:13:16:13:73 | call to query | Mysql2.rb:13:27:13:72 | "SELECT * FROM users WHERE use..." |
| Mysql2.rb:17:16:17:76 | call to query | Mysql2.rb:17:27:17:75 | "SELECT * FROM users WHERE use..." |
| Mysql2.rb:21:16:21:57 | call to execute | Mysql2.rb:20:31:20:82 | "SELECT * FROM users WHERE id ..." |
| Mysql2.rb:25:16:25:60 | call to execute | Mysql2.rb:24:31:24:93 | "SELECT * FROM users WHERE use..." |

View File

@@ -0,0 +1,5 @@
private import codeql.ruby.DataFlow
private import codeql.ruby.Concepts
private import codeql.ruby.frameworks.Mysql2
query predicate mysql2SqlExecution(SqlExecution e, DataFlow::Node sql) { sql = e.getSql() }

View File

@@ -0,0 +1,30 @@
class UsersController < ActionController::Base
def mysql2_handler(event:, context:)
name = params[:user_name]
conn = Mysql2::Client.new(
host: "127.0.0.1",
username: "root"
)
# GOOD: SQL statement is not constructed from user input
results1 = conn.query("SELECT * FROM users")
# BAD: SQL statement constructed from user input
results2 = conn.query("SELECT * FROM users WHERE username='#{name}'")
# GOOD: user input is escaped
escaped = Mysql2::Client.escape(name)
results3 = conn.query("SELECT * FROM users WHERE username='#{escaped}'")
# GOOD: user input is escaped
statement1 = conn.prepare("SELECT * FROM users WHERE id >= ? AND username = ?")
results4 = statement1.execute(1, name, :as => :array)
# BAD: SQL statement constructed from user input
statement2 = conn.prepare("SELECT * FROM users WHERE username='#{name}' AND password = ?")
results4 = statement2.execute("password", :as => :array)
# NOT EXECUTED
statement3 = conn.prepare("SELECT * FROM users WHERE username = ?")
end
end

View File

@@ -0,0 +1,23 @@
sequelSqlConstruction
| sequel.rb:62:29:62:49 | call to cast | sequel.rb:62:45:62:48 | name |
| sequel.rb:65:29:65:49 | call to function | sequel.rb:65:45:65:48 | name |
sequelSqlExecution
| sequel.rb:9:9:9:60 | ...[...] | sequel.rb:9:14:9:59 | "SELECT * FROM users WHERE use..." |
| sequel.rb:12:9:12:64 | call to run | sequel.rb:12:18:12:63 | "SELECT * FROM users WHERE use..." |
| sequel.rb:15:9:17:11 | call to fetch | sequel.rb:15:20:15:65 | "SELECT * FROM users WHERE use..." |
| sequel.rb:20:9:20:65 | ...[...] | sequel.rb:20:14:20:64 | "SELECT * FROM users WHERE use..." |
| sequel.rb:23:9:23:65 | call to execute | sequel.rb:23:22:23:65 | "SELECT * FROM users WHERE use..." |
| sequel.rb:26:9:26:71 | call to execute_ddl | sequel.rb:26:26:26:71 | "SELECT * FROM users WHERE use..." |
| sequel.rb:29:9:29:71 | call to execute_dui | sequel.rb:29:26:29:71 | "SELECT * FROM users WHERE use..." |
| sequel.rb:32:9:32:74 | call to execute_insert | sequel.rb:32:29:32:74 | "SELECT * FROM users WHERE use..." |
| sequel.rb:35:9:35:62 | ... << ... | sequel.rb:35:17:35:62 | "SELECT * FROM users WHERE use..." |
| sequel.rb:38:9:38:79 | call to fetch_rows | sequel.rb:38:25:38:70 | "SELECT * FROM users WHERE use..." |
| sequel.rb:41:9:41:81 | call to with_sql_all | sequel.rb:41:35:41:80 | "SELECT * FROM users WHERE use..." |
| sequel.rb:44:9:44:84 | call to with_sql_delete | sequel.rb:44:38:44:83 | "SELECT * FROM users WHERE use..." |
| sequel.rb:47:9:47:90 | call to with_sql_each | sequel.rb:47:36:47:81 | "SELECT * FROM users WHERE use..." |
| sequel.rb:50:9:50:83 | call to with_sql_first | sequel.rb:50:37:50:82 | "SELECT * FROM users WHERE use..." |
| sequel.rb:53:9:53:84 | call to with_sql_insert | sequel.rb:53:38:53:83 | "SELECT * FROM users WHERE use..." |
| sequel.rb:56:9:56:90 | call to with_sql_single_value | sequel.rb:56:44:56:89 | "SELECT * FROM users WHERE use..." |
| sequel.rb:59:9:59:84 | call to with_sql_update | sequel.rb:59:38:59:83 | "SELECT * FROM users WHERE use..." |
| sequel.rb:62:9:62:20 | ...[...] | sequel.rb:62:14:62:19 | :table |
| sequel.rb:65:9:65:20 | ...[...] | sequel.rb:65:14:65:19 | :table |

View File

@@ -0,0 +1,7 @@
private import codeql.ruby.DataFlow
private import codeql.ruby.Concepts
private import codeql.ruby.frameworks.Sequel
query predicate sequelSqlConstruction(SqlConstruction c, DataFlow::Node sql) { sql = c.getSql() }
query predicate sequelSqlExecution(SqlExecution e, DataFlow::Node sql) { sql = e.getSql() }

View File

@@ -0,0 +1,67 @@
require 'sequel'
class UsersController < ActionController::Base
def sequel_handler(event:, context:)
name = params[:name]
conn = Sequel.sqlite("sqlite://example.db")
# BAD: SQL statement constructed from user input
conn["SELECT * FROM users WHERE username='#{name}'"]
# BAD: SQL statement constructed from user input
conn.run("SELECT * FROM users WHERE username='#{name}'")
# BAD: SQL statement constructed from user input
conn.fetch("SELECT * FROM users WHERE username='#{name}'") do |row|
puts row[:name]
end
# GOOD: SQL statement is not constructed from user input
conn["SELECT * FROM users WHERE username='im_not_input'"]
# BAD: SQL statement constructed from user input
conn.execute "SELECT * FROM users WHERE username=#{name}"
# BAD: SQL statement constructed from user input
conn.execute_ddl "SELECT * FROM users WHERE username='#{name}'"
# BAD: SQL statement constructed from user input
conn.execute_dui "SELECT * FROM users WHERE username='#{name}'"
# BAD: SQL statement constructed from user input
conn.execute_insert "SELECT * FROM users WHERE username='#{name}'"
# BAD: SQL statement constructed from user input
conn << "SELECT * FROM users WHERE username='#{name}'"
# BAD: SQL statement constructed from user input
conn.fetch_rows("SELECT * FROM users WHERE username='#{name}'"){|row| }
# BAD: SQL statement constructed from user input
conn.dataset.with_sql_all("SELECT * FROM users WHERE username='#{name}'")
# BAD: SQL statement constructed from user input
conn.dataset.with_sql_delete("SELECT * FROM users WHERE username='#{name}'")
# BAD: SQL statement constructed from user input
conn.dataset.with_sql_each("SELECT * FROM users WHERE username='#{name}'"){|row| }
# BAD: SQL statement constructed from user input
conn.dataset.with_sql_first("SELECT * FROM users WHERE username='#{name}'")
# BAD: SQL statement constructed from user input
conn.dataset.with_sql_insert("SELECT * FROM users WHERE username='#{name}'")
# BAD: SQL statement constructed from user input
conn.dataset.with_sql_single_value("SELECT * FROM users WHERE username='#{name}'")
# BAD: SQL statement constructed from user input
conn.dataset.with_sql_update("SELECT * FROM users WHERE username='#{name}'")
# BAD: SQL statement constructed from user input
conn[:table].select(Sequel.cast(:a, name))
# BAD: SQL statement constructed from user input
conn[:table].select(Sequel.function(name))
end
end

View File

@@ -40,6 +40,9 @@ edges
| PolynomialReDoS.rb:70:12:70:24 | ...[...] | PolynomialReDoS.rb:70:5:70:8 | name |
| PolynomialReDoS.rb:73:32:73:35 | name | PolynomialReDoS.rb:76:35:76:39 | input |
| PolynomialReDoS.rb:76:35:76:39 | input | PolynomialReDoS.rb:77:5:77:9 | input |
| PolynomialReDoS.rb:103:5:103:8 | name | PolynomialReDoS.rb:105:5:105:8 | name |
| PolynomialReDoS.rb:103:12:103:17 | call to params | PolynomialReDoS.rb:103:12:103:24 | ...[...] |
| PolynomialReDoS.rb:103:12:103:24 | ...[...] | PolynomialReDoS.rb:103:5:103:8 | name |
| lib/index.rb:2:11:2:11 | x | lib/index.rb:4:13:4:13 | x |
| lib/index.rb:8:13:8:13 | x | lib/index.rb:9:15:9:15 | x |
| lib/index.rb:8:13:8:13 | x | lib/index.rb:11:16:11:16 | x |
@@ -91,6 +94,10 @@ nodes
| PolynomialReDoS.rb:73:32:73:35 | name | semmle.label | name |
| PolynomialReDoS.rb:76:35:76:39 | input | semmle.label | input |
| PolynomialReDoS.rb:77:5:77:9 | input | semmle.label | input |
| PolynomialReDoS.rb:103:5:103:8 | name | semmle.label | name |
| PolynomialReDoS.rb:103:12:103:17 | call to params | semmle.label | call to params |
| PolynomialReDoS.rb:103:12:103:24 | ...[...] | semmle.label | ...[...] |
| PolynomialReDoS.rb:105:5:105:8 | name | semmle.label | name |
| lib/index.rb:2:11:2:11 | x | semmle.label | x |
| lib/index.rb:4:13:4:13 | x | semmle.label | x |
| lib/index.rb:8:13:8:13 | x | semmle.label | x |
@@ -121,6 +128,8 @@ subpaths
| PolynomialReDoS.rb:62:5:62:22 | call to gsub | PolynomialReDoS.rb:54:12:54:17 | call to params | PolynomialReDoS.rb:62:5:62:9 | input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:56:31:56:33 | \\s+ | regular expression | PolynomialReDoS.rb:54:12:54:17 | call to params | user-provided value |
| PolynomialReDoS.rb:66:5:66:34 | call to match? | PolynomialReDoS.rb:54:12:54:17 | call to params | PolynomialReDoS.rb:66:5:66:9 | input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:58:30:58:32 | \\s+ | regular expression | PolynomialReDoS.rb:54:12:54:17 | call to params | user-provided value |
| PolynomialReDoS.rb:77:5:77:22 | call to gsub | PolynomialReDoS.rb:70:12:70:17 | call to params | PolynomialReDoS.rb:77:5:77:9 | input | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:72:28:72:30 | \\s+ | regular expression | PolynomialReDoS.rb:70:12:70:17 | call to params | user-provided value |
| PolynomialReDoS.rb:105:5:105:23 | ... =~ ... | PolynomialReDoS.rb:103:12:103:17 | call to params | PolynomialReDoS.rb:105:5:105:8 | name | This $@ that depends on a $@ may run slow on strings starting with '''' and with many repetitions of ' '. | PolynomialReDoS.rb:100:397:100:399 | \\s* | regular expression | PolynomialReDoS.rb:103:12:103:17 | call to params | user-provided value |
| PolynomialReDoS.rb:105:5:105:23 | ... =~ ... | PolynomialReDoS.rb:103:12:103:17 | call to params | PolynomialReDoS.rb:105:5:105:8 | name | This $@ that depends on a $@ may run slow on strings starting with '''' and with many repetitions of ' '. | PolynomialReDoS.rb:100:405:100:407 | \\s* | regular expression | PolynomialReDoS.rb:103:12:103:17 | call to params | user-provided value |
| lib/index.rb:4:13:4:26 | call to match | lib/index.rb:2:11:2:11 | x | lib/index.rb:4:13:4:13 | x | This $@ that depends on a $@ may run slow on strings with many repetitions of 'a'. | lib/index.rb:4:22:4:23 | a+ | regular expression | lib/index.rb:2:11:2:11 | x | library input |
| lib/index.rb:9:15:9:28 | call to match | lib/index.rb:8:13:8:13 | x | lib/index.rb:9:15:9:15 | x | This $@ that depends on a $@ may run slow on strings with many repetitions of 'a'. | lib/index.rb:9:24:9:25 | a+ | regular expression | lib/index.rb:8:13:8:13 | x | library input |
| lib/index.rb:11:16:11:276 | call to match | lib/index.rb:8:13:8:13 | x | lib/index.rb:11:16:11:16 | x | This $@ that depends on a $@ may run slow on strings starting with 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC' and with many repetitions of 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC'. | lib/index.rb:11:271:11:272 | .* | regular expression | lib/index.rb:8:13:8:13 | x | library input |

View File

@@ -76,4 +76,32 @@ class FooController < ActionController::Base
def re_compile_indirect_2 (reg, input)
input.gsub reg, '' # NOT GOOD
end
# See https://github.com/dependabot/dependabot-core/blob/37dc1767fde9b7184020763f4d0c1434f93d11d6/python/lib/dependabot/python/requirement_parser.rb#L6-L25
NAME = /[a-zA-Z0-9](?:[a-zA-Z0-9\-_\.]*[a-zA-Z0-9])?/
EXTRA = /[a-zA-Z0-9\-_\.]+/
COMPARISON = /===|==|>=|<=|<|>|~=|!=/
VERSION = /([1-9][0-9]*!)?[0-9]+[a-zA-Z0-9\-_.*]*(\+[0-9a-zA-Z]+(\.[0-9a-zA-Z]+)*)?/
REQUIREMENT = /(?<comparison>#{COMPARISON})\s*\\?\s*(?<version>#{VERSION})/
HASH = /--hash=(?<algorithm>.*?):(?<hash>.*?)(?=\s|\\|$)/
REQUIREMENTS = /#{REQUIREMENT}(\s*,\s*\\?\s*#{REQUIREMENT})*/
HASHES = /#{HASH}(\s*\\?\s*#{HASH})*/
MARKER_OP = /\s*(#{COMPARISON}|(\s*in)|(\s*not\s*in))/
PYTHON_STR_C = %r{[a-zA-Z0-9\s\(\)\.\{\}\-_\*#:;/\?\[\]!~`@\$%\^&=\+\|<>]}
PYTHON_STR = /('(#{PYTHON_STR_C}|")*'|"(#{PYTHON_STR_C}|')*")/
ENV_VAR =
/python_version|python_full_version|os_name|sys_platform|
platform_release|platform_system|platform_version|platform_machine|
platform_python_implementation|implementation_name|
implementation_version/
MARKER_VAR = /\s*(#{ENV_VAR}|#{PYTHON_STR})/
MARKER_EXPR_ONE = /#{MARKER_VAR}#{MARKER_OP}#{MARKER_VAR}/
MARKER_EXPR = /(#{MARKER_EXPR_ONE}|\(\s*|\s*\)|\s+and\s+|\s+or\s+)+/
def use_marker_expr
name = params[:name] # source
name =~ MARKER_EXPR
end
end