Compare commits

..

2 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
7ec64167ac Convert Ruby qlref tests to inline expectations 2026-06-11 22:32:56 +00:00
copilot-swe-agent[bot]
de281fc00c Initial plan 2026-06-11 22:21:25 +00:00
143 changed files with 1034 additions and 977 deletions

View File

@@ -1 +1,2 @@
experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql
query: experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,27 +1,27 @@
require 'zlib'
class TestController < ActionController::Base
gzip_path = params[:path]
gzip_path = params[:path] # $ Source
Zlib::GzipReader.open(gzip_path).read
Zlib::GzipReader.open(gzip_path).read # $ Alert
Zlib::GzipReader.open(gzip_path) do |uncompressedfile|
puts uncompressedfile.read
end
end # $ Alert
Zlib::GzipReader.open(gzip_path) do |uncompressedfile|
uncompressedfile.each do |entry|
puts entry
end
end
uncompressedfile = Zlib::GzipReader.open(gzip_path)
end # $ Alert
uncompressedfile = Zlib::GzipReader.open(gzip_path) # $ Alert
uncompressedfile.each do |entry|
puts entry
end
Zlib::GzipReader.new(File.open(gzip_path, 'rb')).read
Zlib::GzipReader.new(File.open(gzip_path, 'rb')).each do |entry|
Zlib::GzipReader.new(File.open(gzip_path, 'rb')).read # $ Alert
Zlib::GzipReader.new(File.open(gzip_path, 'rb')).each do |entry| # $ Alert
puts entry
end
Zlib::GzipReader.zcat(open(gzip_path))
Zlib::GzipReader.zcat(open(gzip_path)) # $ Alert
end

View File

@@ -1,21 +1,21 @@
require 'zip'
class TestController < ActionController::Base
zipfile_path = params[:path]
zipfile_path = params[:path] # $ Source
Zip::InputStream.open(zipfile_path) do |input|
while (entry = input.get_next_entry)
puts :file_name, entry.name
input
end
end
end # $ Alert
Zip::InputStream.open(zipfile_path) do |input|
input.read
end
input = Zip::InputStream.open(zipfile_path)
end # $ Alert
input = Zip::InputStream.open(zipfile_path) # $ Alert
Zip::File.open(zipfile_path).read "10GB"
Zip::File.open(zipfile_path).extract "10GB", "./"
Zip::File.open(zipfile_path).read "10GB" # $ Alert
Zip::File.open(zipfile_path).extract "10GB", "./" # $ Alert
Zip::File.open(zipfile_path) do |zip_file|
# Handle entries one by one
@@ -25,33 +25,33 @@ class TestController < ActionController::Base
# Extract to file or directory based on name in the archive
entry.extract
# Read into memory
entry.get_input_stream.read
entry.get_input_stream.read # $ Alert
end
end
zip_file = Zip::File.open(zipfile_path)
zip_file.each do |entry|
entry.extract
entry.get_input_stream.read
entry.extract # $ Alert
entry.get_input_stream.read # $ Alert
end
# Find specific entry
Zip::File.open(zipfile_path) do |zip_file|
zip_file.glob('*.xml').each do |entry|
zip_file.read(entry.name)
entry.extract
zip_file.read(entry.name) # $ Alert
entry.extract # $ Alert
end
entry = zip_file.glob('*.csv').first
raise 'File too large when extracted' if entry.size > MAX_SIZE
puts entry.get_input_stream.read
puts entry.get_input_stream.read # $ Alert
end
zip_file = Zip::File.open(zipfile_path)
entry = zip_file.glob('*.csv')
puts entry.get_input_stream.read
puts entry.get_input_stream.read # $ Alert
zip_file = Zip::File.open(zipfile_path)
zip_file.glob('*') do |entry|
entry.get_input_stream.read
entry.get_input_stream.read # $ Alert
end
end

View File

@@ -1 +1,2 @@
experimental/ldap-improper-auth/ImproperLdapAuth.ql
query: experimental/ldap-improper-auth/ImproperLdapAuth.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,7 +2,7 @@ class FooController < ActionController::Base
def some_request_handler
# A string tainted by user input is used directly as password
# (i.e a remote flow source)
pass = params[:pass]
pass = params[:pass] # $ Source
# BAD: user input is not sanitized
ldap = Net::LDAP.new(
@@ -12,7 +12,7 @@ class FooController < ActionController::Base
auth: {
method: :simple,
username: 'uid=admin,dc=example,dc=com',
password: pass
password: pass # $ Alert
}
)
ldap.bind
@@ -21,14 +21,14 @@ class FooController < ActionController::Base
def some_request_handler
# A string tainted by user input is used directly as password
# (i.e a remote flow source)
pass = params[:pass]
pass = params[:pass] # $ Source
# BAD: user input is not sanitized
ldap = Net::LDAP.new
ldap.host = your_server_ip_address
ldap.encryption(:method => :simple_tls)
ldap.port = 639
ldap.auth "admin", pass
ldap.auth "admin", pass # $ Alert
ldap.bind
end
end
@@ -56,4 +56,4 @@ class BarController < ApplicationController
}
)
end
end
end

View File

@@ -1 +1,2 @@
experimental/insecure-randomness/InsecureRandomness.ql
query: experimental/insecure-randomness/InsecureRandomness.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -3,7 +3,7 @@ require 'securerandom'
def generate_password_1(length)
chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a + ['!', '@', '#', '$', '%']
# BAD: rand is not cryptographically secure
password = (1..length).collect { chars[rand(chars.size)] }.join
password = (1..length).collect { chars[rand(chars.size)] }.join # $ Alert
end
def generate_password_2(length)
@@ -16,4 +16,4 @@ def generate_password_2(length)
end
password = generate_password_1(10)
password = generate_password_2(10)
password = generate_password_2(10)

View File

@@ -2,11 +2,11 @@ class FooController < ActionController::Base
def some_request_handler
# A string tainted by user input is used directly as DN
# (i.e a remote flow source)
dc = params[:dc]
dc = params[:dc] # $ Source
# A string tainted by user input is used directly as search filter or attribute
# (i.e a remote flow source)
name = params[:user_name]
name = params[:user_name] # $ Source
# LDAP Connection
ldap = Net::LDAP.new(
@@ -22,20 +22,20 @@ class FooController < ActionController::Base
# BAD: user input is used as DN
# where dc is unsanitized
ldap.search(base: "ou=people,dc=#{dc},dc=com", filter: "cn=George", attributes: [""])
ldap.search(base: "ou=people,dc=#{dc},dc=com", filter: "cn=George", attributes: [""]) # $ Alert
# BAD: user input is used as search filter
# where name is unsanitized
ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=#{name}", attributes: [""])
ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=#{name}", attributes: [""]) # $ Alert
# BAD: user input is used as attribute
# where name is unsanitized
ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=George", attributes: [name])
ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=George", attributes: [name]) # $ Alert
# BAD: user input is used as search filter
# where name is unsanitized
filter = Net::LDAP::Filter.eq('cn', name)
ldap.search(base: "ou=people,dc=example,dc=com", filter: filter, attributes: [""])
ldap.search(base: "ou=people,dc=example,dc=com", filter: filter, attributes: [""]) # $ Alert
# GOOD: user input is not used in the LDAP query
result = ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=George", attributes: [""])
@@ -63,4 +63,4 @@ class BarController < ApplicationController
end
result = ldap.search(base: "ou=people,dc=example,dc=com", filter: "cn=#{name}", attributes: [""])
end
end
end

View File

@@ -1 +1,2 @@
experimental/ldap-injection/LdapInjection.ql
query: experimental/ldap-injection/LdapInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,7 +2,7 @@ class FooController < ActionController::Base
def some_request_handler
# A string tainted by user input is inserted into a template
# (i.e a remote flow source)
name = params[:name]
name = params[:name] # $ Source
# Template with the source
bad_text = "
@@ -12,11 +12,11 @@ class FooController < ActionController::Base
# BAD: user input is evaluated
# where name is unsanitized
template = ERB.new(bad_text).result(binding)
template = ERB.new(bad_text).result(binding) # $ Alert
# BAD: user input is evaluated
# where name is unsanitized
render inline: bad_text
render inline: bad_text # $ Alert
# Template with the source
good_text = "

View File

@@ -2,7 +2,7 @@ class FooController < ActionController::Base
def some_request_handler
# A string tainted by user input is inserted into a template
# (i.e a remote flow source)
name = params[:name]
name = params[:name] # $ Source
# Template with the source (no sanitizer)
bad_text = "
@@ -11,7 +11,7 @@ class FooController < ActionController::Base
" % name
# BAD: renders user input
# where text is unsanitized
Slim::Template.new{ bad_text }.render
Slim::Template.new{ bad_text }.render # $ Alert
# Template with the source (no sanitizer)
bad2_text = "
@@ -20,7 +20,7 @@ class FooController < ActionController::Base
"
# BAD: renders user input
# where text is unsanitized
Slim::Template.new{ bad2_text }.render
Slim::Template.new{ bad2_text }.render # $ Alert
# Template with the source (no render)
good_text = "
@@ -64,4 +64,4 @@ class BarController < ApplicationController
" % name2
template_bar1 = Slim::Template.new{ text_bar2 }.render
end
end
end

View File

@@ -1 +1,2 @@
experimental/template-injection/TemplateInjection.ql
query: experimental/template-injection/TemplateInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,7 +2,7 @@ require 'libxml'
class FooController < ActionController::Base
def libxml_handler(event:, context:)
name = params[:user_name]
name = params[:user_name] # $ Source
xml = <<-XML
<root>
@@ -18,13 +18,13 @@ class FooController < ActionController::Base
results1 = doc.find_first('//foo')
# BAD: XPath query is constructed from user input
results2 = doc.find_first("//#{name}")
results2 = doc.find_first("//#{name}") # $ Alert
# GOOD: XPath query is not constructed from user input
results3 = doc.find('//foo')
# BAD: XPath query is constructed from user input
results4 = doc.find("//#{name}")
results4 = doc.find("//#{name}") # $ Alert
end
end

View File

@@ -2,7 +2,7 @@ require 'nokogiri'
class FooController < ActionController::Base
def nokogiri_handler(event:, context:)
name = params[:user_name]
name = params[:user_name] # $ Source
xml = <<-XML
<root>
@@ -18,19 +18,19 @@ class FooController < ActionController::Base
results1 = doc.at('//foo')
# BAD: XPath query is constructed from user input
results2 = doc.at("//#{name}")
results2 = doc.at("//#{name}") # $ Alert
# GOOD: XPath query is not constructed from user input
results3 = doc.xpath('//foo')
# BAD: XPath query is constructed from user input
results4 = doc.xpath("//#{name}")
results4 = doc.xpath("//#{name}") # $ Alert
# GOOD: XPath query is not constructed from user input
results5 = doc.at_xpath('//foo')
# BAD: XPath query is constructed from user input
results6 = doc.at_xpath("//#{name}")
results6 = doc.at_xpath("//#{name}") # $ Alert
# GOOD: XPath query is not constructed from user input
doc.xpath('//foo').each do |element|
@@ -38,7 +38,7 @@ class FooController < ActionController::Base
end
# BAD: XPath query constructed from user input
doc.xpath("//#{name}").each do |element|
doc.xpath("//#{name}").each do |element| # $ Alert
puts element.text
end
@@ -48,7 +48,7 @@ class FooController < ActionController::Base
end
# BAD: XPath query constructed from user input
doc.search("//#{name}").each do |element|
doc.search("//#{name}").each do |element| # $ Alert
puts element.text
end
end
@@ -85,4 +85,4 @@ class BarController < ActionController::Base
results9 = doc.at_xpath("//#{safe_name}")
end
end
end

View File

@@ -2,7 +2,7 @@ require 'rexml'
class FooController < ActionController::Base
def rexml_handler(event:, context:)
name = params[:user_name]
name = params[:user_name] # $ Source
xml = <<-XML
<root>
@@ -18,13 +18,13 @@ class FooController < ActionController::Base
results1 = REXML::XPath.first(doc, "//foo")
# BAD: XPath query is constructed from user input
results2 = REXML::XPath.first(doc, "//#{name}")
results2 = REXML::XPath.first(doc, "//#{name}") # $ Alert
# GOOD: XPath query is not constructed from user input
results3 = REXML::XPath.match(doc, "//foo", nil)
# BAD: XPath query is constructed from user input
results4 = REXML::XPath.match(doc, "//#{name}", nil)
results4 = REXML::XPath.match(doc, "//#{name}", nil) # $ Alert
# GOOD: XPath query is not constructed from user input
REXML::XPath.each(doc, "//foo") do |element|
@@ -32,7 +32,7 @@ class FooController < ActionController::Base
end
# BAD: XPath query constructed from user input
REXML::XPath.each(doc, "//#{name}") do |element|
REXML::XPath.each(doc, "//#{name}") do |element| # $ Alert
puts element.text
end
end
@@ -66,4 +66,4 @@ class BarController < ActionController::Base
results6 = REXML::XPath.match(doc, "//#{safe_name}", nil)
end
end
end

View File

@@ -1 +1,2 @@
experimental/xpath-injection/XpathInjection.ql
query: experimental/xpath-injection/XpathInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1 +1,2 @@
experimental/cwe-022-zipslip/ZipSlip.ql
query: experimental/cwe-022-zipslip/ZipSlip.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -5,9 +5,9 @@ class TestController < ActionController::Base
def tarReaderUnsafe
path = params[:path]
file_stream = IO.new(IO.sysopen(path))
tarfile = Gem::Package::TarReader.new(file_stream)
tarfile = Gem::Package::TarReader.new(file_stream) # $ Source
tarfile.each do |entry|
::File.open(entry.full_name, "wb") do |os|
::File.open(entry.full_name, "wb") do |os| # $ Alert
entry.read
end
end
@@ -17,9 +17,9 @@ class TestController < ActionController::Base
def tarReaderBlockUnsafe
path = params[:path]
file_stream = IO.new(IO.sysopen(path))
Gem::Package::TarReader.new(file_stream) do |tarfile|
Gem::Package::TarReader.new(file_stream) do |tarfile| # $ Source
tarfile.each_entry do |entry|
::File.open(entry.full_name, "wb") do |os|
::File.open(entry.full_name, "wb") do |os| # $ Alert
entry.read
end
end
@@ -43,8 +43,8 @@ class TestController < ActionController::Base
# BAD
def zipFileUnsafe
path = params[:path]
Zip::File.open(path).each do |entry|
File.open(entry.name, "wb") do |os|
Zip::File.open(path).each do |entry| # $ Source
File.open(entry.name, "wb") do |os| # $ Alert
entry.read
end
end
@@ -53,9 +53,9 @@ class TestController < ActionController::Base
# BAD
def zipFileBlockUnsafe
path = params[:path]
Zip::File.open(path) do |zip_file|
Zip::File.open(path) do |zip_file| # $ Source
zip_file.each do |entry|
File.open(entry.name, "wb") do |os|
File.open(entry.name, "wb") do |os| # $ Alert
entry.read
end
end
@@ -87,7 +87,7 @@ class TestController < ActionController::Base
end
def get_compressed_file_stream(compressed_file_path)
gzip = Zlib::GzipReader.open(compressed_file_path)
gzip = Zlib::GzipReader.open(compressed_file_path) # $ Source
yield(gzip)
end
@@ -97,7 +97,7 @@ class TestController < ActionController::Base
get_compressed_file_stream(path) do |compressed_file|
compressed_file.each do |entry|
entry_path = entry.full_name
::File.open(entry_path, 'wb') do |os|
::File.open(entry_path, 'wb') do |os| # $ Alert
entry.read
end
end
@@ -120,10 +120,10 @@ class TestController < ActionController::Base
def gzipReaderUnsafeNewInstance
path = params[:path]
File.open(path, 'rb') do |f|
gz = Zlib::GzipReader.new(f)
gz = Zlib::GzipReader.new(f) # $ Source
gz.each do |entry|
entry_path = entry.full_name
::File.open(entry_path, 'wb') do |os|
::File.open(entry_path, 'wb') do |os| # $ Alert
entry.read
end
end

View File

@@ -1 +1,2 @@
experimental/cwe-176/UnicodeBypassValidation.ql
query: experimental/cwe-176/UnicodeBypassValidation.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -4,35 +4,35 @@ require 'cgi'
class UnicodeNormalizationOKController < ActionController::Base
def unicodeNormalize
unicode_input = params[:unicode_input]
normalized_nfkc = unicode_input.unicode_normalize(:nfkc) # $ MISSING:result=OK
normalized_nfc = unicode_input.unicode_normalize(:nfc) # $ MISSING:result=OK
unicode_input = params[:unicode_input] # $ Source
normalized_nfkc = unicode_input.unicode_normalize(:nfkc) # $ Alert // $ MISSING:result=OK
normalized_nfc = unicode_input.unicode_normalize(:nfc) # $ Alert // $ MISSING:result=OK
end
end
class UnicodeNormalizationStrManipulationController < ActionController::Base
def unicodeNormalize
unicode_input = params[:unicode_input]
unicode_input_manip = unicode_input.sub(/[aeiou]/, "*")
normalized_nfkc = unicode_input_manip.unicode_normalize(:nfkc) # $ result=BAD
normalized_nfc = unicode_input_manip.unicode_normalize(:nfc) # $ result=BAD
unicode_input = params[:unicode_input] # $ Source
unicode_input_manip = unicode_input.sub(/[aeiou]/, "*") # $ Source
normalized_nfkc = unicode_input_manip.unicode_normalize(:nfkc) # $ Alert // $ result=BAD
normalized_nfc = unicode_input_manip.unicode_normalize(:nfc) # $ Alert // $ result=BAD
end
end
class UnicodeNormalizationHtMLEscapeController < ActionController::Base
def unicodeNormalize
unicode_input = params[:unicode_input]
unicode_html_safe = html_escape(unicode_input)
normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkc) # $ result=BAD
normalized_nfc = unicode_html_safe.unicode_normalize(:nfc) # $ result=BAD
unicode_input = params[:unicode_input] # $ Source
unicode_html_safe = html_escape(unicode_input) # $ Source
normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkc) # $ Alert // $ result=BAD
normalized_nfc = unicode_html_safe.unicode_normalize(:nfc) # $ Alert // $ result=BAD
end
end
class UnicodeNormalizationCGIHtMLEscapeController < ActionController::Base
def unicodeNormalize
unicode_input = params[:unicode_input]
unicode_html_safe = CGI.escapeHTML(unicode_input).html_safe
normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkd) # $ result=BAD
normalized_nfc = unicode_html_safe.unicode_normalize(:nfd) # $ result=BAD
unicode_input = params[:unicode_input] # $ Source
unicode_html_safe = CGI.escapeHTML(unicode_input).html_safe # $ Source
normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkd) # $ Alert // $ result=BAD
normalized_nfc = unicode_html_safe.unicode_normalize(:nfd) # $ Alert // $ result=BAD
end
end

View File

@@ -1 +1,2 @@
experimental/cwe-347/EmptyJWTSecret.ql
query: experimental/cwe-347/EmptyJWTSecret.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -6,10 +6,10 @@ payload = { foo: 'bar' }
token1 = JWT.encode({ foo: 'bar' }, "secret", 'none')
# BAD: the secret used is empty
token2 = JWT.encode({ foo: 'bar' }, nil, 'HS256')
token2 = JWT.encode({ foo: 'bar' }, nil, 'HS256') # $ Alert[rb/jwt-empty-secret-or-algorithm]
# BAD: the secret used is empty
token3 = JWT.encode({ foo: 'bar' }, "", 'HS256')
token3 = JWT.encode({ foo: 'bar' }, "", 'HS256') # $ Alert[rb/jwt-empty-secret-or-algorithm]
# GOOD: the token is signed
token4 = JWT.encode({ foo: 'bar' }, "secret", 'HS256')
token4 = JWT.encode({ foo: 'bar' }, "secret", 'HS256')

View File

@@ -1 +1,2 @@
experimental/cwe-347/MissingJWTVerification.ql
query: experimental/cwe-347/MissingJWTVerification.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -3,22 +3,22 @@ require 'jwt'
payload = { foo: 'bar' }
# Unsecure token
token_without_signature = JWT.encode(payload, nil, 'none')
token_without_signature = JWT.encode(payload, nil, 'none') # $ Alert[rb/jwt-empty-secret-or-algorithm]
# Secure token
token = JWT.encode(payload, "secret", 'HS256')
# BAD: it does not verify
decoded_token1 = JWT.decode(token_without_signature, nil, false, algorithm: 'HS256')
decoded_token1 = JWT.decode(token_without_signature, nil, false, algorithm: 'HS256') # $ Alert[rb/jwt-missing-verification]
# BAD: it's using none
decoded_token3 = JWT.decode(token_without_signature, secret, true, algorithm: 'none')
decoded_token3 = JWT.decode(token_without_signature, secret, true, algorithm: 'none') # $ Alert[rb/jwt-missing-verification]
# BAD: it's using none
decoded_token4 = JWT.decode(token_without_signature, secret, true, { algorithm: 'none' })
decoded_token4 = JWT.decode(token_without_signature, secret, true, { algorithm: 'none' }) # $ Alert[rb/jwt-missing-verification]
# GOOD: it does verify
decoded_token5 = JWT.decode(token, secret, 'HS256')
# GOOD: it does verify
decoded_token2 = JWT.decode(token,secret)
decoded_token2 = JWT.decode(token,secret)

View File

@@ -1 +1,2 @@
experimental/cwe-502/UnsafeYamlDeserialization.ql
query: experimental/cwe-502/UnsafeYamlDeserialization.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -7,15 +7,15 @@ require "yaml"
class UsersController < ActionController::Base
# BAD before psych version 4.0.0 and
def route1
yaml_data = params[:key]
object = Psych.load yaml_data
yaml_data = params[:key] # $ Source
object = Psych.load yaml_data # $ Alert
object = Psych.load_file yaml_data
end
# GOOD In psych version 4.0.0 and above
def route2
yaml_data = params[:key]
object = Psych.load yaml_data
yaml_data = params[:key] # $ Source
object = Psych.load yaml_data # $ Alert
object = Psych.load_file yaml_data
end
@@ -29,14 +29,14 @@ class UsersController < ActionController::Base
# BAD
def route4
yaml_data = params[:key]
object = Psych.unsafe_load(yaml_data)
object = Psych.unsafe_load_file(yaml_data)
object = Psych.load_stream(yaml_data)
yaml_data = params[:key] # $ Source
object = Psych.unsafe_load(yaml_data) # $ Alert
object = Psych.unsafe_load_file(yaml_data) # $ Alert
object = Psych.load_stream(yaml_data) # $ Alert
parse_output = Psych.parse_stream(yaml_data)
object = parse_output.to_ruby
object = Psych.parse(yaml_data).to_ruby
object = Psych.parse_file(yaml_data).to_ruby
object = parse_output.to_ruby # $ Alert
object = Psych.parse(yaml_data).to_ruby # $ Alert
object = Psych.parse_file(yaml_data).to_ruby # $ Alert
parsed_yaml = Psych.parse_stream(yaml_data)
parsed_yaml.children.each do |child|
object = child.to_ruby
@@ -46,7 +46,7 @@ class UsersController < ActionController::Base
end
object = parsed_yaml.children.first.to_ruby
content = parsed_yaml.children[0].children[0].children
object = parsed_yaml.to_ruby[0]
object = parsed_yaml.to_ruby[0] # $ Alert
object = content.to_ruby[0]
object = Psych.parse(yaml_data).children[0].to_ruby
end
@@ -58,18 +58,18 @@ class UsersController < ActionController::Base
end
def stdin
object = YAML.load $stdin.read
object = YAML.load $stdin.read # $ Alert
# STDIN
object = YAML.load STDIN.gets
object = YAML.load STDIN.gets # $ Alert
# ARGF
object = YAML.load ARGF.read
object = YAML.load ARGF.read # $ Alert
# Kernel.gets
object = YAML.load gets
object = YAML.load gets # $ Alert
# Kernel.readlines
object = YAML.load readlines
object = YAML.load readlines # $ Alert
end
end

View File

@@ -1 +1,2 @@
experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql
query: experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,39 +1,39 @@
class ExampleController < ActionController::Base
# Should find
def example_action
if request.get?
if request.get? # $ Alert
Resource.find(id: params[:example_id])
end
end
# Should find
def other_action
method = request.env['REQUEST_METHOD']
if method == "GET"
method = request.env['REQUEST_METHOD'] # $ Source
if method == "GET" # $ Alert
Resource.find(id: params[:id])
end
end
# Should find
def foo
method = request.request_method
if method == "GET"
method = request.request_method # $ Source
if method == "GET" # $ Alert
Resource.find(id: params[:id])
end
end
# Should find
def bar
method = request.method
if method == "GET"
method = request.method # $ Source
if method == "GET" # $ Alert
Resource.find(id: params[:id])
end
end
# Should find
def baz
method = request.raw_request_method
if method == "GET"
method = request.raw_request_method # $ Source
if method == "GET" # $ Alert
Resource.find(id: params[:id])
end
end
@@ -48,15 +48,15 @@ class ExampleController < ActionController::Base
# Should find
def foobarbaz
method = request.request_method_symbol
if method == :GET
method = request.request_method_symbol # $ Source
if method == :GET # $ Alert
Resource.find(id: params[:id])
end
end
# Should find
def resource_action
case request.env['REQUEST_METHOD']
case request.env['REQUEST_METHOD'] # $ Alert
when "GET"
Resource.find(id: params[:id])
when "POST"
@@ -114,4 +114,4 @@ class NotAController
end
class Resource < ActiveRecord::Base
end
end

View File

@@ -1 +1,2 @@
experimental/weak-params/WeakParams.ql
query: experimental/weak-params/WeakParams.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,22 +2,22 @@ class TestController < ActionController::Base
# Should catch
def create
TestObject.create(foo: request.request_parameters[:foo])
TestObject.create(foo: request.request_parameters[:foo]) # $ Alert
end
# Should catch
def create_query
TestObject.create(foo: request.query_parameters[:foo])
TestObject.create(foo: request.query_parameters[:foo]) # $ Alert
end
# Should catch
def update_unsafe
TestObject.update(foo: request.POST[:foo])
TestObject.update(foo: request.POST[:foo]) # $ Alert
end
# Should catch
def update_unsafe_get
TestObject.update(foo: request.GET[:foo])
TestObject.update(foo: request.GET[:foo]) # $ Alert
end
# Should not catch
@@ -37,4 +37,4 @@ class TestController < ActionController::Base
end
class TestObject < ActiveRecord::Base
end
end

View File

@@ -1 +1,2 @@
experimental/performance/UseDetect.ql
query: experimental/performance/UseDetect.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,14 +2,14 @@
class DetectTest
def test
# These are bad
[].select { |i| true }.first
[].select { |i| true }.last
[].select { |i| true }[0]
[].select { |i| true }[-1]
[].filter { |i| true }.first
[].find_all { |i| true }.last
[].select { |i| true }.first # $ Alert
[].select { |i| true }.last # $ Alert
[].select { |i| true }[0] # $ Alert
[].select { |i| true }[-1] # $ Alert
[].filter { |i| true }.first # $ Alert
[].find_all { |i| true }.last # $ Alert
selection1 = [].select { |i| true }
selection1.first
selection1.first # $ Alert
# These are good
[].select("").first # Selecting a string

View File

@@ -1 +1,2 @@
queries/security/cwe-020/IncompleteHostnameRegExp.ql
query: queries/security/cwe-020/IncompleteHostnameRegExp.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,6 +1,6 @@
UNSAFE_REGEX1 = /(www|beta).example.com\//
UNSAFE_REGEX2 = Regexp.compile("(www|beta).example.com/")
UNSAFE_REGEX3 = Regexp.new("(www|beta).example.com/")
UNSAFE_REGEX1 = /(www|beta).example.com\// # $ Alert
UNSAFE_REGEX2 = Regexp.compile("(www|beta).example.com/") # $ Alert
UNSAFE_REGEX3 = Regexp.new("(www|beta).example.com/") # $ Alert
SAFE_REGEX = /(www|beta)\.example\.com\//
def unsafe

View File

@@ -1,31 +1,31 @@
def foo
/^http:\/\/example.com/; # OK
/^http:\/\/test.example.com/; # NOT OK
/^http:\/\/test.example.com/; # $ Alert // NOT OK
/^http:\/\/test\.example.com/; # OK
/^http:\/\/test.example.net/; # NOT OK
/^http:\/\/test.(example-a|example-b).com/; # NOT OK
/^http:\/\/(.+).example.com\//; # NOT OK
/^http:\/\/test.example.net/; # $ Alert // NOT OK
/^http:\/\/test.(example-a|example-b).com/; # $ Alert // NOT OK
/^http:\/\/(.+).example.com\//; # $ Alert // NOT OK
/^http:\/\/(\.+)\.example.com/; # OK
/^http:\/\/(?:.+)\.test\.example.com\//; # NOT OK
/^http:\/\/test.example.com\/(?:.*)/; # OK
Regexp.new("^http://test.example.com"); # NOT OK
if (s.match("^http://test.example.com")); end # NOT OK
/^http:\/\/(?:.+)\.test\.example.com\//; # $ Alert // NOT OK
/^http:\/\/test.example.com\/(?:.*)/; # $ Alert // OK
Regexp.new("^http://test.example.com"); # $ Alert // NOT OK
if (s.match("^http://test.example.com")); end # $ Alert // NOT OK
Regexp.new(id(id(id("^http://test.example.com")))); # NOT OK
Regexp.new(id(id(id("^http://test.example.com")))); # $ Alert // NOT OK
Regexp.new(`test.example.com$`); # NOT OK
Regexp.new(`test.example.com$`); # $ Alert // NOT OK
hostname = '^test.example.com'; # NOT OK
Regexp.new("#{hostname}$");
hostname = '^test.example.com'; # $ Alert // NOT OK
Regexp.new("#{hostname}$"); # $ Alert
domain = { hostname: 'test.example.com$' }; # NOT OK
domain = { hostname: 'test.example.com$' }; # $ Alert // NOT OK
Regexp.new(domain[:hostname]);
convert1({ hostname: 'test.example.com$' }); # NOT OK
convert1({ hostname: 'test.example.com$' }); # $ Alert // NOT OK
domains = [ { hostname: 'test.example.com$' } ]; # NOT OK - but not flagged due to limitations of TypeTracking.
@@ -34,18 +34,18 @@ def foo
domains.map{ |d| convert2(d) };
/^(.+\.(?:example-a|example-b)\.com)\//; # NOT OK
/^(https?:)?\/\/((service|www).)?example.com(?=$|\/)/; # NOT OK
/^(http|https):\/\/www.example.com\/p\/f\//; # NOT OK
/^(http:\/\/sub.example.com\/)/i; # NOT OK
/^https?:\/\/api.example.com/; # NOT OK
Regexp.new('^http://localhost:8000|' + "^https?://.+\\.example\\.com/"); # NOT OK
/^(https?:)?\/\/((service|www).)?example.com(?=$|\/)/; # $ Alert // NOT OK
/^(http|https):\/\/www.example.com\/p\/f\//; # $ Alert // NOT OK
/^(http:\/\/sub.example.com\/)/i; # $ Alert // NOT OK
/^https?:\/\/api.example.com/; # $ Alert // NOT OK
Regexp.new('^http://localhost:8000|' + "^https?://.+\\.example\\.com/"); # $ Alert // NOT OK
Regexp.new("^http[s]?:\/\/?sub1\\.sub2\\.example\\.com\/f\/(.+)"); # NOT OK
/^https:\/\/[a-z]*.example.com$/; # NOT OK
Regexp.compile('^protos?://(localhost|.+.example.net|.+.example-a.com|.+.example-b.com|.+.example.internal)'); # NOT OK
/^https:\/\/[a-z]*.example.com$/; # $ Alert // NOT OK
Regexp.compile('^protos?://(localhost|.+.example.net|.+.example-a.com|.+.example-b.com|.+.example.internal)'); # $ Alert // NOT OK
/^(example.dev|example.com)/; # OK
Regexp.new('^http://localhost:8000|' + "^https?://.+.example\\.com/"); # NOT OK
Regexp.new('^http://localhost:8000|' + "^https?://.+.example\\.com/"); # $ Alert // NOT OK
primary = 'example.com$';
Regexp.new('test.' + primary); # NOT OK, but not detected
@@ -56,7 +56,7 @@ def foo
/^http:\/\/(..|...)\.example\.com\/index\.html/; # OK, wildcards are intentional
/^http:\/\/.\.example\.com\/index\.html/; # OK, the wildcard is intentional
/^(foo.example\.com|whatever)$/; # kinda OK - one disjunction doesn't even look like a hostname
/^(foo.example\.com|whatever)$/; # $ Alert // kinda OK - one disjunction doesn't even look like a hostname
end
def id(e); return e; end
def convert1(domain)
@@ -78,4 +78,4 @@ class B
end
end
B.match?("^http://test.example.com") # NOT OK
B.match?("^http://test.example.com") # $ Alert // NOT OK

View File

@@ -1 +1,2 @@
queries/security/cwe-020/IncompleteUrlSubstringSanitization.ql
query: queries/security/cwe-020/IncompleteUrlSubstringSanitization.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,23 +1,23 @@
def test (x)
x.index("internal") != nil; # NOT OK, but not flagged
x.index("localhost") != nil; # NOT OK, but not flagged
x.index("secure.com") != nil; # NOT OK
x.index("secure.net") != nil; # NOT OK
x.index(".secure.com") != nil; # NOT OK
x.index("secure.com") != nil; # $ Alert // NOT OK
x.index("secure.net") != nil; # $ Alert // NOT OK
x.index(".secure.com") != nil; # $ Alert // NOT OK
x.index("sub.secure.") != nil; # NOT OK, but not flagged
x.index(".sub.secure.") != nil; # NOT OK, but not flagged
x.index("secure.com") === nil; # NOT OK
x.index("secure.com") === 0; # NOT OK
x.index("secure.com") >= 0; # NOT OK
x.index("secure.com") === nil; # $ Alert // NOT OK
x.index("secure.com") === 0; # $ Alert // NOT OK
x.index("secure.com") >= 0; # $ Alert // NOT OK
x.start_with?("https://secure.com"); # NOT OK
x.end_with?("secure.com"); # NOT OK
x.start_with?("https://secure.com"); # $ Alert // NOT OK
x.end_with?("secure.com"); # $ Alert // NOT OK
x.end_with?(".secure.com"); # OK
x.start_with?("secure.com/"); # OK
x.index("secure.com/") === 0; # OK
x.include?("secure.com"); # NOT OK
x.include?("secure.com"); # $ Alert // NOT OK
x.index("#") != nil; # OK
x.index(":") != nil; # OK
@@ -29,9 +29,9 @@ def test (x)
x.index("some/path") != nil; # OK
x.index("/index.html") != nil; # OK
x.index(":template:") != nil; # OK
x.index("https://secure.com") != nil; # NOT OK
x.index("https://secure.com:443") != nil; # NOT OK
x.index("https://secure.com/") != nil; # NOT OK
x.index("https://secure.com") != nil; # $ Alert // NOT OK
x.index("https://secure.com:443") != nil; # $ Alert // NOT OK
x.index("https://secure.com/") != nil; # $ Alert // NOT OK
x.index(".cn") != nil; # NOT OK, but not flagged
x.index(".jpg") != nil; # OK
@@ -49,28 +49,28 @@ def test (x)
x.index("tar.gz") + offset; # OK
x.index("tar.gz") - offset; # OK
x.index("https://example.internal") != nil; # NOT OK
x.index("https://example.internal") != nil; # $ Alert // NOT OK
x.index("https://") != nil; # OK
x.start_with?("https://example.internal"); # NOT OK
x.index('https://example.internal.org') != 0; # NOT OK
x.index('https://example.internal.org') === 0; # NOT OK
x.end_with?("internal.com"); # NOT OK
x.start_with?("https://example.internal"); # $ Alert // NOT OK
x.index('https://example.internal.org') != 0; # $ Alert // NOT OK
x.index('https://example.internal.org') === 0; # $ Alert // NOT OK
x.end_with?("internal.com"); # $ Alert // NOT OK
x.start_with?("https://example.internal:80"); # OK
x.index("secure.com") != nil; # NOT OK
x.index("secure.com") === nil; # OK
!(x.index("secure.com") != nil); # OK
!x.include?("secure.com"); # OK
x.index("secure.com") != nil; # $ Alert // NOT OK
x.index("secure.com") === nil; # $ Alert // OK
!(x.index("secure.com") != nil); # $ Alert // OK
!x.include?("secure.com"); # $ Alert // OK
if !x.include?("secure.com") # NOT OK
if !x.include?("secure.com") # $ Alert // NOT OK
else
doSomeThingWithTrustedURL(x);
end
x.start_with?("https://secure.com/foo/bar"); # OK - a forward slash after the domain makes prefix checks safe.
x.index("https://secure.com/foo/bar") >= 0 # NOT OK - the url can be anywhere in the string.
x.index("https://secure.com") >= 0 # NOT OK
x.index("https://secure.com/foo/bar-baz") >= 0 # NOT OK - the url can be anywhere in the string.
x.index("https://secure.com/foo/bar") >= 0 # $ Alert // NOT OK - the url can be anywhere in the string.
x.index("https://secure.com") >= 0 # $ Alert // NOT OK
x.index("https://secure.com/foo/bar-baz") >= 0 # $ Alert // NOT OK - the url can be anywhere in the string.
end

View File

@@ -1 +1,2 @@
queries/security/cwe-020/MissingFullAnchor.ql
query: queries/security/cwe-020/MissingFullAnchor.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,17 +1,17 @@
class Foobar
def foo1(name)
raise Blabity, 'Invalid thing' if name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # NOT OK
def foo1(name) # $ Source
raise Blabity, 'Invalid thing' if name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # $ Alert // NOT OK
end
def foo2(name)
raise Blabity, 'Invalid thing' unless name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # NOT OK
def foo2(name) # $ Source
raise Blabity, 'Invalid thing' unless name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # $ Alert // NOT OK
end
def foo3(name)
raise Blabity, 'Invalid thing' unless name !~ /\A[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*\z/ # OK
end
def foo4(name)
raise Blabity, 'Invalid thing' unless not name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # NOT OK
def foo4(name) # $ Source
raise Blabity, 'Invalid thing' unless not name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # $ Alert // NOT OK
end
end

View File

@@ -1 +1,2 @@
queries/security/cwe-020/MissingRegExpAnchor.ql
query: queries/security/cwe-020/MissingRegExpAnchor.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,11 +1,11 @@
/www\.example\.com/ # BAD
/www\.example\.com/ # $ Alert // BAD
/^www\.example\.com$/ # BAD: uses end-of-line anchors rather than end-of-string anchors
/\Awww\.example\.com\z/ # GOOD
/foo\.bar/ # GOOD
/https?:\/\/good\.com/ # BAD
/^https?:\/\/good\.com/ # BAD: missing end-of-string anchor
/https?:\/\/good\.com/ # $ Alert // BAD
/^https?:\/\/good\.com/ # $ Alert // BAD: missing end-of-string anchor
/(^https?:\/\/good1\.com)|(^https?:#good2\.com)/ # BAD: missing end-of-string anchor
/bar/ # GOOD
@@ -16,40 +16,40 @@ foo.gsub!(/www\.example\.com/, "bar") # GOOD
foo.sub!(/www\.example\.com/, "bar") # GOOD
/^a|/
/^a|b/ # BAD
/^a|b/ # $ Alert // BAD
/a|^b/
/^a|^b/
/^a|b|c/ # BAD
/^a|b|c/ # $ Alert // BAD
/a|^b|c/
/a|b|^c/
/^a|^b|c/
/(^a)|b/
/^a|(b)/ # BAD
/^a|(b)/ # $ Alert // BAD
/^a|(^b)/
/^(a)|(b)/ # BAD
/^(a)|(b)/ # $ Alert // BAD
/a|b$/ # BAD
/a|b$/ # $ Alert // BAD
/a$|b/
/a$|b$/
/a|b|c$/ # BAD
/a|b|c$/ # $ Alert // BAD
/a|b$|c/
/a$|b|c/
/a|b$|c$/
/a|(b$)/
/(a)|b$/ # BAD
/(a)|b$/ # $ Alert // BAD
/(a$)|b$/
/(a)|(b)$/ # BAD
/(a)|(b)$/ # $ Alert // BAD
/^good.com|better.com/ # BAD
/^good\.com|better\.com/ # BAD
/^good\\.com|better\\.com/ # BAD
/^good\\\.com|better\\\.com/ # BAD
/^good\\\\.com|better\\\\.com/ # BAD
/^good.com|better.com/ # $ Alert // BAD
/^good\.com|better\.com/ # $ Alert // BAD
/^good\\.com|better\\.com/ # $ Alert // BAD
/^good\\\.com|better\\\.com/ # $ Alert // BAD
/^good\\\\.com|better\\\\.com/ # $ Alert // BAD
/^foo|bar|baz$/ # BAD
/^foo|bar|baz$/ # $ Alert // BAD
/^foo|%/ # OK
REGEXP = /foo/
@@ -57,5 +57,5 @@ REGEXP.match? "http://example.com" # GOOD: the url is the text not the regexp
REGEXP.match "http://example.com" # GOOD: the url is the text not the regexp
"http://example.com".match? REGEXP # GOOD: the url is the text not the regexp
"http://example.com".match REGEXP # GOOD: the url is the text not the regexp
"some text".match? "http://example.com" # BAD
"some text".match "http://example.com" # BAD
"some text".match? "http://example.com" # $ Alert // BAD
"some text".match "http://example.com" # $ Alert // BAD

View File

@@ -1 +1,2 @@
queries/security/cwe-020/OverlyLargeRange.ql
query: queries/security/cwe-020/OverlyLargeRange.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,8 +1,8 @@
overlap1 = /^[0-93-5]$/ # NOT OK
overlap1 = /^[0-93-5]$/ # $ Alert // NOT OK
overlap2 = /[A-ZA-z]/ # NOT OK
overlap2 = /[A-ZA-z]/ # $ Alert // NOT OK
isEmpty = /^[z-a]$/ # NOT OK
isEmpty = /^[z-a]$/ # $ Alert // NOT OK
isAscii = /^[\x00-\x7F]*$/ # OK
@@ -12,22 +12,22 @@ codePoints = /[^\x21-\x7E]|[\[\](){}<>\/%]/ # OK
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/ # OK
smallOverlap = /[0-9a-fA-f]/ # NOT OK
smallOverlap = /[0-9a-fA-f]/ # $ Alert // NOT OK
weirdRange = /[$-`]/ # NOT OK
weirdRange = /[$-`]/ # $ Alert // NOT OK
keywordOperator = /[!\~\*\/%+-<>\^|=&]/ # NOT OK
keywordOperator = /[!\~\*\/%+-<>\^|=&]/ # $ Alert // NOT OK
notYoutube = /youtu\.be\/[a-z1-9.-_]+/ # NOT OK
notYoutube = /youtu\.be\/[a-z1-9.-_]+/ # $ Alert // NOT OK
numberToLetter = /[7-F]/ # NOT OK
numberToLetter = /[7-F]/ # $ Alert // NOT OK
overlapsWithClass1 = /[0-9\d]/ # NOT OK
overlapsWithClass1 = /[0-9\d]/ # $ Alert // NOT OK
overlapsWithClass2 = /[\w,.-?:*+]/ # NOT OK
overlapsWithClass2 = /[\w,.-?:*+]/ # $ Alert // NOT OK
escapes = /[\000-\037\047\134\177-\377]/n # OK - they are escapes
nested = /[a-z&&[^a-c]]/ # OK
overlapsWithNothing = /[\w_%-.]/;
overlapsWithNothing = /[\w_%-.]/; # $ Alert

View File

@@ -1 +1,2 @@
queries/security/cwe-078/KernelOpen.ql
query: queries/security/cwe-078/KernelOpen.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,16 +1,16 @@
class UsersController < ActionController::Base
def create
file = params[:file]
open(file) # BAD
IO.read(file) # BAD
IO.write(file) # BAD
IO.binread(file) # BAD
IO.binwrite(file) # BAD
IO.foreach(file) # BAD
IO.readlines(file) # BAD
URI.open(file) # BAD
file = params[:file] # $ Source
open(file) # $ Alert // BAD
IO.read(file) # $ Alert // BAD
IO.write(file) # $ Alert // BAD
IO.binread(file) # $ Alert // BAD
IO.binwrite(file) # $ Alert // BAD
IO.foreach(file) # $ Alert // BAD
IO.readlines(file) # $ Alert // BAD
URI.open(file) # $ Alert // BAD
IO.read(File.join(file, "")) # BAD - file as first argument to File.join
IO.read(File.join(file, "")) # $ Alert // BAD - file as first argument to File.join
IO.read(File.join("", file)) # GOOD - file path is sanitised by guard
File.open(file).read # GOOD
@@ -23,6 +23,6 @@ class UsersController < ActionController::Base
IO.read(file) # GOOD - file path is sanitised by guard
end
open(file) # BAD - sanity check to verify that file was not mistakenly marked as sanitized
open(file) # $ Alert // BAD - sanity check to verify that file was not mistakenly marked as sanitized
end
end

View File

@@ -1 +1,2 @@
queries/security/cwe-078/NonConstantKernelOpen.ql
query: queries/security/cwe-078/NonConstantKernelOpen.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -4,18 +4,18 @@ class UsersController < ActionController::Base
def create
file = params[:file]
open(file) # BAD
IO.read(file) # BAD
IO.write(file) # BAD
IO.binread(file) # BAD
IO.binwrite(file) # BAD
IO.foreach(file) # BAD
IO.readlines(file) # BAD
URI.open(file) # BAD
open(file) # $ Alert // BAD
IO.read(file) # $ Alert // BAD
IO.write(file) # $ Alert // BAD
IO.binread(file) # $ Alert // BAD
IO.binwrite(file) # $ Alert // BAD
IO.foreach(file) # $ Alert // BAD
IO.readlines(file) # $ Alert // BAD
URI.open(file) # $ Alert // BAD
File.open(file).read # GOOD
Kernel.open(file) # BAD
Kernel.open(file) # $ Alert // BAD
File.open(file, "r") # GOOD
@@ -25,7 +25,7 @@ class UsersController < ActionController::Base
Kernel.open("this is #{fine}") # GOOD
Kernel.open("#{this_is} bad") # BAD
Kernel.open("#{this_is} bad") # $ Alert // BAD
open("| #{this_is_an_explicit_command} foo bar") # GOOD
@@ -43,6 +43,6 @@ class UsersController < ActionController::Base
open.where(external: false) # GOOD - an open method is called withoout arguments
open(file) # BAD - sanity check to verify that file was not mistakenly marked as sanitized
open(file) # $ Alert // BAD - sanity check to verify that file was not mistakenly marked as sanitized
end
end

View File

@@ -1 +1,2 @@
queries/security/cwe-078/UnsafeShellCommandConstruction.ql
query: queries/security/cwe-078/UnsafeShellCommandConstruction.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,6 +1,5 @@
class Foobar
def foo1(target)
IO.popen("cat #{target}", "w") # NOT OK - everything assumed to be imported...
def foo1(target) # $ Source
IO.popen("cat #{target}", "w") # $ Alert // NOT OK - everything assumed to be imported...
end
end

View File

@@ -1,7 +1,7 @@
class Foobar
def foo1(target)
IO.popen("cat #{target}", "w") # NOT OK
def foo1(target) # $ Source
IO.popen("cat #{target}", "w") # $ Alert // NOT OK
end
end
require 'sub/other2'
require 'sub/other2'

View File

@@ -1,5 +1,5 @@
class Foobar
def foo1(target)
IO.popen("cat #{target}", "w") # NOT OK
def foo1(target) # $ Source
IO.popen("cat #{target}", "w") # $ Alert // NOT OK
end
end
end

View File

@@ -1,10 +1,10 @@
class Foobar
def foo1(target)
IO.popen("cat #{target}", "w") # NOT OK
def foo1(target) # $ Source
IO.popen("cat #{target}", "w") # $ Alert // NOT OK
end
def foo2(x)
format = sprintf("cat %s", x) # NOT OK
def foo2(x) # $ Source
format = sprintf("cat %s", x) # $ Alert // NOT OK
IO.popen(format, "w")
end
@@ -12,30 +12,30 @@ class Foobar
File.read(path) # OK
end
def my_exec(cmd, command, myCmd, myCommand, innocent_file_path)
def my_exec(cmd, command, myCmd, myCommand, innocent_file_path) # $ Source
IO.popen("which #{cmd}", "w") # OK - the parameter is named `cmd`, so it's meant to be a command
IO.popen("which #{command}", "w") # OK - the parameter is named `command`, so it's meant to be a command
IO.popen("which #{myCmd}", "w") # OK - the parameter is named `myCmd`, so it's meant to be a command
IO.popen("which #{myCommand}", "w") # OK - the parameter is named `myCommand`, so it's meant to be a command
IO.popen("which #{innocent_file_path}", "w") # NOT OK - the parameter is named `innocent_file_path`, so it's not meant to be a command
IO.popen("which #{innocent_file_path}", "w") # $ Alert // NOT OK - the parameter is named `innocent_file_path`, so it's not meant to be a command
end
def escaped(file_path)
def escaped(file_path) # $ Source
IO.popen("cat #{file_path.shellescape}", "w") # OK - the parameter is escaped
IO.popen("cat #{file_path}", "w") # NOT OK - the parameter is not escaped
IO.popen("cat #{file_path}", "w") # $ Alert // NOT OK - the parameter is not escaped
end
end
require File.join(File.dirname(__FILE__), 'sub', 'other')
class Foobar2
def foo1(target)
IO.popen("cat #{target}", "w") # NOT OK
def foo1(target) # $ Source
IO.popen("cat #{target}", "w") # $ Alert // NOT OK
end
def id(x)
IO.popen("cat #{x}", "w") # NOT OK - the parameter is not a constant.
def id(x) # $ Source
IO.popen("cat #{x}", "w") # $ Alert // NOT OK - the parameter is not a constant.
return x
end
@@ -44,27 +44,27 @@ class Foobar2
end
# class methods
def self.foo(target)
IO.popen("cat #{target}", "w") # NOT OK
def self.foo(target) # $ Source
IO.popen("cat #{target}", "w") # $ Alert // NOT OK
end
def arrayJoin(x)
IO.popen(x.join(' '), "w") # NOT OK
def arrayJoin(x) # $ Source
IO.popen(x.join(' '), "w") # $ Alert // NOT OK
IO.popen(["foo", "bar", x].join(' '), "w") # NOT OK
IO.popen(["foo", "bar", x].join(' '), "w") # $ Alert // NOT OK
end
def string_concat(x)
IO.popen("cat " + x, "w") # NOT OK
def string_concat(x) # $ Source
IO.popen("cat " + x, "w") # $ Alert // NOT OK
end
def array_taint (x, y)
def array_taint (x, y) # $ Source
arr = ["cat"]
arr.push(x)
IO.popen(arr.join(' '), "w") # NOT OK
IO.popen(arr.join(' '), "w") # $ Alert // NOT OK
arr2 = ["cat"]
arr2 << y
IO.popen(arr.join(' '), "w") # NOT OK
IO.popen(arr.join(' '), "w") # $ Alert // NOT OK
end
end

View File

@@ -1 +1,2 @@
queries/security/cwe-079/ReflectedXSS.ql
query: queries/security/cwe-079/ReflectedXSS.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1 +1,2 @@
queries/security/cwe-079/StoredXSS.ql
query: queries/security/cwe-079/StoredXSS.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1 +1,2 @@
queries/security/cwe-079/UnsafeHtmlConstruction.ql
query: queries/security/cwe-079/UnsafeHtmlConstruction.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -6,34 +6,34 @@ class BarsController < ApplicationController
end
def user_name
return params[:user_name]
return params[:user_name] # $ Source[rb/reflected-xss]
end
def user_name_memo
@user_name ||= params[:user_name]
@user_name ||= params[:user_name] # $ Source[rb/reflected-xss]
end
def show
@user_website = params[:website]
dt = params[:text]
@user_website = params[:website] # $ Source[rb/reflected-xss]
dt = params[:text] # $ Source[rb/reflected-xss]
@instance_text = dt
@safe_foo = params[:text]
@safe_foo = "safe_foo"
@html_escaped = ERB::Util.html_escape(params[:text])
@header_escaped = ERB::Util.html_escape(cookies[:foo]) # OK - cookies not controllable by 3rd party
response.header["content-type"] = params[:content_type]
response.header["content-type"] = params[:content_type] # $ Alert[rb/reflected-xss]
response.header["x-customer-header"] = params[:bar] # OK - header not relevant to XSS
render "foo/bars/show", locals: { display_text: dt, safe_text: "hello" }
end
def make_safe_html
str = params[:user_name]
str.html_safe
str = params[:user_name] # $ Source[rb/reflected-xss]
str.html_safe # $ Alert[rb/reflected-xss]
translate("welcome", name: params[:user_name]).html_safe # NOT OK - translate preserves taint
t("welcome", name: params[:user_name]).html_safe # NOT OK - t is an alias of translate
translate("welcome", name: params[:user_name]).html_safe # $ Alert[rb/reflected-xss] // NOT OK - translate preserves taint
t("welcome", name: params[:user_name]).html_safe # $ Alert[rb/reflected-xss] // NOT OK - t is an alias of translate
t("welcome_html", name: params[:user_name]).html_safe # OK - t escapes html when key ends in _html
I18n.t("welcome_html", name: params[:user_name]).html_safe # NOT OK - I18n.t does not escape html
I18n.translate("welcome_html", name: params[:user_name]).html_safe # NOT OK - alias
I18n.t("welcome_html", name: params[:user_name]).html_safe # $ Alert[rb/reflected-xss] // NOT OK - I18n.t does not escape html
I18n.translate("welcome_html", name: params[:user_name]).html_safe # $ Alert[rb/reflected-xss] // NOT OK - alias
end
end

View File

@@ -5,7 +5,7 @@ class StoresController < ApplicationController
end
def show
dt = File.read("foo.txt")
dt = File.read("foo.txt") # $ Source[rb/stored-xss]
@instance_text = dt
@user = User.find 1
@safe_user_handle = ERB::Util.html_escape(@user.handle)

View File

@@ -2,10 +2,10 @@
<%= raw @display_text %>
<%# BAD: A local rendered raw as a local variable %>
<%= raw display_text %>
<%= raw display_text %> <%# $ Alert[rb/reflected-xss], Alert[rb/stored-xss] %>
<%# BAD: A local rendered raw via the local_assigns hash %>
<%= raw local_assigns[:display_text] %>
<%= raw local_assigns[:display_text] %> <%# $ Alert[rb/reflected-xss], Alert[rb/stored-xss] %>
<%# GOOD: A local rendered with default escaping via the local_assigns hash %>
<%= local_assigns[:display_text] %>

View File

@@ -1,20 +1,20 @@
<%# BAD: An instance variable rendered without escaping %>
<a href="<%= raw @user_website %>">website</a>
<a href="<%= raw @user_website %>">website</a> <%# $ Alert[rb/reflected-xss] %>
<%# BAD: A local rendered raw as a local variable %>
<%= raw display_text %>
<%= raw display_text %> <%# $ Alert[rb/reflected-xss] %>
<%# BAD: A local rendered raw via the local_assigns hash %>
<%= raw local_assigns[:display_text] %>
<%= raw local_assigns[:display_text] %> <%# $ Alert[rb/reflected-xss] %>
<% key = :display_text %>
<%# BAD: A local rendered raw via the locals_assigns hash %>
<%= raw local_assigns[key] %>
<%= raw local_assigns[key] %> <%# $ Alert[rb/reflected-xss] %>
<ul>
<% for key in [:display_text, :safe_text] do %>
<%# BAD: A local rendered raw via the locals hash %>
<li><%= raw local_assigns[key] %></li>
<li><%= raw local_assigns[key] %></li> <%# $ Alert[rb/reflected-xss] %>
<% end %>
</ul>
@@ -32,28 +32,28 @@
<%# BAD: html_safe marks string as not requiring HTML escaping %>
<%=
display_text.html_safe
display_text.html_safe <%# $ Alert[rb/reflected-xss] %>
%>
<%# BAD: html_safe marks string as not requiring HTML escaping %>
<%=
@instance_text.html_safe
@instance_text.html_safe <%# $ Alert[rb/reflected-xss] %>
%>
<%= render partial: 'foo/bars/widget', locals: { display_text: "widget_" + display_text } %>
<%# BAD: user_name is a helper method that returns unsanitized user-input %>
<%= user_name.html_safe %>
<%= user_name.html_safe %> <%# $ Alert[rb/reflected-xss] %>
<%# BAD: user_name_memo is a helper method that returns unsanitized user-input %>
<%# TODO: we miss this because the return value from user_name_memo is not properly linked to this call %>
<%= user_name_memo.html_safe %>
<%= user_name_memo.html_safe %> <%# $ Alert[rb/reflected-xss] %>
<%# BAD: unsanitized user-input should not be passed to link_to as the URL %>
<%= link_to "user website", params[:website] %>
<%= link_to "user website", params[:website] %> <%# $ Alert[rb/reflected-xss] %>
<%# BAD: unsanitized user-input should not be passed to link_to as the URL %>
<%= link_to params[:website], class: "user-link" do %>
<%= link_to params[:website], class: "user-link" do %> <%# $ Alert[rb/reflected-xss] %>
user website
<% end %>
@@ -70,20 +70,20 @@
%>
<%# BAD: simple_format called with sanitize: false %>
<%= simple_format(params[:comment], sanitize: false) %>
<%= simple_format(params[:comment], sanitize: false) %> <%# $ Alert[rb/reflected-xss] %>
<%# BAD: javasript_include_tag called with remote input %>
<%= javascript_include_tag params[:url] %>
<%= javascript_include_tag params[:url] %> <%# $ Alert[rb/reflected-xss] %>
<%# GOOD: input is sanitized %>
<%= sanitize(params[:comment]).html_safe %>
<%# BAD: A local rendered raw as a local variable %>
<%== display_text %>
<%== display_text %> <%# $ Alert[rb/reflected-xss] %>
<%# BAD: translate preserves taint %>
<%= raw translate("welcome", name: display_text) %>
<%= raw t("welcome", name: display_text) %>
<%= raw translate("welcome", name: display_text) %> <%# $ Alert[rb/reflected-xss] %>
<%= raw t("welcome", name: display_text) %> <%# $ Alert[rb/reflected-xss] %>
<%# GOOD: translate sanitizes for html keys %>
<%= raw t("welcome1.html", name: display_text) %>

View File

@@ -1,17 +1,17 @@
<%# BAD: A local rendered raw as a local variable %>
<%= raw display_text %>
<%= raw display_text %> <%# $ Alert[rb/stored-xss] %>
<%# BAD: A local rendered raw via the local_assigns hash %>
<%= raw local_assigns[:display_text] %>
<%= raw local_assigns[:display_text] %> <%# $ Alert[rb/stored-xss] %>
<% key = :display_text %>
<%# BAD: A local rendered raw via the locals_assigns hash %>
<%= raw local_assigns[key] %>
<%= raw local_assigns[key] %> <%# $ Alert[rb/stored-xss] %>
<ul>
<% for key in [:display_text, :safe_text] do %>
<%# BAD: A local rendered raw via the locals hash %>
<li><%= raw local_assigns[key] %></li>
<li><%= raw local_assigns[key] %></li> <%# $ Alert[rb/stored-xss] %>
<% end %>
</ul>
@@ -29,12 +29,12 @@
<%# BAD: html_safe marks string as not requiring HTML escaping %>
<%=
display_text.html_safe
display_text.html_safe <%# $ Alert[rb/stored-xss] %>
%>
<%# BAD: html_safe marks string as not requiring HTML escaping %>
<%=
@instance_text.html_safe
@instance_text.html_safe <%# $ Alert[rb/stored-xss] %>
%>
<%= render partial: 'foo/bars/widget', locals: { display_text: "widget_" + display_text } %>
@@ -43,7 +43,7 @@
<%= user_name_handle.html_safe %>
<%# BAD: Direct to a database value without escaping %>
<%= @user.handle.html_safe %>
<%= @user.handle.html_safe %> <%# $ Alert[rb/stored-xss] %>
<%# BAD: Indirect to a database value without escaping %>
<%= @user.raw_name.html_safe %>
@@ -60,7 +60,7 @@
<%# BAD: Direct to a database value without escaping %>
<%=
some_user = User.find 1
some_user.handle.html_safe
some_user.handle.html_safe <%# $ Alert[rb/stored-xss] %>
%>
<%# BAD: Indirect to a database value without escaping (currently missed due to lack of 'self' handling in ORM tracking) %>
@@ -83,7 +83,7 @@
<%# BAD: Kernel.sprintf is a taint-step %>
<%=
sprintf("%s", @user.handle).html_safe
sprintf("%s", @user.handle).html_safe <%# $ Alert[rb/stored-xss] %>
%>
<%# GOOD: The `foo.bar.baz` is not recognized as a source %>

View File

@@ -1,27 +1,27 @@
class Foobar
def create_user_description(name)
"<h2>#{name}</h2>".html_safe # NOT OK - the parameter is not escaped
def create_user_description(name) # $ Source[rb/html-constructed-from-input]
"<h2>#{name}</h2>".html_safe # $ Alert[rb/html-constructed-from-input] // NOT OK - the parameter is not escaped
# escape
"<h2>#{ERB::Util.html_escape(name)}</h2>".html_safe # OK - the parameter is escaped
end
def string_like_literal name
def string_like_literal name # $ Source[rb/html-constructed-from-input]
h = <<-HTML
<h2>#{name}</h2>
<h2>#{name}</h2> # $ Alert[rb/html-constructed-from-input]
HTML
h.html_safe # NOT OK - the parameter is not escaped
end
def sprintf_use name
sprintf("<h2>%s</h2>", name).html_safe # NOT OK - the parameter is not escaped
def sprintf_use name # $ Source[rb/html-constructed-from-input]
sprintf("<h2>%s</h2>", name).html_safe # $ Alert[rb/html-constructed-from-input] // NOT OK - the parameter is not escaped
# escape
sprintf("<h2>%s</h2>", ERB::Util.html_escape(name)).html_safe # OK - the parameter is escaped
end
def create_user_description2(name)
"<h2>#{name}</h2>".html_safe # NOT OK - the value is not necessarily HTML safe
def create_user_description2(name) # $ Source[rb/html-constructed-from-input]
"<h2>#{name}</h2>".html_safe # $ Alert[rb/html-constructed-from-input] // NOT OK - the value is not necessarily HTML safe
if name.html_safe?
"<h2>#{name}</h2>".html_safe # OK - value is marked as being HTML safe

View File

@@ -7,13 +7,13 @@ class User < ApplicationRecord
def self.authenticate(name, pass)
# BAD: possible untrusted input interpolated into SQL fragment
find(:first, :conditions => "name='#{name}' and pass='#{pass}'")
find(:first, :conditions => "name='#{name}' and pass='#{pass}'") # $ Alert
# BAD: interpolation in array argument
find(:first, conditions: ["name='#{name}' and pass='#{pass}'"])
find(:first, conditions: ["name='#{name}' and pass='#{pass}'"]) # $ Alert
# GOOD: using SQL parameters
find(:first, conditions: ["name = ? and pass = ?", name, pass])
# BAD: interpolation with flow
conds = "name=#{name}"
conds = "name=#{name}" # $ Alert
find(:first, conditions: conds)
end
@@ -27,7 +27,7 @@ class Admin < User
def self.delete_by(condition = nil)
# BAD: `delete_by overrides an ActiveRecord method, but doesn't perform
# any validation before passing its arguments on to another ActiveRecord method
destroy_by(condition)
destroy_by(condition) # $ Alert
end
end
@@ -39,64 +39,64 @@ class FooController < ActionController::Base
def some_request_handler
# BAD: executes `SELECT AVG(#{params[:column]}) FROM "users"`
# where `params[:column]` is unsanitized
User.calculate(:average, params[:column])
User.calculate(:average, params[:column]) # $ Alert
# BAD: executes `SELECT MAX(#{params[:column]}) FROM "users"`
# where `params[:column]` is unsanitized
User.maximum(params[:column])
User.maximum(params[:column]) # $ Alert
# BAD: executes `DELETE FROM "users" WHERE (id = '#{params[:id]}')`
# where `params[:id]` is unsanitized
User.delete_by("id = '#{params[:id]}'")
User.delete_by("id = '#{params[:id]}'") # $ Alert
# BAD: executes `DELETE FROM "users" WHERE (id = '#{params[:id]}')`
# where `params[:id]` is unsanitized
# (in Rails < 4.0)
User.delete_all("id = '#{params[:id]}'")
User.delete_all("id = '#{params[:id]}'") # $ Alert
# BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')`
# where `params[:id]` is unsanitized
User.destroy_by(["id = '#{params[:id]}'"])
User.destroy_by(["id = '#{params[:id]}'"]) # $ Alert
# BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')`
# where `params[:id]` is unsanitized
# (in Rails < 4.0)
User.destroy_all(["id = '#{params[:id]}'"])
User.destroy_all(["id = '#{params[:id]}'"]) # $ Alert
# BAD: executes `SELECT "users".* FROM "users" WHERE id BETWEEN '#{params[:min_id]}' AND 100000`
# where `params[:min_id]` is unsanitized
User.where(<<-SQL, MAX_USER_ID)
id BETWEEN '#{params[:min_id]}' AND ?
User.where(<<-SQL, MAX_USER_ID) # $ Alert
id BETWEEN '#{params[:min_id]}' AND ? # $ Source
SQL
# BAD: chained method case
# executes `SELECT "users".* FROM "users" WHERE (NOT (user_id = 'params[:id]'))`
# where `params[:id]` is unsanitized
User.where.not("user.id = '#{params[:id]}'")
User.where.not("user.id = '#{params[:id]}'") # $ Alert
User.authenticate(params[:name], params[:pass])
User.authenticate(params[:name], params[:pass]) # $ Source
# BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')` LIMIT 1
# where `params[:id]` is unsanitized
User.find_or_initialize_by("id = '#{params[:id]}'")
User.find_or_initialize_by("id = '#{params[:id]}'") # $ Alert
user = User.first
# BAD: executes `SELECT "users".* FROM "users" WHERE id = 1 LIMIT 1 #{params[:lock]}`
# where `params[:lock]` is unsanitized
user.reload(lock: params[:lock])
user.reload(lock: params[:lock]) # $ Alert
# BAD: executes `SELECT #{params[:column]} FROM "users"`
# where `params[:column]` is unsanitized
User.select(params[:column])
User.reselect(params[:column])
User.select(params[:column]) # $ Alert
User.reselect(params[:column]) # $ Alert
# BAD: executes `SELECT "users".* FROM "users" WHERE (#{params[:condition]})`
# where `params[:condition]` is unsanitized
User.rewhere(params[:condition])
User.rewhere(params[:condition]) # $ Alert
# BAD: executes `UPDATE "users" SET #{params[:fields]}`
# where `params[:fields]` is unsanitized
User.update_all(params[:fields])
User.update_all(params[:fields]) # $ Alert
# GOOD -- `update_all` sanitizes its bind variable arguments
User.find_by(name: params[:user_name])
@@ -104,41 +104,41 @@ class FooController < ActionController::Base
# BAD -- `update_all` does not sanitize its query (array arg)
User.find_by(name: params[:user_name])
.update_all(["name = '#{params[:new_user_name]}'"])
.update_all(["name = '#{params[:new_user_name]}'"]) # $ Alert
# BAD -- `update_all` does not sanitize its query (string arg)
User.find_by(name: params[:user_name])
.update_all("name = '#{params[:new_user_name]}'")
.update_all("name = '#{params[:new_user_name]}'") # $ Alert
User.reorder(params[:direction])
User.reorder(params[:direction]) # $ Alert
User.select('a','b', params[:column])
User.reselect('a','b', params[:column])
User.order('a ASC', "b #{params[:direction]}")
User.reorder('a ASC', "b #{params[:direction]}")
User.group('a', params[:column])
User.pluck('a', params[:column])
User.joins(:a, params[:column])
User.select('a','b', params[:column]) # $ Alert
User.reselect('a','b', params[:column]) # $ Alert
User.order('a ASC', "b #{params[:direction]}") # $ Alert
User.reorder('a ASC', "b #{params[:direction]}") # $ Alert
User.group('a', params[:column]) # $ Alert
User.pluck('a', params[:column]) # $ Alert
User.joins(:a, params[:column]) # $ Alert
User.count_by_sql(params[:custom_sql_query])
User.count_by_sql(params[:custom_sql_query]) # $ Alert
# BAD: executes `SELECT users.* FROM #{params[:tab]}`
# where `params[:tab]` is unsanitized
User.all.from(params[:tab])
User.all.from(params[:tab]) # $ Alert
# BAD: executes `SELECT "users".* FROM (SELECT "users".* FROM "users") #{params[:sq]}
User.all.from(User.all, params[:sq])
User.all.from(User.all, params[:sq]) # $ Alert
end
end
class BarController < ApplicationController
def some_other_request_handler
ps = params
ps = params # $ Source
uid = ps[:id]
uidEq = "= '#{uid}'"
# BAD: executes `DELETE FROM "users" WHERE (id = #{uid})`
# where `uid` is unsantized
User.delete_by("id " + uidEq)
User.delete_by("id " + uidEq) # $ Alert
end
def safe_paths
@@ -171,7 +171,7 @@ end
class BazController < BarController
def yet_another_handler
Admin.delete_by(params[:admin_condition])
Admin.delete_by(params[:admin_condition]) # $ Alert, Source
end
end
@@ -185,7 +185,7 @@ class AnnotatedController < ActionController::Base
def unsafe_action
name = params[:user_name]
# BAD: user input passed into annotations are vulnerable to SQLi
users = User.annotate("this is an unsafe annotation:#{params[:comment]}").find_by(user_name: name)
users = User.annotate("this is an unsafe annotation:#{params[:comment]}").find_by(user_name: name) # $ Alert
end
end
@@ -198,27 +198,27 @@ class RegressionController < ActionController::Base
def index
my_params = permitted_params
query = "SELECT * FROM users WHERE id = #{my_params[:user_id]}"
result = Regression.find_by_sql(query)
result = Regression.find_by_sql(query) # $ Alert
end
def permitted_params
params.require(:my_key).permit(:id, :user_id, :my_type)
params.require(:my_key).permit(:id, :user_id, :my_type) # $ Source
end
def show
ActiveRecord::Base.connection.execute("SELECT * FROM users WHERE id = #{permitted_params[:user_id]}")
Regression.connection.execute("SELECT * FROM users WHERE id = #{permitted_params[:user_id]}")
ActiveRecord::Base.connection.execute("SELECT * FROM users WHERE id = #{permitted_params[:user_id]}") # $ Alert
Regression.connection.execute("SELECT * FROM users WHERE id = #{permitted_params[:user_id]}") # $ Alert
end
end
class User
scope :with_role, ->(role) { where("role = #{role}") }
scope :with_role, ->(role) { where("role = #{role}") } # $ Alert
end
class UsersController < ActionController::Base
def index
# BAD: user input passed to scope which uses it without sanitization.
@users = User.with_role(params[:role])
@users = User.with_role(params[:role]) # $ Source
end
end

View File

@@ -1,9 +1,9 @@
class PotatoController < ActionController::Base
def unsafe_action
name = params[:user_name]
name = params[:user_name] # $ Source
# BAD: SQL statement constructed from user input
sql = Arel.sql("SELECT * FROM users WHERE name = #{name}")
sql = Arel::Nodes::SqlLiteral.new("SELECT * FROM users WHERE name = #{name}")
sql = Arel.sql("SELECT * FROM users WHERE name = #{name}") # $ Alert
sql = Arel::Nodes::SqlLiteral.new("SELECT * FROM users WHERE name = #{name}") # $ Alert
end
end
end

View File

@@ -3,7 +3,7 @@ class FooController < ActionController::Base
def some_request_handler
# A string tainted by user input is inserted into a query
# (i.e a remote flow source)
name = params[:name]
name = params[:name] # $ Source
# Establish a connection to a PostgreSQL database
conn = PG::Connection.open(:dbname => 'postgresql', :user => 'user', :password => 'pass', :host => 'localhost', :port => '5432')
@@ -11,14 +11,14 @@ class FooController < ActionController::Base
# .exec() and .async_exec()
# BAD: SQL statement constructed from user input
qry1 = "SELECT * FROM users WHERE username = '#{name}';"
conn.exec(qry1)
conn.async_exec(qry1)
conn.exec(qry1) # $ Alert
conn.async_exec(qry1) # $ Alert
# .exec_params() and .async_exec_params()
# BAD: SQL statement constructed from user input
qry2 = "SELECT * FROM users WHERE username = '#{name}';"
conn.exec_params(qry2)
conn.async_exec_params(qry2)
conn.exec_params(qry2) # $ Alert
conn.async_exec_params(qry2) # $ Alert
# .exec_params() and .async_exec_params()
# GOOD: SQL statement constructed from sanitized user input
@@ -29,7 +29,7 @@ class FooController < ActionController::Base
# .prepare() and .exec_prepared()
# BAD: SQL statement constructed from user input
qry3 = "SELECT * FROM users WHERE username = '#{name}';"
conn.prepare("query_1", qry3)
conn.prepare("query_1", qry3) # $ Alert
conn.exec_prepared('query_1')
# .prepare() and .exec_prepared()
@@ -41,7 +41,7 @@ class FooController < ActionController::Base
# .prepare() and .exec_prepared()
# NOT EXECUTED: SQL statement constructed from user input but not executed
qry3 = "SELECT * FROM users WHERE username = '#{name}';"
conn.prepare("query_3", qry3)
conn.prepare("query_3", qry3) # $ Alert
end
end

View File

@@ -1 +1,2 @@
queries/security/cwe-089/SqlInjection.ql
query: queries/security/cwe-089/SqlInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1 +1,2 @@
queries/security/cwe-094/UnsafeCodeConstruction.ql
query: queries/security/cwe-094/UnsafeCodeConstruction.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,16 +1,16 @@
class Foobar
def foo1(target)
eval("foo = #{target}") # NOT OK
def foo1(target) # $ Source
eval("foo = #{target}") # $ Alert // NOT OK
end
# sprintf
def foo2(x)
eval(sprintf("foo = %s", x)) # NOT OK
def foo2(x) # $ Source
eval(sprintf("foo = %s", x)) # $ Alert // NOT OK
end
# String#%
def foo3(x)
eval("foo = %{foo}" % {foo: x}) # NOT OK
def foo3(x) # $ Source
eval("foo = %{foo}" % {foo: x}) # $ Alert // NOT OK
end
def indirect_eval(x)
@@ -25,42 +25,42 @@ class Foobar
eval("def \n #{code} \n end") # OK - parameter is named code
end
def joinStuff(my_arr)
eval(my_arr.join("\n")) # NOT OK
def joinStuff(my_arr) # $ Source
eval(my_arr.join("\n")) # $ Alert // NOT OK
end
def joinWithElemt(x)
def joinWithElemt(x) # $ Source
arr = [x, "foobar"]
eval(arr.join("\n")) # NOT OK
eval(arr.join("\n")) # $ Alert // NOT OK
end
def pushArr(x, y)
def pushArr(x, y) # $ Source
arr = []
arr.push(x)
eval(arr.join("\n")) # NOT OK
eval(arr.join("\n")) # $ Alert // NOT OK
arr2 = []
arr2 << y
eval(arr.join("\n")) # NOT OK
eval(arr.join("\n")) # $ Alert // NOT OK
end
def hereDoc(x)
def hereDoc(x) # $ Source
foo = <<~HERE
#{x}
#{x} # $ Alert
HERE
eval(foo) # NOT OK
end
def string_concat(x)
foo = "foo = " + x
def string_concat(x) # $ Source
foo = "foo = " + x # $ Alert
eval(foo) # NOT OK
end
def join_indirect(x, y)
def join_indirect(x, y) # $ Source
arr = Array(x)
eval(arr.join(" ")) # NOT OK
eval(arr.join(" ")) # $ Alert // NOT OK
arr2 = [Array(["foo = ", y]).join(" ")]
eval(arr2.join("\n")) # NOT OK
eval(arr2.join("\n")) # $ Alert // NOT OK
end
end

View File

@@ -1 +1,2 @@
queries/security/cwe-116/BadTagFilter.ql
query: queries/security/cwe-116/BadTagFilter.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,22 +1,22 @@
filters = [
/<script.*?>.*?<\/script>/i, # NOT OK - doesn't match newlines or `</script >`
/<script.*?>.*?<\/script>/im, # NOT OK - doesn't match `</script >`
/<script.*?>.*?<\/script>/i, # $ Alert // NOT OK - doesn't match newlines or `</script >`
/<script.*?>.*?<\/script>/im, # $ Alert // NOT OK - doesn't match `</script >`
/<script.*?>.*?<\/script[^>]*>/im, # OK
/<!--.*-->/im, # OK - we don't care regexps that only match comments
/<!--.*--!?>/im, # OK
/<!--.*--!?>/i, # NOT OK, does not match newlines
/<script.*?>(.|\s)*?<\/script[^>]*>/i, # NOT OK - doesn't match inside the script tag
/<script[^>]*?>.*?<\/script[^>]*>/i, # NOT OK - doesn't match newlines inside the content
/<script(\s|\w|=|")*?>.*?<\/script[^>]*>/im, # NOT OK - does not match single quotes for attribute values
/<script(\s|\w|=|')*?>.*?<\/script[^>]*>/im, # NOT OK - does not match double quotes for attribute values
/<script( |\n|\w|=|'|")*?>.*?<\/script[^>]*>/im, # NOT OK - does not match tabs between attributes
/<script.*?>.*?<\/script[^>]*>/m, # NOT OK - does not match uppercase SCRIPT tags
/<(script|SCRIPT).*?>.*?<\/(script|SCRIPT)[^>]*>/m, # NOT OK - does not match mixed case script tags
/<script[^>]*?>[\s\S]*?<\/script.*>/i, # NOT OK - doesn't match newlines in the end tag
/<!--.*--!?>/i, # $ Alert // NOT OK, does not match newlines
/<script.*?>(.|\s)*?<\/script[^>]*>/i, # $ Alert // NOT OK - doesn't match inside the script tag
/<script[^>]*?>.*?<\/script[^>]*>/i, # $ Alert // NOT OK - doesn't match newlines inside the content
/<script(\s|\w|=|")*?>.*?<\/script[^>]*>/im, # $ Alert // NOT OK - does not match single quotes for attribute values
/<script(\s|\w|=|')*?>.*?<\/script[^>]*>/im, # $ Alert // NOT OK - does not match double quotes for attribute values
/<script( |\n|\w|=|'|")*?>.*?<\/script[^>]*>/im, # $ Alert // NOT OK - does not match tabs between attributes
/<script.*?>.*?<\/script[^>]*>/m, # $ Alert // NOT OK - does not match uppercase SCRIPT tags
/<(script|SCRIPT).*?>.*?<\/(script|SCRIPT)[^>]*>/m, # $ Alert // NOT OK - does not match mixed case script tags
/<script[^>]*?>[\s\S]*?<\/script.*>/i, # $ Alert // NOT OK - doesn't match newlines in the end tag
/<script[^>]*?>[\s\S]*?<\/script[^>]*?>/i, # OK
/<script\b[^>]*>([\s\S]*?)<\/script>/gi, # NOT OK - too strict matching on the end tag
/<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>/, # NOT OK - doesn't match comments with the right capture groups
/<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))/, # NOT OK - capture groups
/<script\b[^>]*>([\s\S]*?)<\/script>/gi, # $ Alert // NOT OK - too strict matching on the end tag
/<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>/, # $ Alert // NOT OK - doesn't match comments with the right capture groups
/<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))/, # $ Alert // NOT OK - capture groups
]
doFilters(filters)
doFilters(filters)

View File

@@ -1 +1,2 @@
queries/security/cwe-116/IncompleteSanitization.ql
query: queries/security/cwe-116/IncompleteSanitization.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,91 +1,91 @@
def bad1(s)
s.sub "'", "" # NOT OK
s.sub! "'", "" # NOT OK
s.sub "'", "" # $ Alert // NOT OK
s.sub! "'", "" # $ Alert // NOT OK
end
def bad2(s)
s.sub /'/, "" # NOT OK
s.sub! /'/, "" # NOT OK
s.sub /'/, "" # $ Alert // NOT OK
s.sub! /'/, "" # $ Alert // NOT OK
end
def bad3(s1, s2, s3)
s1.gsub /'/, "\\'" # NOT OK
s1.gsub /'/, '\\\'' # NOT OK
s2.gsub! /'/, "\\'" # NOT OK
s3.gsub! /'/, '\\\'' # NOT OK
s1.gsub /'/, "\\'" # $ Alert // NOT OK
s1.gsub /'/, '\\\'' # $ Alert // NOT OK
s2.gsub! /'/, "\\'" # $ Alert // NOT OK
s3.gsub! /'/, '\\\'' # $ Alert // NOT OK
end
def bad4(s1, s2, s3)
s1.gsub /'/, "\\\\\\&" # NOT OK
s1.gsub /'/, '\\\\\&' # NOT OK
s2.gsub! /'/, "\\\\\\&" # NOT OK
s3.gsub! /'/, '\\\\\&' # NOT OK
s1.gsub /'/, "\\\\\\&" # $ Alert // NOT OK
s1.gsub /'/, '\\\\\&' # $ Alert // NOT OK
s2.gsub! /'/, "\\\\\\&" # $ Alert // NOT OK
s3.gsub! /'/, '\\\\\&' # $ Alert // NOT OK
end
def bad5(s)
s.gsub /['"]/, '\\\\\&' # NOT OK
s.gsub! /['"]/, '\\\\\&' # NOT OK
s.gsub /['"]/, '\\\\\&' # $ Alert // NOT OK
s.gsub! /['"]/, '\\\\\&' # $ Alert // NOT OK
end
def bad6(s)
s.gsub /(['"])/, '\\\\\\1' # NOT OK
s.gsub! /(['"])/, '\\\\\\1' # NOT OK
s.gsub /(['"])/, '\\\\\\1' # $ Alert // NOT OK
s.gsub! /(['"])/, '\\\\\\1' # $ Alert // NOT OK
end
def bad7(s)
s.gsub /('|")/, '\\\\\1' # NOT OK
s.gsub! /('|")/, '\\\\\1' # NOT OK
s.gsub /('|")/, '\\\\\1' # $ Alert // NOT OK
s.gsub! /('|")/, '\\\\\1' # $ Alert // NOT OK
end
def bad8(s)
s.sub '|', '' # NOT OK
s.sub! '|', '' # NOT OK
s.sub '|', '' # $ Alert // NOT OK
s.sub! '|', '' # $ Alert // NOT OK
end
def bad9(s1, s2, s3, s4)
s1.gsub /"/, "\\\"" # NOT OK
s1.gsub /"/, '\\"' # NOT OK
s1.gsub '"', '\\"' # NOT OK
s2.gsub! /"/, "\\\"" # NOT OK
s3.gsub! /"/, '\\"' # NOT OK
s4.gsub! '"', '\\"' # NOT OK
s1.gsub /"/, "\\\"" # $ Alert // NOT OK
s1.gsub /"/, '\\"' # $ Alert // NOT OK
s1.gsub '"', '\\"' # $ Alert // NOT OK
s2.gsub! /"/, "\\\"" # $ Alert // NOT OK
s3.gsub! /"/, '\\"' # $ Alert // NOT OK
s4.gsub! '"', '\\"' # $ Alert // NOT OK
end
def bad10(s)
s.sub "/", "%2F" # NOT OK
s.sub! "/", "%2F" # NOT OK
s.sub "/", "%2F" # $ Alert // NOT OK
s.sub! "/", "%2F" # $ Alert // NOT OK
end
def bad11(s)
s.sub "%25", "%" # NOT OK
s.sub! "%25", "%" # NOT OK
s.sub "%25", "%" # $ Alert // NOT OK
s.sub! "%25", "%" # $ Alert // NOT OK
end
def bad12(s)
s.sub %q['], %q[] # NOT OK
s.sub! %q['], %q[] # NOT OK
s.sub %q['], %q[] # $ Alert // NOT OK
s.sub! %q['], %q[] # $ Alert // NOT OK
end
def bad13(s)
s.sub "'" + "", "" # NOT OK
s.sub! "'" + "", "" # NOT OK
s.sub "'" + "", "" # $ Alert // NOT OK
s.sub! "'" + "", "" # $ Alert // NOT OK
end
def bad14(s)
s.sub "'", "" + "" # NOT OK
s.sub! "'", "" + "" # NOT OK
s.sub "'", "" + "" # $ Alert // NOT OK
s.sub! "'", "" + "" # $ Alert // NOT OK
end
def bad15(s)
s.sub "'" + "", "" + "" # NOT OK
s.sub! "'" + "", "" + "" # NOT OK
s.sub "'" + "", "" + "" # $ Alert // NOT OK
s.sub! "'" + "", "" + "" # $ Alert // NOT OK
end
def bad16(s)
indirect = /'/
s.sub(indirect, "") # NOT OK
s.sub!(indirect, "") # NOT OK
s.sub(indirect, "") # $ Alert // NOT OK
s.sub!(indirect, "") # $ Alert // NOT OK
end
def good1a(s)
@@ -212,15 +212,15 @@ def good13a(s)
s.sub('[', '').sub(']', '') # OK
s.sub('(', '').sub(')', '') # OK
s.sub('{', '').sub('}', '') # OK
s.sub('<', '').sub('>', '') # NOT OK: too common as a bad HTML sanitizer
s.sub('<', '').sub('>', '') # $ Alert // NOT OK: too common as a bad HTML sanitizer
s.sub('[', '\\[').sub(']', '\\]') # NOT OK
s.sub('{', '\\{').sub('}', '\\}') # NOT OK
s.sub('[', '\\[').sub(']', '\\]') # $ Alert // NOT OK
s.sub('{', '\\{').sub('}', '\\}') # $ Alert // NOT OK
s = s.sub('[', '') # OK
s = s.sub(']', '') # OK
s.sub(/{/, '').sub(/}/, '') # OK
s.sub(']', '').sub('[', '') # probably OK, but still flagged
s.sub(']', '').sub('[', '') # $ Alert // probably OK, but still flagged
end
def good13b(s1)
@@ -245,8 +245,8 @@ def newlines_a(a, b, c)
# motivation for whitelist
`which emacs`.sub("\n", "") # OK
a.sub("\n", "").sub(b, c) # NOT OK
a.sub(b, c).sub("\n", "") # NOT OK
a.sub("\n", "").sub(b, c) # $ Alert // NOT OK
a.sub(b, c).sub("\n", "") # $ Alert // NOT OK
end
def newlines_b(a, b, c)
@@ -255,18 +255,18 @@ def newlines_b(a, b, c)
output.sub!("\n", "") # OK
d = a.dup
d.sub!("\n", "") # NOT OK
d.sub!("\n", "") # $ Alert // NOT OK
d.sub!(b, c)
e = a.dup
d.sub!(b, c)
d.sub!("\n", "") # NOT OK
d.sub!("\n", "") # $ Alert // NOT OK
end
def bad_path_sanitizer(p1, p2)
# attempt at path sanitization
p1.sub! "/../", "" # NOT OK
p2.sub "/../", "" # NOT OK
p1.sub! "/../", "" # $ Alert // NOT OK
p2.sub "/../", "" # $ Alert // NOT OK
end
def each_line_sanitizer(p1)

View File

@@ -1 +1,2 @@
queries/security/cwe-117/LogInjection.ql
query: queries/security/cwe-117/LogInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -12,9 +12,9 @@ class UsersController < ApplicationController
def read_from_params
init_logger
unsanitized = params[:foo]
@logger.debug unsanitized # BAD: unsanitized user input
@logger.error "input: " + unsanitized # BAD: unsanitized user input
unsanitized = params[:foo] # $ Source
@logger.debug unsanitized # $ Alert // BAD: unsanitized user input
@logger.error "input: " + unsanitized # $ Alert // BAD: unsanitized user input
sanitized = unsanitized.gsub("\n", "")
@logger.fatal sanitized # GOOD: sanitized user input
@@ -22,17 +22,17 @@ class UsersController < ApplicationController
unsanitized2 = unsanitized.sub("\n", "")
@logger.info do
unsanitized2 # BAD: partially sanitized user input
unsanitized2 # $ Alert // BAD: partially sanitized user input
end
@logger << "input: " + unsanitized2 # BAD: partially sanitized user input
@logger << "input: " + unsanitized2 # $ Alert // BAD: partially sanitized user input
end
def read_from_cookies
init_logger
unsanitized = cookies[:bar]
@logger.add(Logger::INFO) { unsanitized } # BAD: unsanitized user input
@logger.log(Logger::WARN) { "input: " + unsanitized } # BAD: unsanitized user input
unsanitized = cookies[:bar] # $ Source
@logger.add(Logger::INFO) { unsanitized } # $ Alert // BAD: unsanitized user input
@logger.log(Logger::WARN) { "input: " + unsanitized } # $ Alert // BAD: unsanitized user input
end
def html_sanitization
@@ -46,7 +46,7 @@ class UsersController < ApplicationController
def inspect_sanitization
init_logger
@logger.debug params[:foo] # BAD: unsanitized user input
@logger.debug params[:foo] # $ Alert // BAD: unsanitized user input
@logger.debug params[:foo].inspect # GOOD: sanitized user input
end
end

View File

@@ -1 +1,2 @@
queries/security/cwe-1333/ReDoS.ql
query: queries/security/cwe-1333/ReDoS.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,7 +1,7 @@
# NOT GOOD; attack: "_" + "__".repeat(100)
# Adapted from marked (https://github.com/markedjs/marked), which is licensed
# under the MIT license; see file marked-LICENSE.
bad1 = /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/
bad1 = /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/ # $ Alert
# GOOD
# Adapted from marked (https://github.com/markedjs/marked), which is licensed
@@ -16,7 +16,7 @@ good2 = /(.*,)+.+/
# NOT GOOD; attack: " '" + "\\\\".repeat(100)
# Adapted from CodeMirror (https://github.com/codemirror/codemirror),
# which is licensed under the MIT license; see file CodeMirror-LICENSE.
bad2 = /^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/
bad2 = /^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/ # $ Alert
# GOOD
# Adapted from lulucms2 (https://github.com/yiifans/lulucms2).
@@ -28,89 +28,89 @@ good2 = /\(\*(?:[\s\S]*?\(\*[\s\S]*?\*\))*[\s\S]*?\*\)/
good3 = /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/
# NOT GOOD, variant of good3; attack: "a|\n:|\n" + "||\n".repeat(100)
bad4 = /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)a/
bad4 = /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)a/ # $ Alert
# NOT GOOD; attack: "/" + "\\/a".repeat(100)
# Adapted from ANodeBlog (https://github.com/gefangshuai/ANodeBlog),
# which is licensed under the Apache License 2.0; see file ANodeBlog-LICENSE.
bad5 = /\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/
bad5 = /\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/ # $ Alert
# NOT GOOD; attack: "##".repeat(100) + "\na"
# Adapted from CodeMirror (https://github.com/codemirror/codemirror),
# which is licensed under the MIT license; see file CodeMirror-LICENSE.
bad6 = /^([\s\[\{\(]|#.*)*$/
bad6 = /^([\s\[\{\(]|#.*)*$/ # $ Alert
# GOOD
good4 = /(\r\n|\r|\n)+/
# BAD - PoC: `node -e "/((?:[^\"\']|\".*?\"|\'.*?\')*?)([(,)]|$)/.test(\"'''''''''''''''''''''''''''''''''''''''''''''\\\"\");"`. It's complicated though, because the regexp still matches something, it just matches the empty-string after the attack string.
actuallyBad = /((?:[^"']|".*?"|'.*?')*?)([(,)]|$)/
actuallyBad = /((?:[^"']|".*?"|'.*?')*?)([(,)]|$)/ # $ Alert
# NOT GOOD; attack: "a" + "[]".repeat(100) + ".b\n"
# Adapted from Knockout (https://github.com/knockout/knockout), which is
# licensed under the MIT license; see file knockout-LICENSE
bad6 = /^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$/i
bad6 = /^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$/i # $ Alert
# GOOD
good6 = /(a|.)*/
# Testing the NFA - only some of the below are detected.
bad7 = /^([a-z]+)+$/
bad8 = /^([a-z]*)*$/
bad9 = /^([a-zA-Z0-9])(([\\.-]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/
bad10 = /^(([a-z])+.)+[A-Z]([a-z])+$/
bad7 = /^([a-z]+)+$/ # $ Alert
bad8 = /^([a-z]*)*$/ # $ Alert
bad9 = /^([a-zA-Z0-9])(([\\.-]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/ # $ Alert
bad10 = /^(([a-z])+.)+[A-Z]([a-z])+$/ # $ Alert
# NOT GOOD; attack: "[" + "][".repeat(100) + "]!"
# Adapted from Prototype.js (https://github.com/prototypejs/prototype), which
# is licensed under the MIT license; see file Prototype.js-LICENSE.
bad11 = /(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/
bad11 = /(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/ # $ Alert
# NOT GOOD; attack: "'" + "\\a".repeat(100) + '"'
# Adapted from Prism (https://github.com/PrismJS/prism), which is licensed
# under the MIT license; see file Prism-LICENSE.
bad12 = /("|')(\\?.)*?\1/
bad12 = /("|')(\\?.)*?\1/ # $ Alert
# NOT GOOD
bad13 = /(b|a?b)*c/
bad13 = /(b|a?b)*c/ # $ Alert
# NOT GOOD
bad15 = /(a|aa?)*b/
bad15 = /(a|aa?)*b/ # $ Alert
# GOOD
good7 = /(.|\n)*!/
# NOT GOOD; attack: "\n".repeat(100) + "."
bad16 = /(.|\n)*!/m
bad16 = /(.|\n)*!/m # $ Alert
# GOOD
good8 = /([\w.]+)*/
# NOT GOOD
bad17 = Regexp.new '(a|aa?)*b'
bad17 = Regexp.new '(a|aa?)*b' # $ Alert
# GOOD - not used as regexp
good9 = '(a|aa?)*b'
# NOT GOOD
bad18 = /(([\S\s]|[^a])*)"/
bad18 = /(([\S\s]|[^a])*)"/ # $ Alert
# GOOD - there is no witness in the end that could cause the regexp to not match
good10 = /([^"']+)*/
# NOT GOOD
bad20 = /((.|[^a])*)"/
bad20 = /((.|[^a])*)"/ # $ Alert
# GOOD
good10 = /((a|[^a])*)"/
# NOT GOOD
bad21 = /((b|[^a])*)"/
bad21 = /((b|[^a])*)"/ # $ Alert
# NOT GOOD
bad22 = /((G|[^a])*)"/
bad22 = /((G|[^a])*)"/ # $ Alert
# NOT GOOD
bad23 = /(([0-9]|[^a])*)"/
bad23 = /(([0-9]|[^a])*)"/ # $ Alert
# BAD - missing result
bad24 = /(?:=(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"))?/
@@ -122,55 +122,55 @@ bad25 = /"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"/
bad26 = /"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*)"/
# NOT GOOD
bad27 = /(([a-z]|[d-h])*)"/
bad27 = /(([a-z]|[d-h])*)"/ # $ Alert
# NOT GOOD
bad27 = /(([^a-z]|[^0-9])*)"/
bad27 = /(([^a-z]|[^0-9])*)"/ # $ Alert
# NOT GOOD
bad28 = /((\d|[0-9])*)"/
bad28 = /((\d|[0-9])*)"/ # $ Alert
# NOT GOOD
bad29 = /((\s|\s)*)"/
bad29 = /((\s|\s)*)"/ # $ Alert
# NOT GOOD
bad30 = /((\w|G)*)"/
bad30 = /((\w|G)*)"/ # $ Alert
# GOOD
good11 = /((\s|\d)*)"/
# NOT GOOD
bad31 = /((\d|\w)*)"/
bad31 = /((\d|\w)*)"/ # $ Alert
# NOT GOOD
bad32 = /((\d|5)*)"/
bad32 = /((\d|5)*)"/ # $ Alert
# BAD - \f is not handled correctly
bad33 = /((\s|[\f])*)"/
bad33 = /((\s|[\f])*)"/ # $ Alert
# BAD - \v is not handled correctly
bad34 = /((\s|[\v]|\\v)*)"/
bad34 = /((\s|[\v]|\\v)*)"/ # $ Alert
# NOT GOOD
bad35 = /((\f|[\f])*)"/
bad35 = /((\f|[\f])*)"/ # $ Alert
# NOT GOOD
bad36 = /((\W|\D)*)"/
bad36 = /((\W|\D)*)"/ # $ Alert
# NOT GOOD
bad37 = /((\S|\w)*)"/
bad37 = /((\S|\w)*)"/ # $ Alert
# NOT GOOD
bad38 = /((\S|[\w])*)"/
bad38 = /((\S|[\w])*)"/ # $ Alert
# NOT GOOD
bad39 = /((1s|[\da-z])*)"/
bad39 = /((1s|[\da-z])*)"/ # $ Alert
# NOT GOOD
bad40 = /((0|[\d])*)"/
bad40 = /((0|[\d])*)"/ # $ Alert
# NOT GOOD
bad41 = /(([\d]+)*)"/
bad41 = /(([\d]+)*)"/ # $ Alert
# GOOD - there is no witness in the end that could cause the regexp to not match
good12 = /(\d+(X\d+)?)+/
@@ -182,49 +182,49 @@ good13 = /([0-9]+(X[0-9]*)?)*/
good15 = /^([^>]+)*(>|$)/
# NOT GOOD
bad43 = /^([^>a]+)*(>|$)/
bad43 = /^([^>a]+)*(>|$)/ # $ Alert
# NOT GOOD
bad44 = /(\n\s*)+$/
bad44 = /(\n\s*)+$/ # $ Alert
# NOT GOOD
bad45 = /^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/
bad45 = /^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/ # $ Alert
# NOT GOOD
bad46 = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}/
bad46 = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}/ # $ Alert
# NOT GOOD
bad47 = /(a+|b+|c+)*c/
bad47 = /(a+|b+|c+)*c/ # $ Alert
# NOT GOOD
bad48 = /(((a+a?)*)+b+)/
bad48 = /(((a+a?)*)+b+)/ # $ Alert
# NOT GOOD
bad49 = /(a+)+bbbb/
bad49 = /(a+)+bbbb/ # $ Alert
# GOOD
good16 = /(a+)+aaaaa*a+/
# NOT GOOD
bad50 = /(a+)+aaaaa$/
bad50 = /(a+)+aaaaa$/ # $ Alert
# GOOD
good17 = /(\n+)+\n\n/
# NOT GOOD
bad51 = /(\n+)+\n\n$/
bad51 = /(\n+)+\n\n$/ # $ Alert
# NOT GOOD
bad52 = /([^X]+)*$/
bad52 = /([^X]+)*$/ # $ Alert
# NOT GOOD
bad53 = /(([^X]b)+)*$/
bad53 = /(([^X]b)+)*$/ # $ Alert
# GOOD
good18 = /(([^X]b)+)*($|[^X]b)/
# NOT GOOD
bad54 = /(([^X]b)+)*($|[^X]c)/
bad54 = /(([^X]b)+)*($|[^X]c)/ # $ Alert
# GOOD
good20 = /((ab)+)*ababab/
@@ -236,13 +236,13 @@ good21 = /((ab)+)*abab(ab)*(ab)+/
good22 = /((ab)+)*/
# NOT GOOD
bad55 = /((ab)+)*$/
bad55 = /((ab)+)*$/ # $ Alert
# GOOD
good23 = /((ab)+)*[a1][b1][a2][b2][a3][b3]/
# NOT GOOD
bad56 = /([\n\s]+)*(.)/
bad56 = /([\n\s]+)*(.)/ # $ Alert
# GOOD - any witness passes through the accept state.
good24 = /(A*A*X)*/
@@ -251,13 +251,13 @@ good24 = /(A*A*X)*/
good26 = /([^\\\]]+)*/
# NOT GOOD
bad59 = /(\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+-/
bad59 = /(\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+-/ # $ Alert
# NOT GOOD
bad60 = /(.thisisagoddamnlongstringforstresstestingthequery|\sthisisagoddamnlongstringforstresstestingthequery)*-/
bad60 = /(.thisisagoddamnlongstringforstresstestingthequery|\sthisisagoddamnlongstringforstresstestingthequery)*-/ # $ Alert
# NOT GOOD
bad61 = /(thisisagoddamnlongstringforstresstestingthequery|this\w+query)*-/
bad61 = /(thisisagoddamnlongstringforstresstestingthequery|this\w+query)*-/ # $ Alert
# GOOD
good27 = /(thisisagoddamnlongstringforstresstestingthequery|imanotherbutunrelatedstringcomparedtotheotherstring)*-/
@@ -269,58 +269,58 @@ good27 = /(thisisagoddamnlongstringforstresstestingthequery|imanotherbutunrelate
#good29 = /foo((\uDC66|\uDC67)|(\uDC68|\uDC69))*foo/
# NOT GOOD (but cannot currently construct a prefix)
bad62 = /a{2,3}(b+)+X/
bad62 = /a{2,3}(b+)+X/ # $ Alert
# NOT GOOD (and a good prefix test)
bad63 = /^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/
bad63 = /^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/ # $ Alert
# GOOD
good30 = /(a+)*[\S\s][\S\s][\S\s]?/
# GOOD - but we fail to see that repeating the attack string ends in the "accept any" state (due to not parsing the range `[^]{2,3}`).
good31 = /(a+)*[\S\s]{2,3}/
good31 = /(a+)*[\S\s]{2,3}/ # $ Alert
# GOOD - but we spuriously conclude that a rejecting suffix exists (due to not parsing the range `[^]{2,}` when constructing the NFA).
good32 = /(a+)*([\S\s]{2,}|X)$/
good32 = /(a+)*([\S\s]{2,}|X)$/ # $ Alert
# GOOD
good33 = /(a+)*([\S\s]*|X)$/
# NOT GOOD
bad64 = /((a+)*$|[\S\s]+)/
bad64 = /((a+)*$|[\S\s]+)/ # $ Alert
# GOOD - but still flagged. The only change compared to the above is the order of alternatives, which we don't model.
good34 = /([\S\s]+|(a+)*$)/
good34 = /([\S\s]+|(a+)*$)/ # $ Alert
# GOOD
good35 = /((;|^)a+)+$/
# NOT GOOD (a good prefix test)
bad65 = /(^|;)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(e+)+f/
bad65 = /(^|;)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(e+)+f/ # $ Alert
# NOT GOOD
bad66 = /^ab(c+)+$/
bad66 = /^ab(c+)+$/ # $ Alert
# NOT GOOD
bad67 = /(\d(\s+)*){20}/
bad67 = /(\d(\s+)*){20}/ # $ Alert
# GOOD - but we spuriously conclude that a rejecting suffix exists.
good36 = /(([^\/]|X)+)(\/[\S\s]*)*$/
good36 = /(([^\/]|X)+)(\/[\S\s]*)*$/ # $ Alert
# GOOD - but we spuriously conclude that a rejecting suffix exists.
good37 = /^((x([^Y]+)?)*(Y|$))/
good37 = /^((x([^Y]+)?)*(Y|$))/ # $ Alert
# NOT GOOD
bad68 = /(a*)+b/
bad68 = /(a*)+b/ # $ Alert
# NOT GOOD
bad69 = /foo([\w-]*)+bar/
bad69 = /foo([\w-]*)+bar/ # $ Alert
# NOT GOOD
bad70 = /((ab)*)+c/
bad70 = /((ab)*)+c/ # $ Alert
# NOT GOOD
bad71 = /(a?a?)*b/
bad71 = /(a?a?)*b/ # $ Alert
# GOOD
good38 = /(a?)*b/
@@ -329,54 +329,54 @@ good38 = /(a?)*b/
bad72 = /(c?a?)*b/
# NOT GOOD
bad73 = /(?:a|a?)+b/
bad73 = /(?:a|a?)+b/ # $ Alert
# NOT GOOD - but not detected.
bad74 = /(a?b?)*$/
# NOT GOOD
bad76 = /PRE(([a-c]|[c-d])T(e?e?e?e?|X))+(cTcT|cTXcTX$)/
bad76 = /PRE(([a-c]|[c-d])T(e?e?e?e?|X))+(cTcT|cTXcTX$)/ # $ Alert
# NOT GOOD - but not detected
bad77 = /^((a)+\w)+$/
bad77 = /^((a)+\w)+$/ # $ Alert
# NOT GOOD
bad78 = /^(b+.)+$/
bad78 = /^(b+.)+$/ # $ Alert
# GOOD
good39 = /a*b/
# All 4 bad combinations of nested * and +
bad79 = /(a*)*b/
bad80 = /(a+)*b/
bad81 = /(a*)+b/
bad82 = /(a+)+b/
bad79 = /(a*)*b/ # $ Alert
bad80 = /(a+)*b/ # $ Alert
bad81 = /(a*)+b/ # $ Alert
bad82 = /(a+)+b/ # $ Alert
# GOOD
good40 = /(a|b)+/
good41 = /(?:[\s;,"'<>(){}|\[\]@=+*]|:(?![\/\\]))+/
# NOT GOOD
bad83 = /^((?:a{|-)|\w\{)+X$/
bad84 = /^((?:a{0|-)|\w\{\d)+X$/
bad85 = /^((?:a{0,|-)|\w\{\d,)+X$/
bad86 = /^((?:a{0,2|-)|\w\{\d,\d)+X$/
bad83 = /^((?:a{|-)|\w\{)+X$/ # $ Alert
bad84 = /^((?:a{0|-)|\w\{\d)+X$/ # $ Alert
bad85 = /^((?:a{0,|-)|\w\{\d,)+X$/ # $ Alert
bad86 = /^((?:a{0,2|-)|\w\{\d,\d)+X$/ # $ Alert
# NOT GOOD
bad87 = /^((?:a{0,2}|-)|\w\{\d,\d\})+X$/
# NOT GOOD
bad88 = /^X(\u0061|a)*Y$/
bad88 = /^X(\u0061|a)*Y$/ # $ Alert
# GOOD
good43 = /^X(\u0061|b)+Y$/
# NOT GOOD
bad88 = /X([[:digit:]]|\d)+Y/
bad88 = /X([[:digit:]]|\d)+Y/ # $ Alert
# NOT GOOD
bad89 = /\G(a|\w)*$/
bad90 = /\b(a|\w)*$/
bad89 = /\G(a|\w)*$/ # $ Alert
bad90 = /\b(a|\w)*$/ # $ Alert
# NOT GOOD; attack: "0".repeat(30) + "!"
# Adapated from addressable (https://github.com/sporkmonger/addressable)
@@ -387,5 +387,5 @@ module Bad91
var_char_class = ALPHA + DIGIT + '_'
var_char = "(?:(?:[#{var_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)"
var = "(?:#{var_char}(?:\\.?#{var_char})*)"
bad91 = /^#{var}$/
bad91 = /^#{var}$/ # $ Alert
end

View File

@@ -1 +1,2 @@
queries/security/cwe-1333/PolynomialReDoS.ql
query: queries/security/cwe-1333/PolynomialReDoS.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,35 +1,35 @@
class FooController < ActionController::Base
def some_request_handler
# A source for the data-flow query (i.e. a remote flow source)
name = params[:name]
name = params[:name] # $ Source
# A vulnerable regex
regex = /^\s+|\s+$/
# Various sinks that match the source against the regex
name =~ regex # NOT GOOD
name !~ regex # NOT GOOD
name[regex] # NOT GOOD
name.gsub regex, '' # NOT GOOD
name.index regex # NOT GOOD
name.match regex # NOT GOOD
name.match? regex # NOT GOOD
name.partition regex # NOT GOOD
name.rindex regex # NOT GOOD
name.rpartition regex # NOT GOOD
name.scan regex # NOT GOOD
name.split regex # NOT GOOD
name.sub regex, '' # NOT GOOD
regex.match name # NOT GOOD
regex.match? name # NOT GOOD
name =~ regex # $ Alert // NOT GOOD
name !~ regex # $ Alert // NOT GOOD
name[regex] # $ Alert // NOT GOOD
name.gsub regex, '' # $ Alert // NOT GOOD
name.index regex # $ Alert // NOT GOOD
name.match regex # $ Alert // NOT GOOD
name.match? regex # $ Alert // NOT GOOD
name.partition regex # $ Alert // NOT GOOD
name.rindex regex # $ Alert // NOT GOOD
name.rpartition regex # $ Alert // NOT GOOD
name.scan regex # $ Alert // NOT GOOD
name.split regex # $ Alert // NOT GOOD
name.sub regex, '' # $ Alert // NOT GOOD
regex.match name # $ Alert // NOT GOOD
regex.match? name # $ Alert // NOT GOOD
# Destructive variants
a = params[:b]
a.gsub! regex, '' # NOT GOOD
b = params[:a]
b.slice! regex # NOT GOOD
c = params[:c]
c.sub! regex, '' # NOT GOOD
a = params[:b] # $ Source
a.gsub! regex, '' # $ Alert // NOT GOOD
b = params[:a] # $ Source
b.slice! regex # $ Alert // NOT GOOD
c = params[:c] # $ Source
c.sub! regex, '' # $ Alert // NOT GOOD
# GOOD - guarded by a string length check
if name.length < 1024
@@ -39,19 +39,19 @@ class FooController < ActionController::Base
# GOOD - regex does not suffer from polynomial backtracking (regression test)
params[:foo] =~ /\A[bc].*\Z/
case name # NOT GOOD
case name # $ Sink // NOT GOOD
when regex
puts "foo"
end
end # $ Alert
case name # NOT GOOD
case name # $ Sink // NOT GOOD
in /^\s+|\s+$/ then
puts "foo"
end
end # $ Alert
end
def some_other_request_handle
name = params[:name] # source
name = params[:name] # $ Source // source
indirect_use_of_reg /^\s+|\s+$/, name
@@ -59,22 +59,22 @@ class FooController < ActionController::Base
end
def indirect_use_of_reg (reg, input)
input.gsub reg, '' # NOT GOOD
input.gsub reg, '' # $ Alert // NOT GOOD
end
def as_string_indirect (reg_as_string, input)
input.match? reg_as_string, '' # NOT GOOD
input.match? reg_as_string, '' # $ Alert // NOT GOOD
end
def re_compile_indirect
name = params[:name] # source
name = params[:name] # $ Source // source
reg = Regexp.new '^\s+|\s+$'
re_compile_indirect_2 reg, name
end
def re_compile_indirect_2 (reg, input)
input.gsub reg, '' # NOT GOOD
input.gsub reg, '' # $ Alert // NOT GOOD
end
# See https://github.com/dependabot/dependabot-core/blob/37dc1767fde9b7184020763f4d0c1434f93d11d6/python/lib/dependabot/python/requirement_parser.rb#L6-L25
@@ -100,8 +100,8 @@ class FooController < ActionController::Base
MARKER_EXPR = /(#{MARKER_EXPR_ONE}|\(\s*|\s*\)|\s+and\s+|\s+or\s+)+/
def use_marker_expr
name = params[:name] # source
name = params[:name] # $ Source // source
name =~ MARKER_EXPR
name =~ MARKER_EXPR # $ Alert
end
end

View File

@@ -1,13 +1,13 @@
module Foo
def bar(x)
def bar(x) # $ Source
# Run the /a+$/ regex on the input x.
match = x.match(/a+$/)
match = x.match(/a+$/) # $ Alert
end
protected
def baz(x)
match = x.match(/a+$/)
def baz(x) # $ Source
match = x.match(/a+$/) # $ Alert
match2 = x.match(/(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)C.*Y$/)
match2 = x.match(/(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)C.*Y$/) # $ Alert
end
end
end

View File

@@ -1 +1,2 @@
queries/security/cwe-1333/RegExpInjection.ql
query: queries/security/cwe-1333/RegExpInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,26 +1,26 @@
class FooController < ActionController::Base
# BAD
def route0
name = params[:name]
regex = /#{name}/
name = params[:name] # $ Source
regex = /#{name}/ # $ Alert
end
# BAD
def route1
name = params[:name]
regex = /foo#{name}bar/
name = params[:name] # $ Source
regex = /foo#{name}bar/ # $ Alert
end
# BAD
def route2
name = params[:name]
regex = Regexp.new(name)
name = params[:name] # $ Source
regex = Regexp.new(name) # $ Alert
end
# BAD
def route3
name = params[:name]
regex = Regexp.new("@" + name)
name = params[:name] # $ Source
regex = Regexp.new("@" + name) # $ Alert
end
# GOOD - string is compared against a constant string
@@ -51,7 +51,7 @@ class FooController < ActionController::Base
# BAD
def route8
name = params[:name]
regex = Regexp.compile("@" + name)
name = params[:name] # $ Source
regex = Regexp.compile("@" + name) # $ Alert
end
end

View File

@@ -1 +1,2 @@
queries/security/cwe-134/TaintedFormatString.ql
query: queries/security/cwe-134/TaintedFormatString.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,44 +1,44 @@
class UsersController < ActionController::Base
def show
printf(params[:format], arg) # BAD
Kernel.printf(params[:format], arg) # BAD
printf(params[:format], arg) # $ Alert // BAD
Kernel.printf(params[:format], arg) # $ Alert // BAD
printf(params[:format]) # GOOD
Kernel.printf(params[:format]) # GOOD
printf(IO.new(1), params[:format], arg) # BAD
Kernel.printf(IO.new(1), params[:format], arg) # BAD
printf(IO.new(1), params[:format], arg) # $ Alert // BAD
Kernel.printf(IO.new(1), params[:format], arg) # $ Alert // BAD
printf("%s", params[:format]) # GOOD
Kernel.printf("%s", params[:format]) # GOOD
fmt = "%s"
printf(fmt, params[:format]) # GOOD
printf(IO.new(1), params[:format]) # GOOD [FALSE POSITIVE]
Kernel.printf(IO.new(1), params[:format]) # GOOD [FALSE POSITIVE]
printf(IO.new(1), params[:format]) # $ Alert // GOOD [FALSE POSITIVE]
Kernel.printf(IO.new(1), params[:format]) # $ Alert // GOOD [FALSE POSITIVE]
str1 = Kernel.sprintf(params[:format], arg) # BAD
str2 = sprintf(params[:format], arg) # BAD
str1 = Kernel.sprintf(params[:format], arg) # $ Alert // BAD
str2 = sprintf(params[:format], arg) # $ Alert // BAD
str1 = Kernel.sprintf(params[:format]) # GOOD
str2 = sprintf(params[:format]) # GOOD
stdout = IO.new 1
stdout.printf(params[:format], arg) # BAD
stdout.printf(params[:format], arg) # $ Alert // BAD
stdout.printf(params[:format]) # GOOD
# Taint via string concatenation
printf("A log message: " + params[:format], arg) # BAD
printf("A log message: " + params[:format], arg) # $ Alert // BAD
# Taint via string interpolation
printf("A log message: #{params[:format]}", arg) # BAD
printf("A log message: #{params[:format]}", arg) # $ Alert // BAD
# Using String#
"A log message #{params[:format]} %{foo}" % {foo: "foo"} # BAD
"A log message #{params[:format]} %{foo}" % {foo: "foo"} # $ Alert // BAD
# String# with an array
"A log message #{params[:format]} %08x" % ["foo"] # BAD
"A log message #{params[:format]} %08x" % ["foo"] # $ Alert // BAD
end
end
end

View File

@@ -1 +1,2 @@
queries/security/cwe-209/StackTraceExposure.ql
query: queries/security/cwe-209/StackTraceExposure.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -3,19 +3,19 @@ class FooController < ApplicationController
def show
something_that_might_fail()
rescue => e
render body: e.backtrace, content_type: "text/plain"
render body: e.backtrace, content_type: "text/plain" # $ Alert
end
def show2
bt = caller()
render body: bt, content_type: "text/plain"
bt = caller() # $ Source
render body: bt, content_type: "text/plain" # $ Alert
end
def show3
not_a_method()
rescue NoMethodError => e
render body: e.backtrace, content_type: "text/plain"
render body: e.backtrace, content_type: "text/plain" # $ Alert
end
end

View File

@@ -3,31 +3,31 @@ require "excon"
def method1
# BAD
Excon.defaults[:ssl_verify_peer] = false
Excon.get("http://example.com/")
Excon.get("http://example.com/") # $ Alert
end
def method2
# BAD
Excon.ssl_verify_peer = false
Excon.get("http://example.com/")
Excon.get("http://example.com/") # $ Alert
end
def method3(secure)
# BAD
Excon.defaults[:ssl_verify_peer] = (secure ? true : false)
Excon.get("http://example.com/")
Excon.get("http://example.com/") # $ Alert
end
def method4
# BAD
conn = Excon::Connection.new("http://example.com/", ssl_verify_peer: false)
conn.get
conn.get # $ Alert
end
def method5
# BAD
Excon.ssl_verify_peer = true
Excon.new("http://example.com/", ssl_verify_peer: false).get
Excon.new("http://example.com/", ssl_verify_peer: false).get # $ Alert
end
def method6
@@ -65,4 +65,4 @@ def method10
# GOOD
connection = Excon.new("foo")
connection.get("bar")
end
end

View File

@@ -2,11 +2,11 @@ require "faraday"
# BAD
connection = Faraday.new("http://example.com", ssl: { verify: false })
response = connection.get("/")
response = connection.get("/") # $ Alert
# BAD
connection = Faraday.new("http://example.com", ssl: { verify_mode: OpenSSL::SSL::VERIFY_NONE })
response = connection.get("/")
response = connection.get("/") # $ Alert
# GOOD
connection = Faraday.new("http://example.com")
@@ -32,7 +32,7 @@ response = connection.get("/")
def verify_as_arg(host, path, arg)
# BAD, due to the call below
connection = Faraday.new(host, ssl: { verify: arg })
response = connection.get(path)
response = connection.get(path) # $ Alert
end
verify_as_arg("http://example.com", "/", false)
@@ -41,7 +41,7 @@ verify_as_arg("http://example.com", "/", false)
def verify_mode_as_arg(host, path, arg)
# BAD, due to the call below
connection = Faraday.new(host, ssl: { verify_mode: arg })
response = connection.get(path)
response = connection.get(path) # $ Alert
end
verify_mode_as_arg("http://example.com", "/", OpenSSL::SSL::VERIFY_NONE)

View File

@@ -3,7 +3,7 @@ require "httpclient"
# BAD
client = HTTPClient.new
client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
client.get("https://example.com")
client.get("https://example.com") # $ Alert
# GOOD
client = HTTPClient.new
@@ -15,4 +15,4 @@ client = HTTPClient.new
client.get("https://example.com")
# GOOD
HTTPClient.get("https://example.com/")
HTTPClient.get("https://example.com/")

View File

@@ -1,19 +1,19 @@
require "httparty"
# BAD
HTTParty.get("http://example.com/", verify: false)
HTTParty.get("http://example.com/", verify: false) # $ Alert
# BAD
HTTParty.get("http://example.com/", verify_peer: false)
HTTParty.get("http://example.com/", verify_peer: false) # $ Alert
# BAD
HTTParty.get("http://example.com/", { verify_peer: false })
HTTParty.get("http://example.com/", { verify_peer: false }) # $ Alert
# BAD
HTTParty.post("http://example.com/", body: "some_data", verify: false)
HTTParty.post("http://example.com/", body: "some_data", verify: false) # $ Alert
# BAD
HTTParty.post("http://example.com/", { body: "some_data", verify: false })
HTTParty.post("http://example.com/", { body: "some_data", verify: false }) # $ Alert
# GOOD
HTTParty.get("http://example.com/")
@@ -34,4 +34,4 @@ HTTParty.post("http://example.com/", body: "some_data", verify: true)
HTTParty.post("http://example.com/", { body: "some_data" })
# GOOD
HTTParty.post("http://example.com/", { body: "some_data", verify: true })
HTTParty.post("http://example.com/", { body: "some_data", verify: true })

View File

@@ -6,5 +6,5 @@ http = Net::HTTP.new uri.host, uri.port
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new uri.request_uri
response = http.request request
response = http.request request # $ Alert
puts response.body

View File

@@ -1,24 +1,24 @@
require "open-uri"
# BAD
Kernel.open("https://example.com", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
Kernel.open("https://example.com", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE) # $ Alert
# BAD
Kernel.open("https://example.com", { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE })
Kernel.open("https://example.com", { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }) # $ Alert
# BAD
options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }
Kernel.open("https://example.com", options)
Kernel.open("https://example.com", options) # $ Alert
# BAD
URI.parse("https://example.com").open(ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
URI.parse("https://example.com").open(ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE) # $ Alert
# BAD
URI.parse("https://example.com").open({ ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE })
URI.parse("https://example.com").open({ ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }) # $ Alert
# BAD
options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }
URI.parse("https://example.com").open(options)
URI.parse("https://example.com").open(options) # $ Alert
# GOOD
Kernel.open("https://example.com")
@@ -44,4 +44,4 @@ URI.parse("https://example.com").open({ ssl_verify_mode: OpenSSL::SSL::VERIFY_PE
# GOOD
options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER }
URI.parse("https://example.com").open(options)
URI.parse("https://example.com").open(options)

View File

@@ -1 +1,2 @@
queries/security/cwe-295/RequestWithoutValidation.ql
query: queries/security/cwe-295/RequestWithoutValidation.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,21 +2,21 @@ require "rest-client"
# BAD
resource = RestClient::Resource.new("https://example.com", verify_ssl: OpenSSL::SSL::VERIFY_NONE)
response = resource.get
response = resource.get # $ Alert
# BAD
resource = RestClient::Resource.new("https://example.com", { verify_ssl: OpenSSL::SSL::VERIFY_NONE })
response = resource.get
response = resource.get # $ Alert
# BAD
options = { verify_ssl: OpenSSL::SSL::VERIFY_NONE }
resource = RestClient::Resource.new("https://example.com", options)
response = resource.get
response = resource.get # $ Alert
# BAD
value = OpenSSL::SSL::VERIFY_NONE
resource = RestClient::Resource.new("https://example.com", verify_ssl: value)
response = resource.get
response = resource.get # $ Alert
# GOOD
RestClient.get("https://example.com")

View File

@@ -1,11 +1,11 @@
require "typhoeus"
# BAD
Typhoeus.get("https://www.example.com", ssl_verifypeer: false)
Typhoeus.get("https://www.example.com", ssl_verifypeer: false) # $ Alert
# BAD
post_options = { body: "some data", ssl_verifypeer: false }
Typhoeus.post("https://www.example.com", post_options)
Typhoeus.post("https://www.example.com", post_options) # $ Alert
# GOOD
Typhoeus.get("https://www.example.com")
Typhoeus.get("https://www.example.com")

View File

@@ -1 +1,2 @@
queries/security/cwe-312/CleartextLogging.ql
query: queries/security/cwe-312/CleartextLogging.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1 +1,2 @@
queries/security/cwe-312/CleartextStorage.ql
query: queries/security/cwe-312/CleartextStorage.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,47 +1,47 @@
class UsersController < ApplicationController
def createLikeCall
new_password = "043697b96909e03ca907599d6420555f"
new_password = "043697b96909e03ca907599d6420555f" # $ Source[rb/clear-text-storage-sensitive-data]
# BAD: plaintext password stored to database
User.create(name: "U1", password: new_password)
User.create(name: "U1", password: new_password) # $ Alert[rb/clear-text-storage-sensitive-data]
# BAD: plaintext password stored to database
User.create({ name: "U1", password: new_password })
User.create({ name: "U1", password: new_password }) # $ Alert[rb/clear-text-storage-sensitive-data]
end
def updateLikeClassMethodCall
new_password = "083c9e1da4cc0c2f5480bb4dbe6ff141"
new_password = "083c9e1da4cc0c2f5480bb4dbe6ff141" # $ Source[rb/clear-text-storage-sensitive-data]
# BAD: plaintext password stored to database
User.update(1, name: "U1", password: new_password)
User.update(1, name: "U1", password: new_password) # $ Alert[rb/clear-text-storage-sensitive-data]
# BAD: plaintext password stored to database
User.update([1, 2], [{name: "U1", password: new_password}, {name: "U2", password: new_password}])
User.update([1, 2], [{name: "U1", password: new_password}, {name: "U2", password: new_password}]) # $ Alert[rb/clear-text-storage-sensitive-data]
end
def insertAllLikeCall
new_password = "504d224a806cf8073cd14ef08242d422"
new_password = "504d224a806cf8073cd14ef08242d422" # $ Source[rb/clear-text-storage-sensitive-data]
# BAD: plaintext password stored to database
User.insert_all([{name: "U1", password: new_password}, {name: "U2", password: new_password}])
User.insert_all([{name: "U1", password: new_password}, {name: "U2", password: new_password}]) # $ Alert[rb/clear-text-storage-sensitive-data]
end
def updateLikeInstanceMethodCall
user = User.find(1)
new_password = "7d6ae08394c3f284506dca70f05995f6"
new_password = "7d6ae08394c3f284506dca70f05995f6" # $ Source[rb/clear-text-storage-sensitive-data]
# BAD: plaintext password stored to database
user.update(password: new_password)
user.update(password: new_password) # $ Alert[rb/clear-text-storage-sensitive-data]
# BAD: plaintext password stored to database
user.update({password: new_password})
user.update({password: new_password}) # $ Alert[rb/clear-text-storage-sensitive-data]
end
def updateAttributeCall
user = User.find(1)
new_password = "ff295f8648a406c37fbe378377320e4c"
new_password = "ff295f8648a406c37fbe378377320e4c" # $ Source[rb/clear-text-storage-sensitive-data]
# BAD: plaintext password stored to database
user.update_attribute("password", new_password)
user.update_attribute("password", new_password) # $ Alert[rb/clear-text-storage-sensitive-data]
end
def assignAttributeCall
user = User.find(1)
new_password = "78ffbec583b546bd073efd898f833184"
new_password = "78ffbec583b546bd073efd898f833184" # $ Source[rb/clear-text-storage-sensitive-data]
# BAD: plaintext password assigned to database field
user.password = new_password
user.password = new_password # $ Alert[rb/clear-text-storage-sensitive-data]
user.save
end
@@ -55,13 +55,13 @@ class UsersController < ApplicationController
end
def fileWrites
new_password = "0157af7c38cbdd24f1616de4e5321861"
new_password = "0157af7c38cbdd24f1616de4e5321861" # $ Source[rb/clear-text-storage-sensitive-data]
# BAD: plaintext password stored to disk
IO.write("foo.txt", "password: #{new_password}\n")
IO.write("foo.txt", "password: #{new_password}\n") # $ Alert[rb/clear-text-storage-sensitive-data]
# BAD: plaintext password stored to disk
File.new("bar.txt", "a").puts("password: #{new_password}")
File.new("bar.txt", "a").puts("password: #{new_password}") # $ Alert[rb/clear-text-storage-sensitive-data]
end
def randomPasswordAssign
@@ -76,15 +76,15 @@ class UsersController < ApplicationController
info = [
{
name: "U1",
password: "aaaaaaaaaa",
credit_card_number: "0000-0000-0000-0000",
SSN: "000-00-00000"
password: "aaaaaaaaaa", # $ Source[rb/clear-text-storage-sensitive-data]
credit_card_number: "0000-0000-0000-0000", # $ Source[rb/clear-text-storage-sensitive-data]
SSN: "000-00-00000" # $ Source[rb/clear-text-storage-sensitive-data]
},
{name: "U2", password: "bbbbbbb"}
{name: "U2", password: "bbbbbbb"} # $ Source[rb/clear-text-storage-sensitive-data]
]
info.each do |inf|
# BAD: Plaintext password, SSN, and CCN stored to database.
User.create!(inf)
User.create!(inf) # $ Alert[rb/clear-text-storage-sensitive-data]
end
end
end

View File

@@ -1,20 +1,20 @@
class User < ActiveRecord::Base
def set_password_1
new_password = "06c38c6a8a9c11a9d3b209a3193047b4"
new_password = "06c38c6a8a9c11a9d3b209a3193047b4" # $ Source[rb/clear-text-storage-sensitive-data]
# BAD: directly storing a potential cleartext password to a field
self.update(password: new_password)
self.update(password: new_password) # $ Alert[rb/clear-text-storage-sensitive-data]
end
def set_password_2
new_password = "52652fb5c709fb6b9b5a0194af7c6067"
new_password = "52652fb5c709fb6b9b5a0194af7c6067" # $ Source[rb/clear-text-storage-sensitive-data]
# BAD: directly storing a potential cleartext password to a field
update(password: new_password)
update(password: new_password) # $ Alert[rb/clear-text-storage-sensitive-data]
end
def set_password_3
new_password = "f982bf2531c149a8a1444a951b12e830"
new_password = "f982bf2531c149a8a1444a951b12e830" # $ Source[rb/clear-text-storage-sensitive-data]
# BAD: directly assigning a potential cleartext password to a field
self.password = new_password
self.password = new_password # $ Alert[rb/clear-text-storage-sensitive-data]
self.save
end
end

View File

@@ -1,45 +1,45 @@
stdout_logger = Logger.new STDOUT
password = "043697b96909e03ca907599d6420555f"
password = "043697b96909e03ca907599d6420555f" # $ Source[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger.info password
stdout_logger.info password # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger.debug password
stdout_logger.debug password # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger.error password
stdout_logger.error password # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger.fatal password
stdout_logger.fatal password # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger.unknown password
stdout_logger.unknown password # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger.warn password
stdout_logger.warn password # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger.add Logger::WARN, password
stdout_logger.add Logger::WARN, password # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger.add Logger::WARN, "message", password
stdout_logger.add Logger::WARN, "message", password # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger.log Logger::WARN, password
stdout_logger.log Logger::WARN, password # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger << "pw: #{password}"
stdout_logger << "pw: #{password}" # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: sensitive data in the progname will taint subsequent logging calls
stdout_logger.progname = password
stdout_logger.progname = password # $ Alert[rb/clear-text-logging-sensitive-data]
hsh1 = { password: "aec5058e61f7f122998b1a30ee2c66b6" }
hsh1 = { password: "aec5058e61f7f122998b1a30ee2c66b6" } # $ Source[rb/clear-text-logging-sensitive-data]
hsh2 = {}
# GOOD: no backwards flow
stdout_logger.info hsh2[:password]
hsh2[:password] = "beeda625d7306b45784d91ea0336e201"
hsh2[:password] = "beeda625d7306b45784d91ea0336e201" # $ Source[rb/clear-text-logging-sensitive-data]
hsh3 = hsh2
# BAD: password logged as plaintext
stdout_logger.info hsh1[:password]
stdout_logger.info hsh1[:password] # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger.info hsh2[:password]
stdout_logger.info hsh2[:password] # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password logged as plaintext
stdout_logger.info hsh3[:password]
stdout_logger.info hsh3[:password] # $ Alert[rb/clear-text-logging-sensitive-data]
# GOOD: not a password
stdout_logger.info hsh1[:foo]
@@ -61,30 +61,30 @@ stdout_logger.info password_masked_sub_ex
# GOOD: password is effectively masked before logging
stdout_logger.info password_masked_gsub_ex
password_masked_ineffective_sub = "ca497451f5e883662fb1a37bc9ec7838"
password_masked_ineffective_sub_ex = "ca497451f5e883662fb1a37bc9ec7838"
password_masked_ineffective_gsub = "a7e3747b19930d4f4b8181047194832f"
password_masked_ineffective_gsub_ex = "a7e3747b19930d4f4b8181047194832f"
password_masked_ineffective_sub = password_masked_ineffective_sub.sub(/./, "[password]")
password_masked_ineffective_sub = "ca497451f5e883662fb1a37bc9ec7838" # $ Source[rb/clear-text-logging-sensitive-data]
password_masked_ineffective_sub_ex = "ca497451f5e883662fb1a37bc9ec7838" # $ Source[rb/clear-text-logging-sensitive-data]
password_masked_ineffective_gsub = "a7e3747b19930d4f4b8181047194832f" # $ Source[rb/clear-text-logging-sensitive-data]
password_masked_ineffective_gsub_ex = "a7e3747b19930d4f4b8181047194832f" # $ Source[rb/clear-text-logging-sensitive-data]
password_masked_ineffective_sub = password_masked_ineffective_sub.sub(/./, "[password]") # $ Source[rb/clear-text-logging-sensitive-data]
password_masked_ineffective_sub_ex.sub!(/./, "[password]")
password_masked_ineffective_gsub = password_masked_ineffective_gsub.gsub(/[A-Z]/, "*")
password_masked_ineffective_gsub = password_masked_ineffective_gsub.gsub(/[A-Z]/, "*") # $ Source[rb/clear-text-logging-sensitive-data]
password_masked_ineffective_gsub_ex.gsub!(/[A-Z]/, "*")
# BAD: password masked ineffectively
stdout_logger.info password_masked_ineffective_sub
stdout_logger.info password_masked_ineffective_sub # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password masked ineffectively
stdout_logger.info password_masked_ineffective_gsub
stdout_logger.info password_masked_ineffective_gsub # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password masked ineffectively
stdout_logger.info password_masked_ineffective_sub_ex
stdout_logger.info password_masked_ineffective_sub_ex # $ Alert[rb/clear-text-logging-sensitive-data]
# BAD: password masked ineffectively
stdout_logger.info password_masked_ineffective_gsub_ex
stdout_logger.info password_masked_ineffective_gsub_ex # $ Alert[rb/clear-text-logging-sensitive-data]
def foo(password, logger)
# BAD: password logged as plaintext
logger.info password
logger.info password # $ Alert[rb/clear-text-logging-sensitive-data]
end
password_arg = "65f2950df2f0e2c38d7ba2ccca767291"
password_arg = "65f2950df2f0e2c38d7ba2ccca767291" # $ Source[rb/clear-text-logging-sensitive-data]
foo(password_arg, stdout_logger)
foo("65f2950df2f0e2c38d7ba2ccca767292", stdout_logger)

View File

@@ -1 +1,2 @@
queries/security/cwe-327/BrokenCryptoAlgorithm.ql
query: queries/security/cwe-327/BrokenCryptoAlgorithm.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1 +1,2 @@
queries/security/cwe-327/WeakSensitiveDataHashing.ql
query: queries/security/cwe-327/WeakSensitiveDataHashing.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

Some files were not shown because too many files have changed in this diff Show More