Compare commits

..

1 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
838d06c53f Fix changelog copy errors in change-notes and CHANGELOG.md files (codeql-cli-2.25.6) 2026-06-11 22:45:33 +02:00
153 changed files with 994 additions and 1089 deletions

View File

@@ -2,7 +2,7 @@
### Minor Analysis Improvements
* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, include regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a sha1 or sha256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used.
* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, including regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a SHA-1 or SHA-256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used.
## 0.4.36

View File

@@ -2,4 +2,4 @@
### Minor Analysis Improvements
* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, include regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a sha1 or sha256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used.
* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, including regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a SHA-1 or SHA-256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used.

View File

@@ -15,7 +15,7 @@
### Bug Fixes
* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on in minor point, added one more listed resource and added one more recommendation for things to check.
* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on a minor point, added one more listed resource and added one more recommendation for things to check.
## 0.6.28

View File

@@ -15,4 +15,4 @@
### Bug Fixes
* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on in minor point, added one more listed resource and added one more recommendation for things to check.
* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on a minor point, added one more listed resource and added one more recommendation for things to check.

View File

@@ -1,4 +0,0 @@
---
category: feature
---
* Added `UseMemoDirective` and `UseNoMemoDirective` classes to model the React compiler directives `"use memo"` and `"use no memo"`.

View File

@@ -435,32 +435,6 @@ module Directive {
UseClientDirective() { this.getDirectiveText() = "use client" }
}
/**
* A `use memo` directive.
*
* Example:
*
* ```
* "use memo";
* ```
*/
class UseMemoDirective extends KnownDirective {
UseMemoDirective() { this.getDirectiveText() = "use memo" }
}
/**
* A `use no memo` directive.
*
* Example:
*
* ```
* "use no memo";
* ```
*/
class UseNoMemoDirective extends KnownDirective {
UseNoMemoDirective() { this.getDirectiveText() = "use no memo" }
}
/**
* A `use cache` directive.
*

View File

@@ -3,18 +3,14 @@
| tst.js:3:1:3:9 | 'bundle'; | bundle |
| tst.js:4:1:4:13 | 'use server'; | use server |
| tst.js:5:1:5:13 | 'use client'; | use client |
| tst.js:6:1:6:11 | 'use memo'; | use memo |
| tst.js:7:1:7:14 | 'use no memo'; | use no memo |
| tst.js:8:1:8:12 | 'use cache'; | use cache |
| tst.js:9:1:9:20 | 'use cache: remote'; | use cache: remote |
| tst.js:10:1:10:21 | 'use ca ... ivate'; | use cache: private |
| tst.js:19:3:19:12 | 'use asm'; | use asm |
| tst.js:20:3:20:11 | 'bundle'; | bundle |
| tst.js:21:3:21:15 | 'use server'; | use server |
| tst.js:22:3:22:15 | 'use client'; | use client |
| tst.js:23:3:23:13 | 'use memo'; | use memo |
| tst.js:24:3:24:16 | 'use no memo'; | use no memo |
| tst.js:25:3:25:14 | 'use cache'; | use cache |
| tst.js:26:3:26:22 | 'use cache: remote'; | use cache: remote |
| tst.js:27:3:27:23 | 'use ca ... ivate'; | use cache: private |
| tst.js:34:5:34:17 | 'use strict'; | use strict |
| tst.js:6:1:6:12 | 'use cache'; | use cache |
| tst.js:7:1:7:20 | 'use cache: remote'; | use cache: remote |
| tst.js:8:1:8:21 | 'use ca ... ivate'; | use cache: private |
| tst.js:17:3:17:12 | 'use asm'; | use asm |
| tst.js:18:3:18:11 | 'bundle'; | bundle |
| tst.js:19:3:19:15 | 'use server'; | use server |
| tst.js:20:3:20:15 | 'use client'; | use client |
| tst.js:21:3:21:14 | 'use cache'; | use cache |
| tst.js:22:3:22:22 | 'use cache: remote'; | use cache: remote |
| tst.js:23:3:23:23 | 'use ca ... ivate'; | use cache: private |
| tst.js:30:5:30:17 | 'use strict'; | use strict |

View File

@@ -3,8 +3,6 @@
'bundle';// and this
'use server';
'use client';
'use memo';
'use no memo';
'use cache';
'use cache: remote';
'use cache: private';
@@ -20,8 +18,6 @@ function f() {
'bundle';
'use server';
'use client';
'use memo';
'use no memo';
'use cache';
'use cache: remote';
'use cache: private';

View File

@@ -2,7 +2,7 @@
### Minor Analysis Improvements
* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and less fewer positive results after these changes.
* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and fewer false positive results after these changes.
## 7.1.1

View File

@@ -2,4 +2,4 @@
### Minor Analysis Improvements
* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and less fewer positive results after these changes.
* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and fewer false positive results after these changes.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,13 @@
module Foo
def bar(x) # $ Source
def bar(x)
# Run the /a+$/ regex on the input x.
match = x.match(/a+$/) # $ Alert
match = x.match(/a+$/)
end
protected
def baz(x) # $ Source
match = x.match(/a+$/) # $ Alert
def baz(x)
match = x.match(/a+$/)
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
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$/)
end
end
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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