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
149 changed files with 1070 additions and 1054 deletions

View File

@@ -1,4 +0,0 @@
---
category: fix
---
* The query `actions/pr-on-self-hosted-runner` was updated to the latest standard runner labels reducing false positive results.

View File

@@ -2,12 +2,10 @@ import actions
bindingset[runner] bindingset[runner]
predicate isGithubHostedRunner(string runner) { predicate isGithubHostedRunner(string runner) {
// The list of github hosted repos: // list of github hosted repos: https://github.com/actions/runner-images/blob/main/README.md#available-images
// https://github.com/actions/runner-images/blob/main/README.md#available-images runner
// https://docs.github.com/en/enterprise-cloud@latest/actions/how-tos/write-workflows/choose-where-workflows-run/choose-the-runner-for-a-job#standard-github-hosted-runners-for-public-repositories .toLowerCase()
runner.toLowerCase().regexpMatch("^ubuntu-([0-9.]+|latest|slim)(-arm)?$") or .regexpMatch("^(ubuntu-([0-9.]+|latest)|macos-([0-9]+|latest)(-x?large)?|windows-([0-9.]+|latest))$")
runner.toLowerCase().regexpMatch("^macos-([0-9]+|latest)(-x?large|-intel)?$") or
runner.toLowerCase().regexpMatch("^windows-([0-9.]+|latest)(-vs[0-9.]+)?(-arm)?$")
} }
bindingset[runner] bindingset[runner]

View File

@@ -1,43 +0,0 @@
name: test
on:
pull_request:
jobs:
test:
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- ubuntu-24.04
- ubuntu-24.04-arm
- ubuntu-22.04
- ubuntu-22.04-arm
- ubuntu-26.04
- ubuntu-26.04-arm
- ubuntu-slim
- macos-26
- macos-26-xlarge
- macos-26-intel
- macos-26-large
- macos-latest-large
- macos-15-large
- macos-15
- macos-15-intel
- macos-latest
- macos-15
- macos-15-xlarge
- macos-14-large
- macos-14
- macos-14-xlarge
- windows-2025-vs2026
- windows-latest
- windows-2025
- windows-2022
- windows-11
- windows-11-arm
- windows-11-vs2026-arm
runs-on: ${{ matrix.os }}
steps:
- run: cmd

View File

@@ -203,7 +203,7 @@ module Ast implements AstSig<Location> {
final private class FinalTryStmt = CS::TryStmt; final private class FinalTryStmt = CS::TryStmt;
class TryStmt extends FinalTryStmt { class TryStmt extends FinalTryStmt {
AstNode getBody(int index) { index = 0 and result = this.getBlock() } Stmt getBody() { result = this.getBlock() }
CatchClause getCatch(int index) { result = this.getCatchClause(index) } CatchClause getCatch(int index) { result = this.getCatchClause(index) }

View File

@@ -117,18 +117,15 @@ private module Ast implements AstSig<Location> {
final private class FinalTryStmt = J::TryStmt; final private class FinalTryStmt = J::TryStmt;
class TryStmt extends FinalTryStmt { class TryStmt extends FinalTryStmt {
AstNode getBody(int index) { Stmt getBody() { result = super.getBlock() }
result = super.getResource(index)
or
index = count(super.getAResource()) and
result = super.getBlock()
}
CatchClause getCatch(int index) { result = super.getCatchClause(index) } CatchClause getCatch(int index) { result = super.getCatchClause(index) }
Stmt getFinally() { result = super.getFinally() } Stmt getFinally() { result = super.getFinally() }
} }
AstNode getTryInit(TryStmt try, int index) { result = try.getResource(index) }
final private class FinalCatchClause = J::CatchClause; final private class FinalCatchClause = J::CatchClause;
class CatchClause extends FinalCatchClause { class CatchClause extends FinalCatchClause {

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' require 'zlib'
class TestController < ActionController::Base 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| Zlib::GzipReader.open(gzip_path) do |uncompressedfile|
puts uncompressedfile.read puts uncompressedfile.read
end end # $ Alert
Zlib::GzipReader.open(gzip_path) do |uncompressedfile| Zlib::GzipReader.open(gzip_path) do |uncompressedfile|
uncompressedfile.each do |entry| uncompressedfile.each do |entry|
puts entry puts entry
end end
end end # $ Alert
uncompressedfile = Zlib::GzipReader.open(gzip_path) uncompressedfile = Zlib::GzipReader.open(gzip_path) # $ Alert
uncompressedfile.each do |entry| uncompressedfile.each do |entry|
puts entry puts entry
end end
Zlib::GzipReader.new(File.open(gzip_path, 'rb')).read Zlib::GzipReader.new(File.open(gzip_path, 'rb')).read # $ Alert
Zlib::GzipReader.new(File.open(gzip_path, 'rb')).each do |entry| Zlib::GzipReader.new(File.open(gzip_path, 'rb')).each do |entry| # $ Alert
puts entry puts entry
end end
Zlib::GzipReader.zcat(open(gzip_path)) Zlib::GzipReader.zcat(open(gzip_path)) # $ Alert
end end

View File

@@ -1,21 +1,21 @@
require 'zip' require 'zip'
class TestController < ActionController::Base class TestController < ActionController::Base
zipfile_path = params[:path] zipfile_path = params[:path] # $ Source
Zip::InputStream.open(zipfile_path) do |input| Zip::InputStream.open(zipfile_path) do |input|
while (entry = input.get_next_entry) while (entry = input.get_next_entry)
puts :file_name, entry.name puts :file_name, entry.name
input input
end end
end end # $ Alert
Zip::InputStream.open(zipfile_path) do |input| Zip::InputStream.open(zipfile_path) do |input|
input.read input.read
end end # $ Alert
input = Zip::InputStream.open(zipfile_path) input = Zip::InputStream.open(zipfile_path) # $ Alert
Zip::File.open(zipfile_path).read "10GB" Zip::File.open(zipfile_path).read "10GB" # $ Alert
Zip::File.open(zipfile_path).extract "10GB", "./" Zip::File.open(zipfile_path).extract "10GB", "./" # $ Alert
Zip::File.open(zipfile_path) do |zip_file| Zip::File.open(zipfile_path) do |zip_file|
# Handle entries one by one # Handle entries one by one
@@ -25,33 +25,33 @@ class TestController < ActionController::Base
# Extract to file or directory based on name in the archive # Extract to file or directory based on name in the archive
entry.extract entry.extract
# Read into memory # Read into memory
entry.get_input_stream.read entry.get_input_stream.read # $ Alert
end end
end end
zip_file = Zip::File.open(zipfile_path) zip_file = Zip::File.open(zipfile_path)
zip_file.each do |entry| zip_file.each do |entry|
entry.extract entry.extract # $ Alert
entry.get_input_stream.read entry.get_input_stream.read # $ Alert
end end
# Find specific entry # Find specific entry
Zip::File.open(zipfile_path) do |zip_file| Zip::File.open(zipfile_path) do |zip_file|
zip_file.glob('*.xml').each do |entry| zip_file.glob('*.xml').each do |entry|
zip_file.read(entry.name) zip_file.read(entry.name) # $ Alert
entry.extract entry.extract # $ Alert
end end
entry = zip_file.glob('*.csv').first entry = zip_file.glob('*.csv').first
raise 'File too large when extracted' if entry.size > MAX_SIZE 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 end
zip_file = Zip::File.open(zipfile_path) zip_file = Zip::File.open(zipfile_path)
entry = zip_file.glob('*.csv') 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 = Zip::File.open(zipfile_path)
zip_file.glob('*') do |entry| zip_file.glob('*') do |entry|
entry.get_input_stream.read entry.get_input_stream.read # $ Alert
end end
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 def some_request_handler
# A string tainted by user input is used directly as password # A string tainted by user input is used directly as password
# (i.e a remote flow source) # (i.e a remote flow source)
pass = params[:pass] pass = params[:pass] # $ Source
# BAD: user input is not sanitized # BAD: user input is not sanitized
ldap = Net::LDAP.new( ldap = Net::LDAP.new(
@@ -12,7 +12,7 @@ class FooController < ActionController::Base
auth: { auth: {
method: :simple, method: :simple,
username: 'uid=admin,dc=example,dc=com', username: 'uid=admin,dc=example,dc=com',
password: pass password: pass # $ Alert
} }
) )
ldap.bind ldap.bind
@@ -21,14 +21,14 @@ class FooController < ActionController::Base
def some_request_handler def some_request_handler
# A string tainted by user input is used directly as password # A string tainted by user input is used directly as password
# (i.e a remote flow source) # (i.e a remote flow source)
pass = params[:pass] pass = params[:pass] # $ Source
# BAD: user input is not sanitized # BAD: user input is not sanitized
ldap = Net::LDAP.new ldap = Net::LDAP.new
ldap.host = your_server_ip_address ldap.host = your_server_ip_address
ldap.encryption(:method => :simple_tls) ldap.encryption(:method => :simple_tls)
ldap.port = 639 ldap.port = 639
ldap.auth "admin", pass ldap.auth "admin", pass # $ Alert
ldap.bind ldap.bind
end 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) def generate_password_1(length)
chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a + ['!', '@', '#', '$', '%'] chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a + ['!', '@', '#', '$', '%']
# BAD: rand is not cryptographically secure # 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 end
def generate_password_2(length) def generate_password_2(length)

View File

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

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

View File

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

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

View File

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

View File

@@ -2,7 +2,7 @@ require 'rexml'
class FooController < ActionController::Base class FooController < ActionController::Base
def rexml_handler(event:, context:) def rexml_handler(event:, context:)
name = params[:user_name] name = params[:user_name] # $ Source
xml = <<-XML xml = <<-XML
<root> <root>
@@ -18,13 +18,13 @@ class FooController < ActionController::Base
results1 = REXML::XPath.first(doc, "//foo") results1 = REXML::XPath.first(doc, "//foo")
# BAD: XPath query is constructed from user input # 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 # GOOD: XPath query is not constructed from user input
results3 = REXML::XPath.match(doc, "//foo", nil) results3 = REXML::XPath.match(doc, "//foo", nil)
# BAD: XPath query is constructed from user input # 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 # GOOD: XPath query is not constructed from user input
REXML::XPath.each(doc, "//foo") do |element| REXML::XPath.each(doc, "//foo") do |element|
@@ -32,7 +32,7 @@ class FooController < ActionController::Base
end end
# BAD: XPath query constructed from user input # 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 puts element.text
end 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 def tarReaderUnsafe
path = params[:path] path = params[:path]
file_stream = IO.new(IO.sysopen(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| tarfile.each do |entry|
::File.open(entry.full_name, "wb") do |os| ::File.open(entry.full_name, "wb") do |os| # $ Alert
entry.read entry.read
end end
end end
@@ -17,9 +17,9 @@ class TestController < ActionController::Base
def tarReaderBlockUnsafe def tarReaderBlockUnsafe
path = params[:path] path = params[:path]
file_stream = IO.new(IO.sysopen(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| tarfile.each_entry do |entry|
::File.open(entry.full_name, "wb") do |os| ::File.open(entry.full_name, "wb") do |os| # $ Alert
entry.read entry.read
end end
end end
@@ -43,8 +43,8 @@ class TestController < ActionController::Base
# BAD # BAD
def zipFileUnsafe def zipFileUnsafe
path = params[:path] path = params[:path]
Zip::File.open(path).each do |entry| Zip::File.open(path).each do |entry| # $ Source
File.open(entry.name, "wb") do |os| File.open(entry.name, "wb") do |os| # $ Alert
entry.read entry.read
end end
end end
@@ -53,9 +53,9 @@ class TestController < ActionController::Base
# BAD # BAD
def zipFileBlockUnsafe def zipFileBlockUnsafe
path = params[:path] path = params[:path]
Zip::File.open(path) do |zip_file| Zip::File.open(path) do |zip_file| # $ Source
zip_file.each do |entry| zip_file.each do |entry|
File.open(entry.name, "wb") do |os| File.open(entry.name, "wb") do |os| # $ Alert
entry.read entry.read
end end
end end
@@ -87,7 +87,7 @@ class TestController < ActionController::Base
end end
def get_compressed_file_stream(compressed_file_path) 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) yield(gzip)
end end
@@ -97,7 +97,7 @@ class TestController < ActionController::Base
get_compressed_file_stream(path) do |compressed_file| get_compressed_file_stream(path) do |compressed_file|
compressed_file.each do |entry| compressed_file.each do |entry|
entry_path = entry.full_name entry_path = entry.full_name
::File.open(entry_path, 'wb') do |os| ::File.open(entry_path, 'wb') do |os| # $ Alert
entry.read entry.read
end end
end end
@@ -120,10 +120,10 @@ class TestController < ActionController::Base
def gzipReaderUnsafeNewInstance def gzipReaderUnsafeNewInstance
path = params[:path] path = params[:path]
File.open(path, 'rb') do |f| File.open(path, 'rb') do |f|
gz = Zlib::GzipReader.new(f) gz = Zlib::GzipReader.new(f) # $ Source
gz.each do |entry| gz.each do |entry|
entry_path = entry.full_name entry_path = entry.full_name
::File.open(entry_path, 'wb') do |os| ::File.open(entry_path, 'wb') do |os| # $ Alert
entry.read entry.read
end end
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 class UnicodeNormalizationOKController < ActionController::Base
def unicodeNormalize def unicodeNormalize
unicode_input = params[:unicode_input] unicode_input = params[:unicode_input] # $ Source
normalized_nfkc = unicode_input.unicode_normalize(:nfkc) # $ MISSING:result=OK normalized_nfkc = unicode_input.unicode_normalize(:nfkc) # $ Alert // $ MISSING:result=OK
normalized_nfc = unicode_input.unicode_normalize(:nfc) # $ MISSING:result=OK normalized_nfc = unicode_input.unicode_normalize(:nfc) # $ Alert // $ MISSING:result=OK
end end
end end
class UnicodeNormalizationStrManipulationController < ActionController::Base class UnicodeNormalizationStrManipulationController < ActionController::Base
def unicodeNormalize def unicodeNormalize
unicode_input = params[:unicode_input] unicode_input = params[:unicode_input] # $ Source
unicode_input_manip = unicode_input.sub(/[aeiou]/, "*") unicode_input_manip = unicode_input.sub(/[aeiou]/, "*") # $ Source
normalized_nfkc = unicode_input_manip.unicode_normalize(:nfkc) # $ result=BAD normalized_nfkc = unicode_input_manip.unicode_normalize(:nfkc) # $ Alert // $ result=BAD
normalized_nfc = unicode_input_manip.unicode_normalize(:nfc) # $ result=BAD normalized_nfc = unicode_input_manip.unicode_normalize(:nfc) # $ Alert // $ result=BAD
end end
end end
class UnicodeNormalizationHtMLEscapeController < ActionController::Base class UnicodeNormalizationHtMLEscapeController < ActionController::Base
def unicodeNormalize def unicodeNormalize
unicode_input = params[:unicode_input] unicode_input = params[:unicode_input] # $ Source
unicode_html_safe = html_escape(unicode_input) unicode_html_safe = html_escape(unicode_input) # $ Source
normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkc) # $ result=BAD normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkc) # $ Alert // $ result=BAD
normalized_nfc = unicode_html_safe.unicode_normalize(:nfc) # $ result=BAD normalized_nfc = unicode_html_safe.unicode_normalize(:nfc) # $ Alert // $ result=BAD
end end
end end
class UnicodeNormalizationCGIHtMLEscapeController < ActionController::Base class UnicodeNormalizationCGIHtMLEscapeController < ActionController::Base
def unicodeNormalize def unicodeNormalize
unicode_input = params[:unicode_input] unicode_input = params[:unicode_input] # $ Source
unicode_html_safe = CGI.escapeHTML(unicode_input).html_safe unicode_html_safe = CGI.escapeHTML(unicode_input).html_safe # $ Source
normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkd) # $ result=BAD normalized_nfkc = unicode_html_safe.unicode_normalize(:nfkd) # $ Alert // $ result=BAD
normalized_nfc = unicode_html_safe.unicode_normalize(:nfd) # $ result=BAD normalized_nfc = unicode_html_safe.unicode_normalize(:nfd) # $ Alert // $ result=BAD
end end
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') token1 = JWT.encode({ foo: 'bar' }, "secret", 'none')
# BAD: the secret used is empty # 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 # 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 # 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,19 +3,19 @@ require 'jwt'
payload = { foo: 'bar' } payload = { foo: 'bar' }
# Unsecure token # 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 # Secure token
token = JWT.encode(payload, "secret", 'HS256') token = JWT.encode(payload, "secret", 'HS256')
# BAD: it does not verify # 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 # 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 # 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 # GOOD: it does verify
decoded_token5 = JWT.decode(token, secret, 'HS256') decoded_token5 = JWT.decode(token, secret, 'HS256')

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 class UsersController < ActionController::Base
# BAD before psych version 4.0.0 and # BAD before psych version 4.0.0 and
def route1 def route1
yaml_data = params[:key] yaml_data = params[:key] # $ Source
object = Psych.load yaml_data object = Psych.load yaml_data # $ Alert
object = Psych.load_file yaml_data object = Psych.load_file yaml_data
end end
# GOOD In psych version 4.0.0 and above # GOOD In psych version 4.0.0 and above
def route2 def route2
yaml_data = params[:key] yaml_data = params[:key] # $ Source
object = Psych.load yaml_data object = Psych.load yaml_data # $ Alert
object = Psych.load_file yaml_data object = Psych.load_file yaml_data
end end
@@ -29,14 +29,14 @@ class UsersController < ActionController::Base
# BAD # BAD
def route4 def route4
yaml_data = params[:key] yaml_data = params[:key] # $ Source
object = Psych.unsafe_load(yaml_data) object = Psych.unsafe_load(yaml_data) # $ Alert
object = Psych.unsafe_load_file(yaml_data) object = Psych.unsafe_load_file(yaml_data) # $ Alert
object = Psych.load_stream(yaml_data) object = Psych.load_stream(yaml_data) # $ Alert
parse_output = Psych.parse_stream(yaml_data) parse_output = Psych.parse_stream(yaml_data)
object = parse_output.to_ruby object = parse_output.to_ruby # $ Alert
object = Psych.parse(yaml_data).to_ruby object = Psych.parse(yaml_data).to_ruby # $ Alert
object = Psych.parse_file(yaml_data).to_ruby object = Psych.parse_file(yaml_data).to_ruby # $ Alert
parsed_yaml = Psych.parse_stream(yaml_data) parsed_yaml = Psych.parse_stream(yaml_data)
parsed_yaml.children.each do |child| parsed_yaml.children.each do |child|
object = child.to_ruby object = child.to_ruby
@@ -46,7 +46,7 @@ class UsersController < ActionController::Base
end end
object = parsed_yaml.children.first.to_ruby object = parsed_yaml.children.first.to_ruby
content = parsed_yaml.children[0].children[0].children 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 = content.to_ruby[0]
object = Psych.parse(yaml_data).children[0].to_ruby object = Psych.parse(yaml_data).children[0].to_ruby
end end
@@ -58,18 +58,18 @@ class UsersController < ActionController::Base
end end
def stdin def stdin
object = YAML.load $stdin.read object = YAML.load $stdin.read # $ Alert
# STDIN # STDIN
object = YAML.load STDIN.gets object = YAML.load STDIN.gets # $ Alert
# ARGF # ARGF
object = YAML.load ARGF.read object = YAML.load ARGF.read # $ Alert
# Kernel.gets # Kernel.gets
object = YAML.load gets object = YAML.load gets # $ Alert
# Kernel.readlines # Kernel.readlines
object = YAML.load readlines object = YAML.load readlines # $ Alert
end end
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 class ExampleController < ActionController::Base
# Should find # Should find
def example_action def example_action
if request.get? if request.get? # $ Alert
Resource.find(id: params[:example_id]) Resource.find(id: params[:example_id])
end end
end end
# Should find # Should find
def other_action def other_action
method = request.env['REQUEST_METHOD'] method = request.env['REQUEST_METHOD'] # $ Source
if method == "GET" if method == "GET" # $ Alert
Resource.find(id: params[:id]) Resource.find(id: params[:id])
end end
end end
# Should find # Should find
def foo def foo
method = request.request_method method = request.request_method # $ Source
if method == "GET" if method == "GET" # $ Alert
Resource.find(id: params[:id]) Resource.find(id: params[:id])
end end
end end
# Should find # Should find
def bar def bar
method = request.method method = request.method # $ Source
if method == "GET" if method == "GET" # $ Alert
Resource.find(id: params[:id]) Resource.find(id: params[:id])
end end
end end
# Should find # Should find
def baz def baz
method = request.raw_request_method method = request.raw_request_method # $ Source
if method == "GET" if method == "GET" # $ Alert
Resource.find(id: params[:id]) Resource.find(id: params[:id])
end end
end end
@@ -48,15 +48,15 @@ class ExampleController < ActionController::Base
# Should find # Should find
def foobarbaz def foobarbaz
method = request.request_method_symbol method = request.request_method_symbol # $ Source
if method == :GET if method == :GET # $ Alert
Resource.find(id: params[:id]) Resource.find(id: params[:id])
end end
end end
# Should find # Should find
def resource_action def resource_action
case request.env['REQUEST_METHOD'] case request.env['REQUEST_METHOD'] # $ Alert
when "GET" when "GET"
Resource.find(id: params[:id]) Resource.find(id: params[:id])
when "POST" when "POST"

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 # Should catch
def create def create
TestObject.create(foo: request.request_parameters[:foo]) TestObject.create(foo: request.request_parameters[:foo]) # $ Alert
end end
# Should catch # Should catch
def create_query def create_query
TestObject.create(foo: request.query_parameters[:foo]) TestObject.create(foo: request.query_parameters[:foo]) # $ Alert
end end
# Should catch # Should catch
def update_unsafe def update_unsafe
TestObject.update(foo: request.POST[:foo]) TestObject.update(foo: request.POST[:foo]) # $ Alert
end end
# Should catch # Should catch
def update_unsafe_get def update_unsafe_get
TestObject.update(foo: request.GET[:foo]) TestObject.update(foo: request.GET[:foo]) # $ Alert
end end
# Should not catch # Should not catch

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 class DetectTest
def test def test
# These are bad # These are bad
[].select { |i| true }.first [].select { |i| true }.first # $ Alert
[].select { |i| true }.last [].select { |i| true }.last # $ Alert
[].select { |i| true }[0] [].select { |i| true }[0] # $ Alert
[].select { |i| true }[-1] [].select { |i| true }[-1] # $ Alert
[].filter { |i| true }.first [].filter { |i| true }.first # $ Alert
[].find_all { |i| true }.last [].find_all { |i| true }.last # $ Alert
selection1 = [].select { |i| true } selection1 = [].select { |i| true }
selection1.first selection1.first # $ Alert
# These are good # These are good
[].select("").first # Selecting a string [].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_REGEX1 = /(www|beta).example.com\// # $ Alert
UNSAFE_REGEX2 = Regexp.compile("(www|beta).example.com/") UNSAFE_REGEX2 = Regexp.compile("(www|beta).example.com/") # $ Alert
UNSAFE_REGEX3 = Regexp.new("(www|beta).example.com/") UNSAFE_REGEX3 = Regexp.new("(www|beta).example.com/") # $ Alert
SAFE_REGEX = /(www|beta)\.example\.com\// SAFE_REGEX = /(www|beta)\.example\.com\//
def unsafe def unsafe

View File

@@ -1,31 +1,31 @@
def foo def foo
/^http:\/\/example.com/; # OK /^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.com/; # OK
/^http:\/\/test.example.net/; # NOT OK /^http:\/\/test.example.net/; # $ Alert // NOT OK
/^http:\/\/test.(example-a|example-b).com/; # NOT OK /^http:\/\/test.(example-a|example-b).com/; # $ Alert // NOT OK
/^http:\/\/(.+).example.com\//; # NOT OK /^http:\/\/(.+).example.com\//; # $ Alert // NOT OK
/^http:\/\/(\.+)\.example.com/; # OK /^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.com\/(?:.*)/; # $ Alert // OK
Regexp.new("^http://test.example.com"); # NOT OK Regexp.new("^http://test.example.com"); # $ Alert // NOT OK
if (s.match("^http://test.example.com")); end # 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 hostname = '^test.example.com'; # $ Alert // NOT OK
Regexp.new("#{hostname}$"); Regexp.new("#{hostname}$"); # $ Alert
domain = { hostname: 'test.example.com$' }; # NOT OK domain = { hostname: 'test.example.com$' }; # $ Alert // NOT OK
Regexp.new(domain[:hostname]); 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. 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) }; domains.map{ |d| convert2(d) };
/^(.+\.(?:example-a|example-b)\.com)\//; # NOT OK /^(.+\.(?:example-a|example-b)\.com)\//; # NOT OK
/^(https?:)?\/\/((service|www).)?example.com(?=$|\/)/; # NOT OK /^(https?:)?\/\/((service|www).)?example.com(?=$|\/)/; # $ Alert // NOT OK
/^(http|https):\/\/www.example.com\/p\/f\//; # NOT OK /^(http|https):\/\/www.example.com\/p\/f\//; # $ Alert // NOT OK
/^(http:\/\/sub.example.com\/)/i; # NOT OK /^(http:\/\/sub.example.com\/)/i; # $ Alert // NOT OK
/^https?:\/\/api.example.com/; # NOT OK /^https?:\/\/api.example.com/; # $ Alert // NOT OK
Regexp.new('^http://localhost:8000|' + "^https?://.+\\.example\\.com/"); # NOT OK Regexp.new('^http://localhost:8000|' + "^https?://.+\\.example\\.com/"); # $ Alert // NOT OK
Regexp.new("^http[s]?:\/\/?sub1\\.sub2\\.example\\.com\/f\/(.+)"); # NOT OK Regexp.new("^http[s]?:\/\/?sub1\\.sub2\\.example\\.com\/f\/(.+)"); # NOT OK
/^https:\/\/[a-z]*.example.com$/; # NOT OK /^https:\/\/[a-z]*.example.com$/; # $ Alert // NOT OK
Regexp.compile('^protos?://(localhost|.+.example.net|.+.example-a.com|.+.example-b.com|.+.example.internal)'); # NOT OK Regexp.compile('^protos?://(localhost|.+.example.net|.+.example-a.com|.+.example-b.com|.+.example.internal)'); # $ Alert // NOT OK
/^(example.dev|example.com)/; # 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$'; primary = 'example.com$';
Regexp.new('test.' + primary); # NOT OK, but not detected 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, wildcards are intentional
/^http:\/\/.\.example\.com\/index\.html/; # OK, the wildcard is 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 end
def id(e); return e; end def id(e); return e; end
def convert1(domain) def convert1(domain)
@@ -78,4 +78,4 @@ class B
end end
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) def test (x)
x.index("internal") != nil; # NOT OK, but not flagged x.index("internal") != nil; # NOT OK, but not flagged
x.index("localhost") != 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.com") != nil; # $ Alert // NOT OK
x.index("secure.net") != nil; # NOT OK x.index("secure.net") != nil; # $ Alert // NOT OK
x.index(".secure.com") != nil; # 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(".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") === nil; # $ Alert // NOT OK
x.index("secure.com") === 0; # NOT OK x.index("secure.com") === 0; # $ Alert // NOT OK
x.index("secure.com") >= 0; # NOT OK x.index("secure.com") >= 0; # $ Alert // NOT OK
x.start_with?("https://secure.com"); # NOT OK x.start_with?("https://secure.com"); # $ Alert // NOT OK
x.end_with?("secure.com"); # NOT OK x.end_with?("secure.com"); # $ Alert // NOT OK
x.end_with?(".secure.com"); # OK x.end_with?(".secure.com"); # OK
x.start_with?("secure.com/"); # OK x.start_with?("secure.com/"); # OK
x.index("secure.com/") === 0; # 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
x.index(":") != nil; # OK x.index(":") != nil; # OK
@@ -29,9 +29,9 @@ def test (x)
x.index("some/path") != nil; # OK x.index("some/path") != nil; # OK
x.index("/index.html") != nil; # OK x.index("/index.html") != nil; # OK
x.index(":template:") != nil; # OK x.index(":template:") != nil; # 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; # NOT OK x.index("https://secure.com:443") != nil; # $ Alert // NOT OK
x.index("https://secure.com/") != nil; # NOT OK x.index("https://secure.com/") != nil; # $ Alert // NOT OK
x.index(".cn") != nil; # NOT OK, but not flagged x.index(".cn") != nil; # NOT OK, but not flagged
x.index(".jpg") != nil; # OK 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("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.index("https://") != nil; # OK
x.start_with?("https://example.internal"); # NOT OK x.start_with?("https://example.internal"); # $ Alert // NOT OK
x.index('https://example.internal.org') != 0; # NOT OK x.index('https://example.internal.org') != 0; # $ Alert // NOT OK
x.index('https://example.internal.org') === 0; # NOT OK x.index('https://example.internal.org') === 0; # $ Alert // NOT OK
x.end_with?("internal.com"); # NOT OK x.end_with?("internal.com"); # $ Alert // NOT OK
x.start_with?("https://example.internal:80"); # OK x.start_with?("https://example.internal:80"); # OK
x.index("secure.com") != nil; # NOT OK x.index("secure.com") != nil; # $ Alert // NOT OK
x.index("secure.com") === nil; # OK x.index("secure.com") === nil; # $ Alert // OK
!(x.index("secure.com") != nil); # OK !(x.index("secure.com") != nil); # $ Alert // OK
!x.include?("secure.com"); # OK !x.include?("secure.com"); # $ Alert // OK
if !x.include?("secure.com") # NOT OK if !x.include?("secure.com") # $ Alert // NOT OK
else else
doSomeThingWithTrustedURL(x); doSomeThingWithTrustedURL(x);
end end
x.start_with?("https://secure.com/foo/bar"); # OK - a forward slash after the domain makes prefix checks safe. 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/foo/bar") >= 0 # $ Alert // NOT OK - the url can be anywhere in the string.
x.index("https://secure.com") >= 0 # NOT OK x.index("https://secure.com") >= 0 # $ Alert // 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-baz") >= 0 # $ Alert // NOT OK - the url can be anywhere in the string.
end 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 class Foobar
def foo1(name) def foo1(name) # $ Source
raise Blabity, 'Invalid thing' if name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # NOT OK raise Blabity, 'Invalid thing' if name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # $ Alert // NOT OK
end end
def foo2(name) def foo2(name) # $ Source
raise Blabity, 'Invalid thing' unless name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # NOT OK raise Blabity, 'Invalid thing' unless name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # $ Alert // NOT OK
end end
def foo3(name) def foo3(name)
raise Blabity, 'Invalid thing' unless name !~ /\A[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*\z/ # OK raise Blabity, 'Invalid thing' unless name !~ /\A[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*\z/ # OK
end end
def foo4(name) def foo4(name) # $ Source
raise Blabity, 'Invalid thing' unless not name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # NOT OK raise Blabity, 'Invalid thing' unless not name !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ # $ Alert // NOT OK
end end
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 /^www\.example\.com$/ # BAD: uses end-of-line anchors rather than end-of-string anchors
/\Awww\.example\.com\z/ # GOOD /\Awww\.example\.com\z/ # GOOD
/foo\.bar/ # GOOD /foo\.bar/ # GOOD
/https?:\/\/good\.com/ # BAD /https?:\/\/good\.com/ # $ Alert // BAD
/^https?:\/\/good\.com/ # BAD: missing end-of-string anchor /^https?:\/\/good\.com/ # $ Alert // BAD: missing end-of-string anchor
/(^https?:\/\/good1\.com)|(^https?:#good2\.com)/ # BAD: missing end-of-string anchor /(^https?:\/\/good1\.com)|(^https?:#good2\.com)/ # BAD: missing end-of-string anchor
/bar/ # GOOD /bar/ # GOOD
@@ -16,40 +16,40 @@ foo.gsub!(/www\.example\.com/, "bar") # GOOD
foo.sub!(/www\.example\.com/, "bar") # GOOD foo.sub!(/www\.example\.com/, "bar") # GOOD
/^a|/ /^a|/
/^a|b/ # BAD /^a|b/ # $ Alert // BAD
/a|^b/ /a|^b/
/^a|^b/ /^a|^b/
/^a|b|c/ # BAD /^a|b|c/ # $ Alert // BAD
/a|^b|c/ /a|^b|c/
/a|b|^c/ /a|b|^c/
/^a|^b|c/ /^a|^b|c/
/(^a)|b/ /(^a)|b/
/^a|(b)/ # BAD /^a|(b)/ # $ Alert // BAD
/^a|(^b)/ /^a|(^b)/
/^(a)|(b)/ # BAD /^(a)|(b)/ # $ Alert // BAD
/a|b$/ # BAD /a|b$/ # $ Alert // BAD
/a$|b/ /a$|b/
/a$|b$/ /a$|b$/
/a|b|c$/ # BAD /a|b|c$/ # $ Alert // BAD
/a|b$|c/ /a|b$|c/
/a$|b|c/ /a$|b|c/
/a|b$|c$/ /a|b$|c$/
/a|(b$)/ /a|(b$)/
/(a)|b$/ # BAD /(a)|b$/ # $ Alert // BAD
/(a$)|b$/ /(a$)|b$/
/(a)|(b)$/ # BAD /(a)|(b)$/ # $ Alert // BAD
/^good.com|better.com/ # BAD /^good.com|better.com/ # $ Alert // BAD
/^good\.com|better\.com/ # BAD /^good\.com|better\.com/ # $ Alert // BAD
/^good\\.com|better\\.com/ # BAD /^good\\.com|better\\.com/ # $ Alert // BAD
/^good\\\.com|better\\\.com/ # BAD /^good\\\.com|better\\\.com/ # $ Alert // BAD
/^good\\\\.com|better\\\\.com/ # BAD /^good\\\\.com|better\\\\.com/ # $ Alert // BAD
/^foo|bar|baz$/ # BAD /^foo|bar|baz$/ # $ Alert // BAD
/^foo|%/ # OK /^foo|%/ # OK
REGEXP = /foo/ 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 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
"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" # $ Alert // BAD
"some text".match "http://example.com" # 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 isAscii = /^[\x00-\x7F]*$/ # OK
@@ -12,22 +12,22 @@ codePoints = /[^\x21-\x7E]|[\[\](){}<>\/%]/ # OK
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/ # 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 escapes = /[\000-\037\047\134\177-\377]/n # OK - they are escapes
nested = /[a-z&&[^a-c]]/ # OK 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 class UsersController < ActionController::Base
def create def create
file = params[:file] file = params[:file] # $ Source
open(file) # BAD open(file) # $ Alert // BAD
IO.read(file) # BAD IO.read(file) # $ Alert // BAD
IO.write(file) # BAD IO.write(file) # $ Alert // BAD
IO.binread(file) # BAD IO.binread(file) # $ Alert // BAD
IO.binwrite(file) # BAD IO.binwrite(file) # $ Alert // BAD
IO.foreach(file) # BAD IO.foreach(file) # $ Alert // BAD
IO.readlines(file) # BAD IO.readlines(file) # $ Alert // BAD
URI.open(file) # 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 IO.read(File.join("", file)) # GOOD - file path is sanitised by guard
File.open(file).read # GOOD File.open(file).read # GOOD
@@ -23,6 +23,6 @@ class UsersController < ActionController::Base
IO.read(file) # GOOD - file path is sanitised by guard IO.read(file) # GOOD - file path is sanitised by guard
end 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
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 def create
file = params[:file] file = params[:file]
open(file) # BAD open(file) # $ Alert // BAD
IO.read(file) # BAD IO.read(file) # $ Alert // BAD
IO.write(file) # BAD IO.write(file) # $ Alert // BAD
IO.binread(file) # BAD IO.binread(file) # $ Alert // BAD
IO.binwrite(file) # BAD IO.binwrite(file) # $ Alert // BAD
IO.foreach(file) # BAD IO.foreach(file) # $ Alert // BAD
IO.readlines(file) # BAD IO.readlines(file) # $ Alert // BAD
URI.open(file) # BAD URI.open(file) # $ Alert // BAD
File.open(file).read # GOOD File.open(file).read # GOOD
Kernel.open(file) # BAD Kernel.open(file) # $ Alert // BAD
File.open(file, "r") # GOOD File.open(file, "r") # GOOD
@@ -25,7 +25,7 @@ class UsersController < ActionController::Base
Kernel.open("this is #{fine}") # GOOD 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 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.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
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 class Foobar
def foo1(target) def foo1(target) # $ Source
IO.popen("cat #{target}", "w") # NOT OK - everything assumed to be imported... IO.popen("cat #{target}", "w") # $ Alert // NOT OK - everything assumed to be imported...
end end
end end

View File

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

View File

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

View File

@@ -1,10 +1,10 @@
class Foobar class Foobar
def foo1(target) def foo1(target) # $ Source
IO.popen("cat #{target}", "w") # NOT OK IO.popen("cat #{target}", "w") # $ Alert // NOT OK
end end
def foo2(x) def foo2(x) # $ Source
format = sprintf("cat %s", x) # NOT OK format = sprintf("cat %s", x) # $ Alert // NOT OK
IO.popen(format, "w") IO.popen(format, "w")
end end
@@ -12,30 +12,30 @@ class Foobar
File.read(path) # OK File.read(path) # OK
end 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 #{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 #{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 #{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 #{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 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.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
end end
require File.join(File.dirname(__FILE__), 'sub', 'other') require File.join(File.dirname(__FILE__), 'sub', 'other')
class Foobar2 class Foobar2
def foo1(target) def foo1(target) # $ Source
IO.popen("cat #{target}", "w") # NOT OK IO.popen("cat #{target}", "w") # $ Alert // NOT OK
end end
def id(x) def id(x) # $ Source
IO.popen("cat #{x}", "w") # NOT OK - the parameter is not a constant. IO.popen("cat #{x}", "w") # $ Alert // NOT OK - the parameter is not a constant.
return x return x
end end
@@ -44,27 +44,27 @@ class Foobar2
end end
# class methods # class methods
def self.foo(target) def self.foo(target) # $ Source
IO.popen("cat #{target}", "w") # NOT OK IO.popen("cat #{target}", "w") # $ Alert // NOT OK
end end
def arrayJoin(x) def arrayJoin(x) # $ Source
IO.popen(x.join(' '), "w") # NOT OK 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 end
def string_concat(x) def string_concat(x) # $ Source
IO.popen("cat " + x, "w") # NOT OK IO.popen("cat " + x, "w") # $ Alert // NOT OK
end end
def array_taint (x, y) def array_taint (x, y) # $ Source
arr = ["cat"] arr = ["cat"]
arr.push(x) arr.push(x)
IO.popen(arr.join(' '), "w") # NOT OK IO.popen(arr.join(' '), "w") # $ Alert // NOT OK
arr2 = ["cat"] arr2 = ["cat"]
arr2 << y arr2 << y
IO.popen(arr.join(' '), "w") # NOT OK IO.popen(arr.join(' '), "w") # $ Alert // NOT OK
end end
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 end
def user_name def user_name
return params[:user_name] return params[:user_name] # $ Source[rb/reflected-xss]
end end
def user_name_memo def user_name_memo
@user_name ||= params[:user_name] @user_name ||= params[:user_name] # $ Source[rb/reflected-xss]
end end
def show def show
@user_website = params[:website] @user_website = params[:website] # $ Source[rb/reflected-xss]
dt = params[:text] dt = params[:text] # $ Source[rb/reflected-xss]
@instance_text = dt @instance_text = dt
@safe_foo = params[:text] @safe_foo = params[:text]
@safe_foo = "safe_foo" @safe_foo = "safe_foo"
@html_escaped = ERB::Util.html_escape(params[:text]) @html_escaped = ERB::Util.html_escape(params[:text])
@header_escaped = ERB::Util.html_escape(cookies[:foo]) # OK - cookies not controllable by 3rd party @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 response.header["x-customer-header"] = params[:bar] # OK - header not relevant to XSS
render "foo/bars/show", locals: { display_text: dt, safe_text: "hello" } render "foo/bars/show", locals: { display_text: dt, safe_text: "hello" }
end end
def make_safe_html def make_safe_html
str = params[:user_name] str = params[:user_name] # $ Source[rb/reflected-xss]
str.html_safe str.html_safe # $ Alert[rb/reflected-xss]
translate("welcome", name: params[:user_name]).html_safe # NOT OK - translate preserves taint 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 # NOT OK - t is an alias of translate 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 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.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 # NOT OK - alias I18n.translate("welcome_html", name: params[:user_name]).html_safe # $ Alert[rb/reflected-xss] // NOT OK - alias
end end
end end

View File

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

View File

@@ -2,10 +2,10 @@
<%= raw @display_text %> <%= raw @display_text %>
<%# BAD: A local rendered raw as a local variable %> <%# 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 %> <%# 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 %> <%# GOOD: A local rendered with default escaping via the local_assigns hash %>
<%= local_assigns[:display_text] %> <%= local_assigns[:display_text] %>

View File

@@ -1,20 +1,20 @@
<%# BAD: An instance variable rendered without escaping %> <%# 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 %> <%# 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 %> <%# 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 %> <% key = :display_text %>
<%# BAD: A local rendered raw via the locals_assigns hash %> <%# BAD: A local rendered raw via the locals_assigns hash %>
<%= raw local_assigns[key] %> <%= raw local_assigns[key] %> <%# $ Alert[rb/reflected-xss] %>
<ul> <ul>
<% for key in [:display_text, :safe_text] do %> <% for key in [:display_text, :safe_text] do %>
<%# BAD: A local rendered raw via the locals hash %> <%# 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 %> <% end %>
</ul> </ul>
@@ -32,28 +32,28 @@
<%# BAD: html_safe marks string as not requiring HTML escaping %> <%# 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 %> <%# 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 } %> <%= render partial: 'foo/bars/widget', locals: { display_text: "widget_" + display_text } %>
<%# BAD: user_name is a helper method that returns unsanitized user-input %> <%# 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 %> <%# 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 %> <%# 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 %> <%# 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 %> <%# 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 user website
<% end %> <% end %>
@@ -70,20 +70,20 @@
%> %>
<%# BAD: simple_format called with sanitize: false %> <%# 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 %> <%# 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 %> <%# GOOD: input is sanitized %>
<%= sanitize(params[:comment]).html_safe %> <%= sanitize(params[:comment]).html_safe %>
<%# BAD: A local rendered raw as a local variable %> <%# BAD: A local rendered raw as a local variable %>
<%== display_text %> <%== display_text %> <%# $ Alert[rb/reflected-xss] %>
<%# BAD: translate preserves taint %> <%# BAD: translate preserves taint %>
<%= raw translate("welcome", name: display_text) %> <%= raw translate("welcome", name: display_text) %> <%# $ Alert[rb/reflected-xss] %>
<%= raw t("welcome", name: display_text) %> <%= raw t("welcome", name: display_text) %> <%# $ Alert[rb/reflected-xss] %>
<%# GOOD: translate sanitizes for html keys %> <%# GOOD: translate sanitizes for html keys %>
<%= raw t("welcome1.html", name: display_text) %> <%= raw t("welcome1.html", name: display_text) %>

View File

@@ -1,17 +1,17 @@
<%# BAD: A local rendered raw as a local variable %> <%# 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 %> <%# 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 %> <% key = :display_text %>
<%# BAD: A local rendered raw via the locals_assigns hash %> <%# BAD: A local rendered raw via the locals_assigns hash %>
<%= raw local_assigns[key] %> <%= raw local_assigns[key] %> <%# $ Alert[rb/stored-xss] %>
<ul> <ul>
<% for key in [:display_text, :safe_text] do %> <% for key in [:display_text, :safe_text] do %>
<%# BAD: A local rendered raw via the locals hash %> <%# 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 %> <% end %>
</ul> </ul>
@@ -29,12 +29,12 @@
<%# BAD: html_safe marks string as not requiring HTML escaping %> <%# 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 %> <%# 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 } %> <%= render partial: 'foo/bars/widget', locals: { display_text: "widget_" + display_text } %>
@@ -43,7 +43,7 @@
<%= user_name_handle.html_safe %> <%= user_name_handle.html_safe %>
<%# BAD: Direct to a database value without escaping %> <%# 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 %> <%# BAD: Indirect to a database value without escaping %>
<%= @user.raw_name.html_safe %> <%= @user.raw_name.html_safe %>
@@ -60,7 +60,7 @@
<%# BAD: Direct to a database value without escaping %> <%# BAD: Direct to a database value without escaping %>
<%= <%=
some_user = User.find 1 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) %> <%# 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 %> <%# 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 %> <%# GOOD: The `foo.bar.baz` is not recognized as a source %>

View File

@@ -1,27 +1,27 @@
class Foobar class Foobar
def create_user_description(name) def create_user_description(name) # $ Source[rb/html-constructed-from-input]
"<h2>#{name}</h2>".html_safe # NOT OK - the parameter is not escaped "<h2>#{name}</h2>".html_safe # $ Alert[rb/html-constructed-from-input] // NOT OK - the parameter is not escaped
# escape # escape
"<h2>#{ERB::Util.html_escape(name)}</h2>".html_safe # OK - the parameter is escaped "<h2>#{ERB::Util.html_escape(name)}</h2>".html_safe # OK - the parameter is escaped
end end
def string_like_literal name def string_like_literal name # $ Source[rb/html-constructed-from-input]
h = <<-HTML h = <<-HTML
<h2>#{name}</h2> <h2>#{name}</h2> # $ Alert[rb/html-constructed-from-input]
HTML HTML
h.html_safe # NOT OK - the parameter is not escaped h.html_safe # NOT OK - the parameter is not escaped
end end
def sprintf_use name def sprintf_use name # $ Source[rb/html-constructed-from-input]
sprintf("<h2>%s</h2>", name).html_safe # NOT OK - the parameter is not escaped sprintf("<h2>%s</h2>", name).html_safe # $ Alert[rb/html-constructed-from-input] // NOT OK - the parameter is not escaped
# escape # escape
sprintf("<h2>%s</h2>", ERB::Util.html_escape(name)).html_safe # OK - the parameter is escaped sprintf("<h2>%s</h2>", ERB::Util.html_escape(name)).html_safe # OK - the parameter is escaped
end end
def create_user_description2(name) def create_user_description2(name) # $ Source[rb/html-constructed-from-input]
"<h2>#{name}</h2>".html_safe # NOT OK - the value is not necessarily HTML safe "<h2>#{name}</h2>".html_safe # $ Alert[rb/html-constructed-from-input] // NOT OK - the value is not necessarily HTML safe
if name.html_safe? if name.html_safe?
"<h2>#{name}</h2>".html_safe # OK - value is marked as being 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) def self.authenticate(name, pass)
# BAD: possible untrusted input interpolated into SQL fragment # 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 # 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 # GOOD: using SQL parameters
find(:first, conditions: ["name = ? and pass = ?", name, pass]) find(:first, conditions: ["name = ? and pass = ?", name, pass])
# BAD: interpolation with flow # BAD: interpolation with flow
conds = "name=#{name}" conds = "name=#{name}" # $ Alert
find(:first, conditions: conds) find(:first, conditions: conds)
end end
@@ -27,7 +27,7 @@ class Admin < User
def self.delete_by(condition = nil) def self.delete_by(condition = nil)
# BAD: `delete_by overrides an ActiveRecord method, but doesn't perform # BAD: `delete_by overrides an ActiveRecord method, but doesn't perform
# any validation before passing its arguments on to another ActiveRecord method # any validation before passing its arguments on to another ActiveRecord method
destroy_by(condition) destroy_by(condition) # $ Alert
end end
end end
@@ -39,64 +39,64 @@ class FooController < ActionController::Base
def some_request_handler def some_request_handler
# BAD: executes `SELECT AVG(#{params[:column]}) FROM "users"` # BAD: executes `SELECT AVG(#{params[:column]}) FROM "users"`
# where `params[:column]` is unsanitized # where `params[:column]` is unsanitized
User.calculate(:average, params[:column]) User.calculate(:average, params[:column]) # $ Alert
# BAD: executes `SELECT MAX(#{params[:column]}) FROM "users"` # BAD: executes `SELECT MAX(#{params[:column]}) FROM "users"`
# where `params[:column]` is unsanitized # where `params[:column]` is unsanitized
User.maximum(params[:column]) User.maximum(params[:column]) # $ Alert
# BAD: executes `DELETE FROM "users" WHERE (id = '#{params[:id]}')` # BAD: executes `DELETE FROM "users" WHERE (id = '#{params[:id]}')`
# where `params[:id]` is unsanitized # 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]}')` # BAD: executes `DELETE FROM "users" WHERE (id = '#{params[:id]}')`
# where `params[:id]` is unsanitized # where `params[:id]` is unsanitized
# (in Rails < 4.0) # (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]}')` # BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')`
# where `params[:id]` is unsanitized # 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]}')` # BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')`
# where `params[:id]` is unsanitized # where `params[:id]` is unsanitized
# (in Rails < 4.0) # (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` # BAD: executes `SELECT "users".* FROM "users" WHERE id BETWEEN '#{params[:min_id]}' AND 100000`
# where `params[:min_id]` is unsanitized # where `params[:min_id]` is unsanitized
User.where(<<-SQL, MAX_USER_ID) User.where(<<-SQL, MAX_USER_ID) # $ Alert
id BETWEEN '#{params[:min_id]}' AND ? id BETWEEN '#{params[:min_id]}' AND ? # $ Source
SQL SQL
# BAD: chained method case # BAD: chained method case
# executes `SELECT "users".* FROM "users" WHERE (NOT (user_id = 'params[:id]'))` # executes `SELECT "users".* FROM "users" WHERE (NOT (user_id = 'params[:id]'))`
# where `params[:id]` is unsanitized # 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 # BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')` LIMIT 1
# where `params[:id]` is unsanitized # 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 user = User.first
# BAD: executes `SELECT "users".* FROM "users" WHERE id = 1 LIMIT 1 #{params[:lock]}` # BAD: executes `SELECT "users".* FROM "users" WHERE id = 1 LIMIT 1 #{params[:lock]}`
# where `params[:lock]` is unsanitized # where `params[:lock]` is unsanitized
user.reload(lock: params[:lock]) user.reload(lock: params[:lock]) # $ Alert
# BAD: executes `SELECT #{params[:column]} FROM "users"` # BAD: executes `SELECT #{params[:column]} FROM "users"`
# where `params[:column]` is unsanitized # where `params[:column]` is unsanitized
User.select(params[:column]) User.select(params[:column]) # $ Alert
User.reselect(params[:column]) User.reselect(params[:column]) # $ Alert
# BAD: executes `SELECT "users".* FROM "users" WHERE (#{params[:condition]})` # BAD: executes `SELECT "users".* FROM "users" WHERE (#{params[:condition]})`
# where `params[:condition]` is unsanitized # where `params[:condition]` is unsanitized
User.rewhere(params[:condition]) User.rewhere(params[:condition]) # $ Alert
# BAD: executes `UPDATE "users" SET #{params[:fields]}` # BAD: executes `UPDATE "users" SET #{params[:fields]}`
# where `params[:fields]` is unsanitized # where `params[:fields]` is unsanitized
User.update_all(params[:fields]) User.update_all(params[:fields]) # $ Alert
# GOOD -- `update_all` sanitizes its bind variable arguments # GOOD -- `update_all` sanitizes its bind variable arguments
User.find_by(name: params[:user_name]) 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) # BAD -- `update_all` does not sanitize its query (array arg)
User.find_by(name: params[:user_name]) 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) # BAD -- `update_all` does not sanitize its query (string arg)
User.find_by(name: params[:user_name]) 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.select('a','b', params[:column]) # $ Alert
User.reselect('a','b', params[:column]) User.reselect('a','b', params[:column]) # $ Alert
User.order('a ASC', "b #{params[:direction]}") User.order('a ASC', "b #{params[:direction]}") # $ Alert
User.reorder('a ASC', "b #{params[:direction]}") User.reorder('a ASC', "b #{params[:direction]}") # $ Alert
User.group('a', params[:column]) User.group('a', params[:column]) # $ Alert
User.pluck('a', params[:column]) User.pluck('a', params[:column]) # $ Alert
User.joins(:a, params[:column]) 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]}` # BAD: executes `SELECT users.* FROM #{params[:tab]}`
# where `params[:tab]` is unsanitized # 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]} # 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
end end
class BarController < ApplicationController class BarController < ApplicationController
def some_other_request_handler def some_other_request_handler
ps = params ps = params # $ Source
uid = ps[:id] uid = ps[:id]
uidEq = "= '#{uid}'" uidEq = "= '#{uid}'"
# BAD: executes `DELETE FROM "users" WHERE (id = #{uid})` # BAD: executes `DELETE FROM "users" WHERE (id = #{uid})`
# where `uid` is unsantized # where `uid` is unsantized
User.delete_by("id " + uidEq) User.delete_by("id " + uidEq) # $ Alert
end end
def safe_paths def safe_paths
@@ -171,7 +171,7 @@ end
class BazController < BarController class BazController < BarController
def yet_another_handler def yet_another_handler
Admin.delete_by(params[:admin_condition]) Admin.delete_by(params[:admin_condition]) # $ Alert, Source
end end
end end
@@ -185,7 +185,7 @@ class AnnotatedController < ActionController::Base
def unsafe_action def unsafe_action
name = params[:user_name] name = params[:user_name]
# BAD: user input passed into annotations are vulnerable to SQLi # 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
end end
@@ -198,27 +198,27 @@ class RegressionController < ActionController::Base
def index def index
my_params = permitted_params my_params = permitted_params
query = "SELECT * FROM users WHERE id = #{my_params[:user_id]}" query = "SELECT * FROM users WHERE id = #{my_params[:user_id]}"
result = Regression.find_by_sql(query) result = Regression.find_by_sql(query) # $ Alert
end end
def permitted_params 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 end
def show def show
ActiveRecord::Base.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]}") Regression.connection.execute("SELECT * FROM users WHERE id = #{permitted_params[:user_id]}") # $ Alert
end end
end end
class User class User
scope :with_role, ->(role) { where("role = #{role}") } scope :with_role, ->(role) { where("role = #{role}") } # $ Alert
end end
class UsersController < ActionController::Base class UsersController < ActionController::Base
def index def index
# BAD: user input passed to scope which uses it without sanitization. # 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
end end

View File

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

View File

@@ -3,7 +3,7 @@ class FooController < ActionController::Base
def some_request_handler def some_request_handler
# A string tainted by user input is inserted into a query # A string tainted by user input is inserted into a query
# (i.e a remote flow source) # (i.e a remote flow source)
name = params[:name] name = params[:name] # $ Source
# Establish a connection to a PostgreSQL database # Establish a connection to a PostgreSQL database
conn = PG::Connection.open(:dbname => 'postgresql', :user => 'user', :password => 'pass', :host => 'localhost', :port => '5432') 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() # .exec() and .async_exec()
# BAD: SQL statement constructed from user input # BAD: SQL statement constructed from user input
qry1 = "SELECT * FROM users WHERE username = '#{name}';" qry1 = "SELECT * FROM users WHERE username = '#{name}';"
conn.exec(qry1) conn.exec(qry1) # $ Alert
conn.async_exec(qry1) conn.async_exec(qry1) # $ Alert
# .exec_params() and .async_exec_params() # .exec_params() and .async_exec_params()
# BAD: SQL statement constructed from user input # BAD: SQL statement constructed from user input
qry2 = "SELECT * FROM users WHERE username = '#{name}';" qry2 = "SELECT * FROM users WHERE username = '#{name}';"
conn.exec_params(qry2) conn.exec_params(qry2) # $ Alert
conn.async_exec_params(qry2) conn.async_exec_params(qry2) # $ Alert
# .exec_params() and .async_exec_params() # .exec_params() and .async_exec_params()
# GOOD: SQL statement constructed from sanitized user input # GOOD: SQL statement constructed from sanitized user input
@@ -29,7 +29,7 @@ class FooController < ActionController::Base
# .prepare() and .exec_prepared() # .prepare() and .exec_prepared()
# BAD: SQL statement constructed from user input # BAD: SQL statement constructed from user input
qry3 = "SELECT * FROM users WHERE username = '#{name}';" qry3 = "SELECT * FROM users WHERE username = '#{name}';"
conn.prepare("query_1", qry3) conn.prepare("query_1", qry3) # $ Alert
conn.exec_prepared('query_1') conn.exec_prepared('query_1')
# .prepare() and .exec_prepared() # .prepare() and .exec_prepared()
@@ -41,7 +41,7 @@ class FooController < ActionController::Base
# .prepare() and .exec_prepared() # .prepare() and .exec_prepared()
# NOT EXECUTED: SQL statement constructed from user input but not executed # NOT EXECUTED: SQL statement constructed from user input but not executed
qry3 = "SELECT * FROM users WHERE username = '#{name}';" qry3 = "SELECT * FROM users WHERE username = '#{name}';"
conn.prepare("query_3", qry3) conn.prepare("query_3", qry3) # $ Alert
end end
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 class Foobar
def foo1(target) def foo1(target) # $ Source
eval("foo = #{target}") # NOT OK eval("foo = #{target}") # $ Alert // NOT OK
end end
# sprintf # sprintf
def foo2(x) def foo2(x) # $ Source
eval(sprintf("foo = %s", x)) # NOT OK eval(sprintf("foo = %s", x)) # $ Alert // NOT OK
end end
# String#% # String#%
def foo3(x) def foo3(x) # $ Source
eval("foo = %{foo}" % {foo: x}) # NOT OK eval("foo = %{foo}" % {foo: x}) # $ Alert // NOT OK
end end
def indirect_eval(x) def indirect_eval(x)
@@ -25,42 +25,42 @@ class Foobar
eval("def \n #{code} \n end") # OK - parameter is named code eval("def \n #{code} \n end") # OK - parameter is named code
end end
def joinStuff(my_arr) def joinStuff(my_arr) # $ Source
eval(my_arr.join("\n")) # NOT OK eval(my_arr.join("\n")) # $ Alert // NOT OK
end end
def joinWithElemt(x) def joinWithElemt(x) # $ Source
arr = [x, "foobar"] arr = [x, "foobar"]
eval(arr.join("\n")) # NOT OK eval(arr.join("\n")) # $ Alert // NOT OK
end end
def pushArr(x, y) def pushArr(x, y) # $ Source
arr = [] arr = []
arr.push(x) arr.push(x)
eval(arr.join("\n")) # NOT OK eval(arr.join("\n")) # $ Alert // NOT OK
arr2 = [] arr2 = []
arr2 << y arr2 << y
eval(arr.join("\n")) # NOT OK eval(arr.join("\n")) # $ Alert // NOT OK
end end
def hereDoc(x) def hereDoc(x) # $ Source
foo = <<~HERE foo = <<~HERE
#{x} #{x} # $ Alert
HERE HERE
eval(foo) # NOT OK eval(foo) # NOT OK
end end
def string_concat(x) def string_concat(x) # $ Source
foo = "foo = " + x foo = "foo = " + x # $ Alert
eval(foo) # NOT OK eval(foo) # NOT OK
end end
def join_indirect(x, y) def join_indirect(x, y) # $ Source
arr = Array(x) arr = Array(x)
eval(arr.join(" ")) # NOT OK eval(arr.join(" ")) # $ Alert // NOT OK
arr2 = [Array(["foo = ", y]).join(" ")] arr2 = [Array(["foo = ", y]).join(" ")]
eval(arr2.join("\n")) # NOT OK eval(arr2.join("\n")) # $ Alert // NOT OK
end end
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 = [ filters = [
/<script.*?>.*?<\/script>/i, # NOT OK - doesn't match newlines or `</script >` /<script.*?>.*?<\/script>/i, # $ Alert // NOT OK - doesn't match newlines or `</script >`
/<script.*?>.*?<\/script>/im, # NOT OK - doesn't match `</script >` /<script.*?>.*?<\/script>/im, # $ Alert // NOT OK - doesn't match `</script >`
/<script.*?>.*?<\/script[^>]*>/im, # OK /<script.*?>.*?<\/script[^>]*>/im, # OK
/<!--.*-->/im, # OK - we don't care regexps that only match comments /<!--.*-->/im, # OK - we don't care regexps that only match comments
/<!--.*--!?>/im, # OK /<!--.*--!?>/im, # OK
/<!--.*--!?>/i, # NOT OK, does not match newlines /<!--.*--!?>/i, # $ Alert // NOT OK, does not match newlines
/<script.*?>(.|\s)*?<\/script[^>]*>/i, # NOT OK - doesn't match inside the script tag /<script.*?>(.|\s)*?<\/script[^>]*>/i, # $ Alert // NOT OK - doesn't match inside the script tag
/<script[^>]*?>.*?<\/script[^>]*>/i, # NOT OK - doesn't match newlines inside the content /<script[^>]*?>.*?<\/script[^>]*>/i, # $ Alert // 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, # $ Alert // 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(\s|\w|=|')*?>.*?<\/script[^>]*>/im, # $ Alert // NOT OK - does not match double quotes for attribute values
/<script( |\n|\w|=|'|")*?>.*?<\/script[^>]*>/im, # NOT OK - does not match tabs between attributes /<script( |\n|\w|=|'|")*?>.*?<\/script[^>]*>/im, # $ Alert // NOT OK - does not match tabs between attributes
/<script.*?>.*?<\/script[^>]*>/m, # NOT OK - does not match uppercase SCRIPT tags /<script.*?>.*?<\/script[^>]*>/m, # $ Alert // NOT OK - does not match uppercase SCRIPT tags
/<(script|SCRIPT).*?>.*?<\/(script|SCRIPT)[^>]*>/m, # NOT OK - does not match mixed case script tags /<(script|SCRIPT).*?>.*?<\/(script|SCRIPT)[^>]*>/m, # $ Alert // NOT OK - does not match mixed case script tags
/<script[^>]*?>[\s\S]*?<\/script.*>/i, # NOT OK - doesn't match newlines in the end tag /<script[^>]*?>[\s\S]*?<\/script.*>/i, # $ Alert // NOT OK - doesn't match newlines in the end tag
/<script[^>]*?>[\s\S]*?<\/script[^>]*?>/i, # OK /<script[^>]*?>[\s\S]*?<\/script[^>]*?>/i, # OK
/<script\b[^>]*>([\s\S]*?)<\/script>/gi, # NOT OK - too strict matching on the end tag /<script\b[^>]*>([\s\S]*?)<\/script>/gi, # $ Alert // 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\s]*?>/, # $ Alert // 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 /<(?:(?:\/([^>]+)>)|(?:!--([\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) def bad1(s)
s.sub "'", "" # NOT OK s.sub "'", "" # $ Alert // NOT OK
s.sub! "'", "" # NOT OK s.sub! "'", "" # $ Alert // NOT OK
end end
def bad2(s) def bad2(s)
s.sub /'/, "" # NOT OK s.sub /'/, "" # $ Alert // NOT OK
s.sub! /'/, "" # NOT OK s.sub! /'/, "" # $ Alert // NOT OK
end end
def bad3(s1, s2, s3) def bad3(s1, s2, s3)
s1.gsub /'/, "\\'" # NOT OK s1.gsub /'/, "\\'" # $ Alert // NOT OK
s1.gsub /'/, '\\\'' # NOT OK s1.gsub /'/, '\\\'' # $ Alert // NOT OK
s2.gsub! /'/, "\\'" # NOT OK s2.gsub! /'/, "\\'" # $ Alert // NOT OK
s3.gsub! /'/, '\\\'' # NOT OK s3.gsub! /'/, '\\\'' # $ Alert // NOT OK
end end
def bad4(s1, s2, s3) def bad4(s1, s2, s3)
s1.gsub /'/, "\\\\\\&" # NOT OK s1.gsub /'/, "\\\\\\&" # $ Alert // NOT OK
s1.gsub /'/, '\\\\\&' # NOT OK s1.gsub /'/, '\\\\\&' # $ Alert // NOT OK
s2.gsub! /'/, "\\\\\\&" # NOT OK s2.gsub! /'/, "\\\\\\&" # $ Alert // NOT OK
s3.gsub! /'/, '\\\\\&' # NOT OK s3.gsub! /'/, '\\\\\&' # $ Alert // NOT OK
end end
def bad5(s) def bad5(s)
s.gsub /['"]/, '\\\\\&' # NOT OK s.gsub /['"]/, '\\\\\&' # $ Alert // NOT OK
s.gsub! /['"]/, '\\\\\&' # NOT OK s.gsub! /['"]/, '\\\\\&' # $ Alert // NOT OK
end end
def bad6(s) def bad6(s)
s.gsub /(['"])/, '\\\\\\1' # NOT OK s.gsub /(['"])/, '\\\\\\1' # $ Alert // NOT OK
s.gsub! /(['"])/, '\\\\\\1' # NOT OK s.gsub! /(['"])/, '\\\\\\1' # $ Alert // NOT OK
end end
def bad7(s) def bad7(s)
s.gsub /('|")/, '\\\\\1' # NOT OK s.gsub /('|")/, '\\\\\1' # $ Alert // NOT OK
s.gsub! /('|")/, '\\\\\1' # NOT OK s.gsub! /('|")/, '\\\\\1' # $ Alert // NOT OK
end end
def bad8(s) def bad8(s)
s.sub '|', '' # NOT OK s.sub '|', '' # $ Alert // NOT OK
s.sub! '|', '' # NOT OK s.sub! '|', '' # $ Alert // NOT OK
end end
def bad9(s1, s2, s3, s4) def bad9(s1, s2, s3, s4)
s1.gsub /"/, "\\\"" # NOT OK s1.gsub /"/, "\\\"" # $ Alert // NOT OK
s1.gsub /"/, '\\"' # NOT OK s1.gsub /"/, '\\"' # $ Alert // NOT OK
s1.gsub '"', '\\"' # NOT OK s1.gsub '"', '\\"' # $ Alert // NOT OK
s2.gsub! /"/, "\\\"" # NOT OK s2.gsub! /"/, "\\\"" # $ Alert // NOT OK
s3.gsub! /"/, '\\"' # NOT OK s3.gsub! /"/, '\\"' # $ Alert // NOT OK
s4.gsub! '"', '\\"' # NOT OK s4.gsub! '"', '\\"' # $ Alert // NOT OK
end end
def bad10(s) def bad10(s)
s.sub "/", "%2F" # NOT OK s.sub "/", "%2F" # $ Alert // NOT OK
s.sub! "/", "%2F" # NOT OK s.sub! "/", "%2F" # $ Alert // NOT OK
end end
def bad11(s) def bad11(s)
s.sub "%25", "%" # NOT OK s.sub "%25", "%" # $ Alert // NOT OK
s.sub! "%25", "%" # NOT OK s.sub! "%25", "%" # $ Alert // NOT OK
end end
def bad12(s) def bad12(s)
s.sub %q['], %q[] # NOT OK s.sub %q['], %q[] # $ Alert // NOT OK
s.sub! %q['], %q[] # NOT OK s.sub! %q['], %q[] # $ Alert // NOT OK
end end
def bad13(s) def bad13(s)
s.sub "'" + "", "" # NOT OK s.sub "'" + "", "" # $ Alert // NOT OK
s.sub! "'" + "", "" # NOT OK s.sub! "'" + "", "" # $ Alert // NOT OK
end end
def bad14(s) def bad14(s)
s.sub "'", "" + "" # NOT OK s.sub "'", "" + "" # $ Alert // NOT OK
s.sub! "'", "" + "" # NOT OK s.sub! "'", "" + "" # $ Alert // NOT OK
end end
def bad15(s) def bad15(s)
s.sub "'" + "", "" + "" # NOT OK s.sub "'" + "", "" + "" # $ Alert // NOT OK
s.sub! "'" + "", "" + "" # NOT OK s.sub! "'" + "", "" + "" # $ Alert // NOT OK
end end
def bad16(s) def bad16(s)
indirect = /'/ indirect = /'/
s.sub(indirect, "") # NOT OK s.sub(indirect, "") # $ Alert // NOT OK
s.sub!(indirect, "") # NOT OK s.sub!(indirect, "") # $ Alert // NOT OK
end end
def good1a(s) 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(')', '') # 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(']', '\\]') # $ Alert // NOT OK
s.sub('{', '\\{').sub('}', '\\}') # NOT OK s.sub('{', '\\{').sub('}', '\\}') # $ Alert // NOT OK
s = s.sub('[', '') # OK s = s.sub('[', '') # OK
s = s.sub(']', '') # OK s = s.sub(']', '') # OK
s.sub(/{/, '').sub(/}/, '') # OK s.sub(/{/, '').sub(/}/, '') # OK
s.sub(']', '').sub('[', '') # probably OK, but still flagged s.sub(']', '').sub('[', '') # $ Alert // probably OK, but still flagged
end end
def good13b(s1) def good13b(s1)
@@ -245,8 +245,8 @@ def newlines_a(a, b, c)
# motivation for whitelist # motivation for whitelist
`which emacs`.sub("\n", "") # OK `which emacs`.sub("\n", "") # OK
a.sub("\n", "").sub(b, c) # NOT OK a.sub("\n", "").sub(b, c) # $ Alert // NOT OK
a.sub(b, c).sub("\n", "") # NOT OK a.sub(b, c).sub("\n", "") # $ Alert // NOT OK
end end
def newlines_b(a, b, c) def newlines_b(a, b, c)
@@ -255,18 +255,18 @@ def newlines_b(a, b, c)
output.sub!("\n", "") # OK output.sub!("\n", "") # OK
d = a.dup d = a.dup
d.sub!("\n", "") # NOT OK d.sub!("\n", "") # $ Alert // NOT OK
d.sub!(b, c) d.sub!(b, c)
e = a.dup e = a.dup
d.sub!(b, c) d.sub!(b, c)
d.sub!("\n", "") # NOT OK d.sub!("\n", "") # $ Alert // NOT OK
end end
def bad_path_sanitizer(p1, p2) def bad_path_sanitizer(p1, p2)
# attempt at path sanitization # attempt at path sanitization
p1.sub! "/../", "" # NOT OK p1.sub! "/../", "" # $ Alert // NOT OK
p2.sub "/../", "" # NOT OK p2.sub "/../", "" # $ Alert // NOT OK
end end
def each_line_sanitizer(p1) 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 def read_from_params
init_logger init_logger
unsanitized = params[:foo] unsanitized = params[:foo] # $ Source
@logger.debug unsanitized # BAD: unsanitized user input @logger.debug unsanitized # $ Alert // BAD: unsanitized user input
@logger.error "input: " + unsanitized # BAD: unsanitized user input @logger.error "input: " + unsanitized # $ Alert // BAD: unsanitized user input
sanitized = unsanitized.gsub("\n", "") sanitized = unsanitized.gsub("\n", "")
@logger.fatal sanitized # GOOD: sanitized user input @logger.fatal sanitized # GOOD: sanitized user input
@@ -22,17 +22,17 @@ class UsersController < ApplicationController
unsanitized2 = unsanitized.sub("\n", "") unsanitized2 = unsanitized.sub("\n", "")
@logger.info do @logger.info do
unsanitized2 # BAD: partially sanitized user input unsanitized2 # $ Alert // BAD: partially sanitized user input
end end
@logger << "input: " + unsanitized2 # BAD: partially sanitized user input @logger << "input: " + unsanitized2 # $ Alert // BAD: partially sanitized user input
end end
def read_from_cookies def read_from_cookies
init_logger init_logger
unsanitized = cookies[:bar] unsanitized = cookies[:bar] # $ Source
@logger.add(Logger::INFO) { unsanitized } # BAD: unsanitized user input @logger.add(Logger::INFO) { unsanitized } # $ Alert // BAD: unsanitized user input
@logger.log(Logger::WARN) { "input: " + unsanitized } # BAD: unsanitized user input @logger.log(Logger::WARN) { "input: " + unsanitized } # $ Alert // BAD: unsanitized user input
end end
def html_sanitization def html_sanitization
@@ -46,7 +46,7 @@ class UsersController < ApplicationController
def inspect_sanitization def inspect_sanitization
init_logger 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 @logger.debug params[:foo].inspect # GOOD: sanitized user input
end end
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) # NOT GOOD; attack: "_" + "__".repeat(100)
# Adapted from marked (https://github.com/markedjs/marked), which is licensed # Adapted from marked (https://github.com/markedjs/marked), which is licensed
# under the MIT license; see file marked-LICENSE. # under the MIT license; see file marked-LICENSE.
bad1 = /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/ bad1 = /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/ # $ Alert
# GOOD # GOOD
# Adapted from marked (https://github.com/markedjs/marked), which is licensed # Adapted from marked (https://github.com/markedjs/marked), which is licensed
@@ -16,7 +16,7 @@ good2 = /(.*,)+.+/
# NOT GOOD; attack: " '" + "\\\\".repeat(100) # NOT GOOD; attack: " '" + "\\\\".repeat(100)
# Adapted from CodeMirror (https://github.com/codemirror/codemirror), # Adapted from CodeMirror (https://github.com/codemirror/codemirror),
# which is licensed under the MIT license; see file CodeMirror-LICENSE. # which is licensed under the MIT license; see file CodeMirror-LICENSE.
bad2 = /^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/ bad2 = /^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/ # $ Alert
# GOOD # GOOD
# Adapted from lulucms2 (https://github.com/yiifans/lulucms2). # 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*/ good3 = /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/
# NOT GOOD, variant of good3; attack: "a|\n:|\n" + "||\n".repeat(100) # 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) # NOT GOOD; attack: "/" + "\\/a".repeat(100)
# Adapted from ANodeBlog (https://github.com/gefangshuai/ANodeBlog), # Adapted from ANodeBlog (https://github.com/gefangshuai/ANodeBlog),
# which is licensed under the Apache License 2.0; see file ANodeBlog-LICENSE. # 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" # NOT GOOD; attack: "##".repeat(100) + "\na"
# Adapted from CodeMirror (https://github.com/codemirror/codemirror), # Adapted from CodeMirror (https://github.com/codemirror/codemirror),
# which is licensed under the MIT license; see file CodeMirror-LICENSE. # which is licensed under the MIT license; see file CodeMirror-LICENSE.
bad6 = /^([\s\[\{\(]|#.*)*$/ bad6 = /^([\s\[\{\(]|#.*)*$/ # $ Alert
# GOOD # GOOD
good4 = /(\r\n|\r|\n)+/ 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. # 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" # NOT GOOD; attack: "a" + "[]".repeat(100) + ".b\n"
# Adapted from Knockout (https://github.com/knockout/knockout), which is # Adapted from Knockout (https://github.com/knockout/knockout), which is
# licensed under the MIT license; see file knockout-LICENSE # 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 # GOOD
good6 = /(a|.)*/ good6 = /(a|.)*/
# Testing the NFA - only some of the below are detected. # Testing the NFA - only some of the below are detected.
bad7 = /^([a-z]+)+$/ bad7 = /^([a-z]+)+$/ # $ Alert
bad8 = /^([a-z]*)*$/ 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}))$/ 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])+$/ bad10 = /^(([a-z])+.)+[A-Z]([a-z])+$/ # $ Alert
# NOT GOOD; attack: "[" + "][".repeat(100) + "]!" # NOT GOOD; attack: "[" + "][".repeat(100) + "]!"
# Adapted from Prototype.js (https://github.com/prototypejs/prototype), which # Adapted from Prototype.js (https://github.com/prototypejs/prototype), which
# is licensed under the MIT license; see file Prototype.js-LICENSE. # 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) + '"' # NOT GOOD; attack: "'" + "\\a".repeat(100) + '"'
# Adapted from Prism (https://github.com/PrismJS/prism), which is licensed # Adapted from Prism (https://github.com/PrismJS/prism), which is licensed
# under the MIT license; see file Prism-LICENSE. # under the MIT license; see file Prism-LICENSE.
bad12 = /("|')(\\?.)*?\1/ bad12 = /("|')(\\?.)*?\1/ # $ Alert
# NOT GOOD # NOT GOOD
bad13 = /(b|a?b)*c/ bad13 = /(b|a?b)*c/ # $ Alert
# NOT GOOD # NOT GOOD
bad15 = /(a|aa?)*b/ bad15 = /(a|aa?)*b/ # $ Alert
# GOOD # GOOD
good7 = /(.|\n)*!/ good7 = /(.|\n)*!/
# NOT GOOD; attack: "\n".repeat(100) + "." # NOT GOOD; attack: "\n".repeat(100) + "."
bad16 = /(.|\n)*!/m bad16 = /(.|\n)*!/m # $ Alert
# GOOD # GOOD
good8 = /([\w.]+)*/ good8 = /([\w.]+)*/
# NOT GOOD # NOT GOOD
bad17 = Regexp.new '(a|aa?)*b' bad17 = Regexp.new '(a|aa?)*b' # $ Alert
# GOOD - not used as regexp # GOOD - not used as regexp
good9 = '(a|aa?)*b' good9 = '(a|aa?)*b'
# NOT GOOD # 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 # GOOD - there is no witness in the end that could cause the regexp to not match
good10 = /([^"']+)*/ good10 = /([^"']+)*/
# NOT GOOD # NOT GOOD
bad20 = /((.|[^a])*)"/ bad20 = /((.|[^a])*)"/ # $ Alert
# GOOD # GOOD
good10 = /((a|[^a])*)"/ good10 = /((a|[^a])*)"/
# NOT GOOD # NOT GOOD
bad21 = /((b|[^a])*)"/ bad21 = /((b|[^a])*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad22 = /((G|[^a])*)"/ bad22 = /((G|[^a])*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad23 = /(([0-9]|[^a])*)"/ bad23 = /(([0-9]|[^a])*)"/ # $ Alert
# BAD - missing result # BAD - missing result
bad24 = /(?:=(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"))?/ 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"\\])*)"/ bad26 = /"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*)"/
# NOT GOOD # NOT GOOD
bad27 = /(([a-z]|[d-h])*)"/ bad27 = /(([a-z]|[d-h])*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad27 = /(([^a-z]|[^0-9])*)"/ bad27 = /(([^a-z]|[^0-9])*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad28 = /((\d|[0-9])*)"/ bad28 = /((\d|[0-9])*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad29 = /((\s|\s)*)"/ bad29 = /((\s|\s)*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad30 = /((\w|G)*)"/ bad30 = /((\w|G)*)"/ # $ Alert
# GOOD # GOOD
good11 = /((\s|\d)*)"/ good11 = /((\s|\d)*)"/
# NOT GOOD # NOT GOOD
bad31 = /((\d|\w)*)"/ bad31 = /((\d|\w)*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad32 = /((\d|5)*)"/ bad32 = /((\d|5)*)"/ # $ Alert
# BAD - \f is not handled correctly # BAD - \f is not handled correctly
bad33 = /((\s|[\f])*)"/ bad33 = /((\s|[\f])*)"/ # $ Alert
# BAD - \v is not handled correctly # BAD - \v is not handled correctly
bad34 = /((\s|[\v]|\\v)*)"/ bad34 = /((\s|[\v]|\\v)*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad35 = /((\f|[\f])*)"/ bad35 = /((\f|[\f])*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad36 = /((\W|\D)*)"/ bad36 = /((\W|\D)*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad37 = /((\S|\w)*)"/ bad37 = /((\S|\w)*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad38 = /((\S|[\w])*)"/ bad38 = /((\S|[\w])*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad39 = /((1s|[\da-z])*)"/ bad39 = /((1s|[\da-z])*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad40 = /((0|[\d])*)"/ bad40 = /((0|[\d])*)"/ # $ Alert
# NOT GOOD # NOT GOOD
bad41 = /(([\d]+)*)"/ bad41 = /(([\d]+)*)"/ # $ Alert
# GOOD - there is no witness in the end that could cause the regexp to not match # GOOD - there is no witness in the end that could cause the regexp to not match
good12 = /(\d+(X\d+)?)+/ good12 = /(\d+(X\d+)?)+/
@@ -182,49 +182,49 @@ good13 = /([0-9]+(X[0-9]*)?)*/
good15 = /^([^>]+)*(>|$)/ good15 = /^([^>]+)*(>|$)/
# NOT GOOD # NOT GOOD
bad43 = /^([^>a]+)*(>|$)/ bad43 = /^([^>a]+)*(>|$)/ # $ Alert
# NOT GOOD # NOT GOOD
bad44 = /(\n\s*)+$/ bad44 = /(\n\s*)+$/ # $ Alert
# NOT GOOD # NOT GOOD
bad45 = /^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/ bad45 = /^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/ # $ Alert
# NOT GOOD # 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 # NOT GOOD
bad47 = /(a+|b+|c+)*c/ bad47 = /(a+|b+|c+)*c/ # $ Alert
# NOT GOOD # NOT GOOD
bad48 = /(((a+a?)*)+b+)/ bad48 = /(((a+a?)*)+b+)/ # $ Alert
# NOT GOOD # NOT GOOD
bad49 = /(a+)+bbbb/ bad49 = /(a+)+bbbb/ # $ Alert
# GOOD # GOOD
good16 = /(a+)+aaaaa*a+/ good16 = /(a+)+aaaaa*a+/
# NOT GOOD # NOT GOOD
bad50 = /(a+)+aaaaa$/ bad50 = /(a+)+aaaaa$/ # $ Alert
# GOOD # GOOD
good17 = /(\n+)+\n\n/ good17 = /(\n+)+\n\n/
# NOT GOOD # NOT GOOD
bad51 = /(\n+)+\n\n$/ bad51 = /(\n+)+\n\n$/ # $ Alert
# NOT GOOD # NOT GOOD
bad52 = /([^X]+)*$/ bad52 = /([^X]+)*$/ # $ Alert
# NOT GOOD # NOT GOOD
bad53 = /(([^X]b)+)*$/ bad53 = /(([^X]b)+)*$/ # $ Alert
# GOOD # GOOD
good18 = /(([^X]b)+)*($|[^X]b)/ good18 = /(([^X]b)+)*($|[^X]b)/
# NOT GOOD # NOT GOOD
bad54 = /(([^X]b)+)*($|[^X]c)/ bad54 = /(([^X]b)+)*($|[^X]c)/ # $ Alert
# GOOD # GOOD
good20 = /((ab)+)*ababab/ good20 = /((ab)+)*ababab/
@@ -236,13 +236,13 @@ good21 = /((ab)+)*abab(ab)*(ab)+/
good22 = /((ab)+)*/ good22 = /((ab)+)*/
# NOT GOOD # NOT GOOD
bad55 = /((ab)+)*$/ bad55 = /((ab)+)*$/ # $ Alert
# GOOD # GOOD
good23 = /((ab)+)*[a1][b1][a2][b2][a3][b3]/ good23 = /((ab)+)*[a1][b1][a2][b2][a3][b3]/
# NOT GOOD # NOT GOOD
bad56 = /([\n\s]+)*(.)/ bad56 = /([\n\s]+)*(.)/ # $ Alert
# GOOD - any witness passes through the accept state. # GOOD - any witness passes through the accept state.
good24 = /(A*A*X)*/ good24 = /(A*A*X)*/
@@ -251,13 +251,13 @@ good24 = /(A*A*X)*/
good26 = /([^\\\]]+)*/ good26 = /([^\\\]]+)*/
# NOT GOOD # 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 # NOT GOOD
bad60 = /(.thisisagoddamnlongstringforstresstestingthequery|\sthisisagoddamnlongstringforstresstestingthequery)*-/ bad60 = /(.thisisagoddamnlongstringforstresstestingthequery|\sthisisagoddamnlongstringforstresstestingthequery)*-/ # $ Alert
# NOT GOOD # NOT GOOD
bad61 = /(thisisagoddamnlongstringforstresstestingthequery|this\w+query)*-/ bad61 = /(thisisagoddamnlongstringforstresstestingthequery|this\w+query)*-/ # $ Alert
# GOOD # GOOD
good27 = /(thisisagoddamnlongstringforstresstestingthequery|imanotherbutunrelatedstringcomparedtotheotherstring)*-/ good27 = /(thisisagoddamnlongstringforstresstestingthequery|imanotherbutunrelatedstringcomparedtotheotherstring)*-/
@@ -269,58 +269,58 @@ good27 = /(thisisagoddamnlongstringforstresstestingthequery|imanotherbutunrelate
#good29 = /foo((\uDC66|\uDC67)|(\uDC68|\uDC69))*foo/ #good29 = /foo((\uDC66|\uDC67)|(\uDC68|\uDC69))*foo/
# NOT GOOD (but cannot currently construct a prefix) # 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) # 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 # GOOD
good30 = /(a+)*[\S\s][\S\s][\S\s]?/ 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}`). # 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). # 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 # GOOD
good33 = /(a+)*([\S\s]*|X)$/ good33 = /(a+)*([\S\s]*|X)$/
# NOT GOOD # 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. # 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 # GOOD
good35 = /((;|^)a+)+$/ good35 = /((;|^)a+)+$/
# NOT GOOD (a good prefix test) # 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 # NOT GOOD
bad66 = /^ab(c+)+$/ bad66 = /^ab(c+)+$/ # $ Alert
# NOT GOOD # NOT GOOD
bad67 = /(\d(\s+)*){20}/ bad67 = /(\d(\s+)*){20}/ # $ Alert
# GOOD - but we spuriously conclude that a rejecting suffix exists. # 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. # GOOD - but we spuriously conclude that a rejecting suffix exists.
good37 = /^((x([^Y]+)?)*(Y|$))/ good37 = /^((x([^Y]+)?)*(Y|$))/ # $ Alert
# NOT GOOD # NOT GOOD
bad68 = /(a*)+b/ bad68 = /(a*)+b/ # $ Alert
# NOT GOOD # NOT GOOD
bad69 = /foo([\w-]*)+bar/ bad69 = /foo([\w-]*)+bar/ # $ Alert
# NOT GOOD # NOT GOOD
bad70 = /((ab)*)+c/ bad70 = /((ab)*)+c/ # $ Alert
# NOT GOOD # NOT GOOD
bad71 = /(a?a?)*b/ bad71 = /(a?a?)*b/ # $ Alert
# GOOD # GOOD
good38 = /(a?)*b/ good38 = /(a?)*b/
@@ -329,54 +329,54 @@ good38 = /(a?)*b/
bad72 = /(c?a?)*b/ bad72 = /(c?a?)*b/
# NOT GOOD # NOT GOOD
bad73 = /(?:a|a?)+b/ bad73 = /(?:a|a?)+b/ # $ Alert
# NOT GOOD - but not detected. # NOT GOOD - but not detected.
bad74 = /(a?b?)*$/ bad74 = /(a?b?)*$/
# NOT GOOD # 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 # NOT GOOD - but not detected
bad77 = /^((a)+\w)+$/ bad77 = /^((a)+\w)+$/ # $ Alert
# NOT GOOD # NOT GOOD
bad78 = /^(b+.)+$/ bad78 = /^(b+.)+$/ # $ Alert
# GOOD # GOOD
good39 = /a*b/ good39 = /a*b/
# All 4 bad combinations of nested * and + # All 4 bad combinations of nested * and +
bad79 = /(a*)*b/ bad79 = /(a*)*b/ # $ Alert
bad80 = /(a+)*b/ bad80 = /(a+)*b/ # $ Alert
bad81 = /(a*)+b/ bad81 = /(a*)+b/ # $ Alert
bad82 = /(a+)+b/ bad82 = /(a+)+b/ # $ Alert
# GOOD # GOOD
good40 = /(a|b)+/ good40 = /(a|b)+/
good41 = /(?:[\s;,"'<>(){}|\[\]@=+*]|:(?![\/\\]))+/ good41 = /(?:[\s;,"'<>(){}|\[\]@=+*]|:(?![\/\\]))+/
# NOT GOOD # NOT GOOD
bad83 = /^((?:a{|-)|\w\{)+X$/ bad83 = /^((?:a{|-)|\w\{)+X$/ # $ Alert
bad84 = /^((?:a{0|-)|\w\{\d)+X$/ bad84 = /^((?:a{0|-)|\w\{\d)+X$/ # $ Alert
bad85 = /^((?:a{0,|-)|\w\{\d,)+X$/ bad85 = /^((?:a{0,|-)|\w\{\d,)+X$/ # $ Alert
bad86 = /^((?:a{0,2|-)|\w\{\d,\d)+X$/ bad86 = /^((?:a{0,2|-)|\w\{\d,\d)+X$/ # $ Alert
# NOT GOOD # NOT GOOD
bad87 = /^((?:a{0,2}|-)|\w\{\d,\d\})+X$/ bad87 = /^((?:a{0,2}|-)|\w\{\d,\d\})+X$/
# NOT GOOD # NOT GOOD
bad88 = /^X(\u0061|a)*Y$/ bad88 = /^X(\u0061|a)*Y$/ # $ Alert
# GOOD # GOOD
good43 = /^X(\u0061|b)+Y$/ good43 = /^X(\u0061|b)+Y$/
# NOT GOOD # NOT GOOD
bad88 = /X([[:digit:]]|\d)+Y/ bad88 = /X([[:digit:]]|\d)+Y/ # $ Alert
# NOT GOOD # NOT GOOD
bad89 = /\G(a|\w)*$/ bad89 = /\G(a|\w)*$/ # $ Alert
bad90 = /\b(a|\w)*$/ bad90 = /\b(a|\w)*$/ # $ Alert
# NOT GOOD; attack: "0".repeat(30) + "!" # NOT GOOD; attack: "0".repeat(30) + "!"
# Adapated from addressable (https://github.com/sporkmonger/addressable) # Adapated from addressable (https://github.com/sporkmonger/addressable)
@@ -387,5 +387,5 @@ module Bad91
var_char_class = ALPHA + DIGIT + '_' var_char_class = ALPHA + DIGIT + '_'
var_char = "(?:(?:[#{var_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)" var_char = "(?:(?:[#{var_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)"
var = "(?:#{var_char}(?:\\.?#{var_char})*)" var = "(?:#{var_char}(?:\\.?#{var_char})*)"
bad91 = /^#{var}$/ bad91 = /^#{var}$/ # $ Alert
end 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 class FooController < ActionController::Base
def some_request_handler def some_request_handler
# A source for the data-flow query (i.e. a remote flow source) # A source for the data-flow query (i.e. a remote flow source)
name = params[:name] name = params[:name] # $ Source
# A vulnerable regex # A vulnerable regex
regex = /^\s+|\s+$/ regex = /^\s+|\s+$/
# Various sinks that match the source against the regex # Various sinks that match the source against the regex
name =~ regex # NOT GOOD name =~ regex # $ Alert // NOT GOOD
name !~ regex # NOT GOOD name !~ regex # $ Alert // NOT GOOD
name[regex] # NOT GOOD name[regex] # $ Alert // NOT GOOD
name.gsub regex, '' # NOT GOOD name.gsub regex, '' # $ Alert // NOT GOOD
name.index regex # NOT GOOD name.index regex # $ Alert // NOT GOOD
name.match regex # NOT GOOD name.match regex # $ Alert // NOT GOOD
name.match? regex # NOT GOOD name.match? regex # $ Alert // NOT GOOD
name.partition regex # NOT GOOD name.partition regex # $ Alert // NOT GOOD
name.rindex regex # NOT GOOD name.rindex regex # $ Alert // NOT GOOD
name.rpartition regex # NOT GOOD name.rpartition regex # $ Alert // NOT GOOD
name.scan regex # NOT GOOD name.scan regex # $ Alert // NOT GOOD
name.split regex # NOT GOOD name.split regex # $ Alert // NOT GOOD
name.sub regex, '' # NOT GOOD name.sub regex, '' # $ Alert // NOT GOOD
regex.match name # NOT GOOD regex.match name # $ Alert // NOT GOOD
regex.match? name # NOT GOOD regex.match? name # $ Alert // NOT GOOD
# Destructive variants # Destructive variants
a = params[:b] a = params[:b] # $ Source
a.gsub! regex, '' # NOT GOOD a.gsub! regex, '' # $ Alert // NOT GOOD
b = params[:a] b = params[:a] # $ Source
b.slice! regex # NOT GOOD b.slice! regex # $ Alert // NOT GOOD
c = params[:c] c = params[:c] # $ Source
c.sub! regex, '' # NOT GOOD c.sub! regex, '' # $ Alert // NOT GOOD
# GOOD - guarded by a string length check # GOOD - guarded by a string length check
if name.length < 1024 if name.length < 1024
@@ -39,19 +39,19 @@ class FooController < ActionController::Base
# GOOD - regex does not suffer from polynomial backtracking (regression test) # GOOD - regex does not suffer from polynomial backtracking (regression test)
params[:foo] =~ /\A[bc].*\Z/ params[:foo] =~ /\A[bc].*\Z/
case name # NOT GOOD case name # $ Sink // NOT GOOD
when regex when regex
puts "foo" puts "foo"
end end # $ Alert
case name # NOT GOOD case name # $ Sink // NOT GOOD
in /^\s+|\s+$/ then in /^\s+|\s+$/ then
puts "foo" puts "foo"
end end # $ Alert
end end
def some_other_request_handle def some_other_request_handle
name = params[:name] # source name = params[:name] # $ Source // source
indirect_use_of_reg /^\s+|\s+$/, name indirect_use_of_reg /^\s+|\s+$/, name
@@ -59,22 +59,22 @@ class FooController < ActionController::Base
end end
def indirect_use_of_reg (reg, input) def indirect_use_of_reg (reg, input)
input.gsub reg, '' # NOT GOOD input.gsub reg, '' # $ Alert // NOT GOOD
end end
def as_string_indirect (reg_as_string, input) def as_string_indirect (reg_as_string, input)
input.match? reg_as_string, '' # NOT GOOD input.match? reg_as_string, '' # $ Alert // NOT GOOD
end end
def re_compile_indirect def re_compile_indirect
name = params[:name] # source name = params[:name] # $ Source // source
reg = Regexp.new '^\s+|\s+$' reg = Regexp.new '^\s+|\s+$'
re_compile_indirect_2 reg, name re_compile_indirect_2 reg, name
end end
def re_compile_indirect_2 (reg, input) def re_compile_indirect_2 (reg, input)
input.gsub reg, '' # NOT GOOD input.gsub reg, '' # $ Alert // NOT GOOD
end end
# See https://github.com/dependabot/dependabot-core/blob/37dc1767fde9b7184020763f4d0c1434f93d11d6/python/lib/dependabot/python/requirement_parser.rb#L6-L25 # 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+)+/ MARKER_EXPR = /(#{MARKER_EXPR_ONE}|\(\s*|\s*\)|\s+and\s+|\s+or\s+)+/
def use_marker_expr def use_marker_expr
name = params[:name] # source name = params[:name] # $ Source // source
name =~ MARKER_EXPR name =~ MARKER_EXPR # $ Alert
end end
end end

View File

@@ -1,13 +1,13 @@
module Foo module Foo
def bar(x) def bar(x) # $ Source
# Run the /a+$/ regex on the input x. # Run the /a+$/ regex on the input x.
match = x.match(/a+$/) match = x.match(/a+$/) # $ Alert
end end
protected protected
def baz(x) def baz(x) # $ Source
match = x.match(/a+$/) 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 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 class FooController < ActionController::Base
# BAD # BAD
def route0 def route0
name = params[:name] name = params[:name] # $ Source
regex = /#{name}/ regex = /#{name}/ # $ Alert
end end
# BAD # BAD
def route1 def route1
name = params[:name] name = params[:name] # $ Source
regex = /foo#{name}bar/ regex = /foo#{name}bar/ # $ Alert
end end
# BAD # BAD
def route2 def route2
name = params[:name] name = params[:name] # $ Source
regex = Regexp.new(name) regex = Regexp.new(name) # $ Alert
end end
# BAD # BAD
def route3 def route3
name = params[:name] name = params[:name] # $ Source
regex = Regexp.new("@" + name) regex = Regexp.new("@" + name) # $ Alert
end end
# GOOD - string is compared against a constant string # GOOD - string is compared against a constant string
@@ -51,7 +51,7 @@ class FooController < ActionController::Base
# BAD # BAD
def route8 def route8
name = params[:name] name = params[:name] # $ Source
regex = Regexp.compile("@" + name) regex = Regexp.compile("@" + name) # $ Alert
end end
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 class UsersController < ActionController::Base
def show def show
printf(params[:format], arg) # BAD printf(params[:format], arg) # $ Alert // BAD
Kernel.printf(params[:format], arg) # BAD Kernel.printf(params[:format], arg) # $ Alert // BAD
printf(params[:format]) # GOOD printf(params[:format]) # GOOD
Kernel.printf(params[:format]) # GOOD Kernel.printf(params[:format]) # GOOD
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) # BAD Kernel.printf(IO.new(1), params[:format], arg) # $ Alert // BAD
printf("%s", params[:format]) # GOOD printf("%s", params[:format]) # GOOD
Kernel.printf("%s", params[:format]) # GOOD Kernel.printf("%s", params[:format]) # GOOD
fmt = "%s" fmt = "%s"
printf(fmt, params[:format]) # GOOD printf(fmt, params[:format]) # GOOD
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]) # GOOD [FALSE POSITIVE] Kernel.printf(IO.new(1), params[:format]) # $ Alert // GOOD [FALSE POSITIVE]
str1 = Kernel.sprintf(params[:format], arg) # BAD str1 = Kernel.sprintf(params[:format], arg) # $ Alert // BAD
str2 = sprintf(params[:format], arg) # BAD str2 = sprintf(params[:format], arg) # $ Alert // BAD
str1 = Kernel.sprintf(params[:format]) # GOOD str1 = Kernel.sprintf(params[:format]) # GOOD
str2 = sprintf(params[:format]) # GOOD str2 = sprintf(params[:format]) # GOOD
stdout = IO.new 1 stdout = IO.new 1
stdout.printf(params[:format], arg) # BAD stdout.printf(params[:format], arg) # $ Alert // BAD
stdout.printf(params[:format]) # GOOD stdout.printf(params[:format]) # GOOD
# Taint via string concatenation # 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 # Taint via string interpolation
printf("A log message: #{params[:format]}", arg) # BAD printf("A log message: #{params[:format]}", arg) # $ Alert // BAD
# Using String# # 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 # String# with an array
"A log message #{params[:format]} %08x" % ["foo"] # BAD "A log message #{params[:format]} %08x" % ["foo"] # $ Alert // BAD
end 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 def show
something_that_might_fail() something_that_might_fail()
rescue => e rescue => e
render body: e.backtrace, content_type: "text/plain" render body: e.backtrace, content_type: "text/plain" # $ Alert
end end
def show2 def show2
bt = caller() bt = caller() # $ Source
render body: bt, content_type: "text/plain" render body: bt, content_type: "text/plain" # $ Alert
end end
def show3 def show3
not_a_method() not_a_method()
rescue NoMethodError => e rescue NoMethodError => e
render body: e.backtrace, content_type: "text/plain" render body: e.backtrace, content_type: "text/plain" # $ Alert
end end
end end

View File

@@ -3,31 +3,31 @@ require "excon"
def method1 def method1
# BAD # BAD
Excon.defaults[:ssl_verify_peer] = false Excon.defaults[:ssl_verify_peer] = false
Excon.get("http://example.com/") Excon.get("http://example.com/") # $ Alert
end end
def method2 def method2
# BAD # BAD
Excon.ssl_verify_peer = false Excon.ssl_verify_peer = false
Excon.get("http://example.com/") Excon.get("http://example.com/") # $ Alert
end end
def method3(secure) def method3(secure)
# BAD # BAD
Excon.defaults[:ssl_verify_peer] = (secure ? true : false) Excon.defaults[:ssl_verify_peer] = (secure ? true : false)
Excon.get("http://example.com/") Excon.get("http://example.com/") # $ Alert
end end
def method4 def method4
# BAD # BAD
conn = Excon::Connection.new("http://example.com/", ssl_verify_peer: false) conn = Excon::Connection.new("http://example.com/", ssl_verify_peer: false)
conn.get conn.get # $ Alert
end end
def method5 def method5
# BAD # BAD
Excon.ssl_verify_peer = true 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 end
def method6 def method6

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,24 +1,24 @@
require "open-uri" require "open-uri"
# BAD # 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 # 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 # BAD
options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE } options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }
Kernel.open("https://example.com", options) Kernel.open("https://example.com", options) # $ Alert
# BAD # 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 # 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 # BAD
options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE } options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }
URI.parse("https://example.com").open(options) URI.parse("https://example.com").open(options) # $ Alert
# GOOD # GOOD
Kernel.open("https://example.com") Kernel.open("https://example.com")

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 # BAD
resource = RestClient::Resource.new("https://example.com", verify_ssl: OpenSSL::SSL::VERIFY_NONE) resource = RestClient::Resource.new("https://example.com", verify_ssl: OpenSSL::SSL::VERIFY_NONE)
response = resource.get response = resource.get # $ Alert
# BAD # BAD
resource = RestClient::Resource.new("https://example.com", { verify_ssl: OpenSSL::SSL::VERIFY_NONE }) resource = RestClient::Resource.new("https://example.com", { verify_ssl: OpenSSL::SSL::VERIFY_NONE })
response = resource.get response = resource.get # $ Alert
# BAD # BAD
options = { verify_ssl: OpenSSL::SSL::VERIFY_NONE } options = { verify_ssl: OpenSSL::SSL::VERIFY_NONE }
resource = RestClient::Resource.new("https://example.com", options) resource = RestClient::Resource.new("https://example.com", options)
response = resource.get response = resource.get # $ Alert
# BAD # BAD
value = OpenSSL::SSL::VERIFY_NONE value = OpenSSL::SSL::VERIFY_NONE
resource = RestClient::Resource.new("https://example.com", verify_ssl: value) resource = RestClient::Resource.new("https://example.com", verify_ssl: value)
response = resource.get response = resource.get # $ Alert
# GOOD # GOOD
RestClient.get("https://example.com") RestClient.get("https://example.com")

View File

@@ -1,11 +1,11 @@
require "typhoeus" require "typhoeus"
# BAD # BAD
Typhoeus.get("https://www.example.com", ssl_verifypeer: false) Typhoeus.get("https://www.example.com", ssl_verifypeer: false) # $ Alert
# BAD # BAD
post_options = { body: "some data", ssl_verifypeer: false } 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 # 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

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