├── README.md ├── codeql ├── __init__.py ├── __init__.pyc ├── __pycache__ │ ├── BQRS.cpython-39.pyc │ ├── Database.cpython-39.pyc │ ├── Query.cpython-39.pyc │ ├── __init__.cpython-39.pyc │ └── common.cpython-39.pyc ├── bqrs.py ├── common.py ├── database.py └── query.py ├── config └── config.ini ├── main.py ├── out ├── log │ └── 2022-11-21.log └── result │ └── 2022-11-21_1669002601.csv ├── plugins ├── java │ ├── BeanShellInjection.ql │ ├── ClientSuppliedIpUsedInSecurityCheck.ql │ ├── CredentialsInPropertiesFile.ql │ ├── DisabledRevocationChecking.ql │ ├── EJBMain.ql │ ├── ExecTainted.ql │ ├── FilePathInjection.ql │ ├── HardcodedJwtKey.ql │ ├── HashWithoutSalt.ql │ ├── IgnoredHostnameVerification.ql │ ├── IncorrectURLVerification.ql │ ├── InsecureDexLoading.ql │ ├── InsecureDirectoryConfig.ql │ ├── InsecureLdapAuth.ql │ ├── InsecureLdapEndpoint.ql │ ├── InsecureRmiJmxEnvironmentConfiguration.ql │ ├── InsecureSpringActuatorConfig.ql │ ├── InsecureTomcatConfig.ql │ ├── InsecureWebResourceResponse.ql │ ├── InsufficientKeySize.ql │ ├── JShellInjection.ql │ ├── JakartaExpressionInjection.ql │ ├── JsonpInjection.ql │ ├── JxBrowserWithoutCertValidation.ql │ ├── JythonInjection.ql │ ├── LocalThreadResourceAbuse.ql │ ├── Log4jJndiInjection.ql │ ├── MyBatisAnnotationSqlInjection.ql │ ├── MyBatisMapperXmlSqlInjection.ql │ ├── NFEAndroidDoS.ql │ ├── OpenStream.ql │ ├── PasswordInConfigurationFile.ql │ ├── PermissiveDotRegex.ql │ ├── PossibleTimingAttackAgainstSignature.ql │ ├── RegexInjection.ql │ ├── ScriptInjection.ql │ ├── SensitiveAndroidFileLeak.ql │ ├── SensitiveCookieNotHttpOnly.ql │ ├── SensitiveGetQuery.ql │ ├── SpringBootActuators.ql │ ├── SpringImplicitViewManipulation.ql │ ├── SpringUrlRedirect.ql │ ├── SpringViewManipulation.ql │ ├── ThreadResourceAbuse.ql │ ├── TimingAttackAgainstHeader.ql │ ├── TimingAttackAgainstSignature.ql │ ├── UncaughtServletException.ql │ ├── UnsafeDeserializationRmi.ql │ ├── UnsafeReflection.ql │ ├── UnsafeSpringExporterInConfigurationClass.ql │ ├── UnsafeSpringExporterInXMLConfiguration.ql │ ├── UnsafeTlsVersion.ql │ ├── UnsafeUrlForward.ql │ ├── UnvalidatedCors.ql │ ├── WebComponentMain.ql │ ├── XQueryInjection.ql │ ├── XXE.ql │ ├── XXELocal.ql │ ├── devMode.ql │ └── 说明.txt ├── java_ext │ ├── Fastjson.ql │ ├── SQLI1.ql │ ├── SSRF1.ql │ ├── URLRedirect.ql │ ├── Unserialize.ql │ ├── XSS1.ql │ └── XStream.ql ├── javascript │ ├── ExceptionXss.ql │ ├── ReflectedXss.ql │ ├── StoredXss.ql │ ├── UnsafeHtmlConstruction.ql │ ├── UnsafeJQueryPlugin.ql │ ├── Xss.ql │ └── XssThroughDom.ql └── test │ ├── test.ql │ └── test2.ql ├── scan ├── JavaScan.py ├── JavaScan.pyc ├── JavascriptScan.py ├── Scan.py ├── __init__.py ├── __init__.pyc └── __pycache__ │ ├── JavaScan.cpython-39.pyc │ ├── JavascriptScan.cpython-39.pyc │ ├── Scan.cpython-39.pyc │ └── __init__.cpython-39.pyc └── utils ├── __init__.py ├── __init__.pyc ├── __pycache__ ├── __init__.cpython-39.pyc ├── check.cpython-39.pyc ├── color_print.cpython-39.pyc ├── functions.cpython-39.pyc ├── log.cpython-39.pyc └── option.cpython-39.pyc ├── check.py ├── color_print.py ├── color_print.pyc ├── functions.py ├── log.py ├── log.pyc ├── option.py └── option.pyc /README.md: -------------------------------------------------------------------------------- 1 | # codeql 2 | codeqlpy 3 | 4 | 使用说明: 5 | https://mp.weixin.qq.com/s/5UmBkqCsBt64WY2JllIVSw 6 | 7 | ## 免责声明: 8 | 9 | 本篇文章仅用于技术交流学习和研究的目的,严禁使用文章中的技术用于非法目的和破坏,否则造成一切后果与发表本文章的作者无关。 10 | 11 | ### 中文版本: 12 | 13 | 本免责声明旨在明确指出,本文仅为技术交流、学习和研究之用,不得将文章中的技术用于任何非法目的或破坏行为。发表本文章的作者对于任何非法使用技术或对他人或系统造成的损害概不负责。 14 | 15 | 阅读和参考本文章时,您必须明确并承诺,不会利用文章中提供的技术来实施非法活动、侵犯他人的权益或对系统进行攻击。任何使用本文中的技术所导致的任何意外、损失或损害,包括但不限于数据损失、财产损失、法律责任等问题,都与发表本文章的作者无关。 16 | 17 | 本文提供的技术信息仅供学习和参考之用,不构成任何形式的担保或保证。发表本文章的作者不对技术的准确性、有效性或适用性做任何声明或保证。 18 | 19 | ### 英文版本: 20 | 21 | This disclaimer is intended to clearly state that this article is solely for the purpose of technical exchange, learning, and research, and the use of the techniques mentioned in the article for any illegal purposes or destructive actions is strictly prohibited. The author of this article shall not be held responsible for any consequences resulting from the misuse of the techniques mentioned. 22 | 23 | By reading and referring to this article, you must acknowledge and commit that you will not exploit the techniques provided in the article for any illegal activities, infringement of rights of others, or attacks on systems. The author of this article bears no responsibility for any accidents, losses, or damages caused by the use of the techniques mentioned in this article, including but not limited to data loss, property damage, legal liabilities, etc. 24 | 25 | The technical information provided in this article is for learning and reference purposes only and does not constitute any form of warranty or guarantee. The author of this article makes no representations or warranties regarding the accuracy, effectiveness, or applicability of the techniques mentioned. 26 | -------------------------------------------------------------------------------- /codeql/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | CodeQL for Python. 5 | """ 6 | 7 | # Imports 8 | from .bqrs import * 9 | from .database import * 10 | from .query import * -------------------------------------------------------------------------------- /codeql/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/codeql/__init__.pyc -------------------------------------------------------------------------------- /codeql/__pycache__/BQRS.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/codeql/__pycache__/BQRS.cpython-39.pyc -------------------------------------------------------------------------------- /codeql/__pycache__/Database.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/codeql/__pycache__/Database.cpython-39.pyc -------------------------------------------------------------------------------- /codeql/__pycache__/Query.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/codeql/__pycache__/Query.cpython-39.pyc -------------------------------------------------------------------------------- /codeql/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/codeql/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /codeql/__pycache__/common.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/codeql/__pycache__/common.cpython-39.pyc -------------------------------------------------------------------------------- /codeql/bqrs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | CodeQL for Python. 5 | """ 6 | 7 | import csv 8 | import io 9 | import os 10 | import shutil 11 | import tempfile 12 | 13 | from .common import * 14 | 15 | class BQRS(object): 16 | def __init__(self, path): 17 | """ 18 | Arguments: 19 | path -- Location of the query results file 20 | """ 21 | # Temporaries will be cleaned up on destructor 22 | self.path = path 23 | 24 | # Helpers 25 | def run_command(self, command, options=[], post=[]): 26 | return run(['bqrs', command] + options + [self.path]) 27 | 28 | def parse(self): 29 | path = temporary_file(suffix='.csv') 30 | self.decode(format='csv', output=path) 31 | with open(path, 'r') as f: 32 | return list(csv.reader(f, delimiter=',')) 33 | 34 | # Interface 35 | def info(self, format): 36 | """ 37 | Display metadata for a BQRS file. 38 | This command displays an overview of the data contained in the compact binary BQRS file that is the result of executing a 39 | query. It shows the names and sizes of each result set (table) in the BQRS file, and the column types of each result set. 40 | It can also optionally precompute offsets for using the pagination options of codeql bqrs decode. This is mainly useful 41 | for IDE plugins. 42 | """ 43 | options = ['-v'] 44 | self.run_command('info', options) 45 | 46 | def decode(self, format=None, output=None): 47 | """ 48 | Convert result data from BQRS into other forms. 49 | The decoded output will be written to standard output, unless the --output option is specified. 50 | """ 51 | options = [] 52 | if format: 53 | options += [f'--format={format:s}'] 54 | if output: 55 | options += ['-o', output] 56 | self.run_command('decode', options) 57 | 58 | def diff(self, other): 59 | """ 60 | Compute the difference between two result sets. 61 | """ 62 | if type(other) == BQRS: 63 | other = other.path 64 | self.run_command('diff', post=[other]) -------------------------------------------------------------------------------- /codeql/common.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | CodeQL for Python. 5 | """ 6 | 7 | import io 8 | import os 9 | import subprocess 10 | import tempfile 11 | import uuid 12 | 13 | from utils.option import qlConfig 14 | from utils.log import log 15 | 16 | # Configuration 17 | codeql_path = 'codeql' 18 | search_path = None 19 | library_path = None 20 | 21 | # Temporaries 22 | temp_path = tempfile.TemporaryDirectory(dir=qlConfig("qlpath")) 23 | 24 | def temporary_root(): 25 | global temp_path 26 | if temp_path is None: 27 | temp_path = tempfile.TemporaryDirectory(prefix="codeql-python_") 28 | return temp_path.name 29 | 30 | def temporary_path(prefix, suffix): 31 | name = '' 32 | if prefix: 33 | name += prefix 34 | name += uuid.uuid4().hex 35 | if suffix: 36 | name += suffix 37 | return os.path.join(temporary_root(), name) 38 | 39 | def temporary_dir(create=True, prefix=None, suffix=None): 40 | path = temporary_path(prefix, suffix) 41 | if create: 42 | os.makedirs(str(path)) 43 | 44 | return path 45 | 46 | def temporary_file(create=True, prefix=None, suffix=None): 47 | path = temporary_path(prefix, suffix) 48 | if create: 49 | open(path, 'a').close() 50 | return path 51 | 52 | # Environment 53 | def set_search_path(path): 54 | global search_path 55 | if type(path) == list: 56 | separator = ';' if os.name == 'nt' else ':' 57 | path = separator.join(path) 58 | search_path = path 59 | 60 | def run(args): 61 | command = [codeql_path] + list(map(str, args)) 62 | proc = subprocess.Popen(" ".join(command), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=-1) 63 | proc.wait() 64 | stream_stdout = io.TextIOWrapper(proc.stdout, encoding='utf-8') 65 | stream_stderr = io.TextIOWrapper(proc.stderr, encoding='utf-8') 66 | str_stdout = stream_stdout.read() 67 | 68 | if qlConfig("debug").lower() == "on": 69 | str_stderr = stream_stderr.read() 70 | log.warning(str_stderr) 71 | 72 | return str_stdout 73 | 74 | # return subprocess.run(command, stdout=subprocess.DEVNULL) 75 | -------------------------------------------------------------------------------- /codeql/database.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | CodeQL for Python. 5 | """ 6 | 7 | import os 8 | import shutil 9 | import tempfile 10 | 11 | import codeql 12 | from .common import * 13 | from utils.option import qlConfig 14 | 15 | # Constants 16 | CODEQL_QLPACK = ''' 17 | name: codeql/java-tests 18 | groups: [java, test] 19 | dependencies: 20 | codeql/java-all: "*" 21 | codeql/java-queries: "*" 22 | extractor: java 23 | tests: . 24 | ''' 25 | 26 | class Database(object): 27 | def __init__(self, path, temp=False): 28 | """ 29 | Arguments: 30 | path -- Path of the database 31 | temp -- Remove database path in destructor 32 | """ 33 | self.path = path 34 | self.temp = temp 35 | 36 | def __del__(self): 37 | if self.temp: 38 | shutil.rmtree(self.path) 39 | 40 | # Helpers 41 | def run_command(self, command, options=[], post=[]): 42 | run(['database', command] + options + [self.path] + post) 43 | 44 | @staticmethod 45 | def from_cpp(code, command=None): 46 | # Get default compiler 47 | compilers = ['cxx', 'clang++', 'g++', 'cc', 'clang', 'gcc'] 48 | if command is None: 49 | for compiler in compilers: 50 | if shutil.which(compiler) is not None: 51 | command = [compiler, '-c'] 52 | break 53 | # Create database 54 | directory = temporary_dir() 55 | fpath = os.path.join(directory, 'source.cpp') 56 | with open(fpath, 'w') as f: 57 | f.write(code) 58 | command.append(fpath) 59 | return Database.create('cpp', directory, command) 60 | 61 | def query(self, ql): 62 | """ 63 | Syntactic sugar to execute a CodeQL snippet and parse the results. 64 | """ 65 | # Prepare query directory 66 | if not hasattr(self, 'qldir'): 67 | # self.qldir = qlConfig("qlpath") 68 | self.qldir = tempfile.TemporaryDirectory(dir=qlConfig("qlpath")) 69 | # qlpack_path = os.path.join(self.qldir, 'qlpack.yml') 70 | # with open(qlpack_path, mode='w') as f: 71 | # qlpack_text = CODEQL_QLPACK 72 | # f.write(qlpack_text) 73 | # Perform query 74 | # print(qlpack_path, os.path.exists(qlpack_path)) 75 | query_path = os.path.join(self.qldir.name, uuid.uuid4().hex + ".ql") 76 | # reply_path = os.path.join(self.qldir, 'reply.csv') 77 | with open(query_path, mode='w') as f: 78 | f.write(ql) 79 | query = codeql.Query(query_path) 80 | bqrs = query.run(database=self) 81 | return bqrs.parse() 82 | 83 | # Interface 84 | @staticmethod 85 | def create(language, source, command=None, location=None): 86 | """ 87 | Create a CodeQL database instance for a source tree that can be analyzed 88 | using one of the CodeQL products. 89 | Arguments: 90 | language -- The language that the new database will be used to analyze. 91 | source -- The root source code directory. 92 | In many cases, this will be the checkout root. Files within it are 93 | considered to be the primary source files for this database. 94 | In some output formats, files will be referred to by their relative path 95 | from this directory. 96 | command -- For compiled languages, build commands that will cause the 97 | compiler to be invoked on the source code to analyze. These commands 98 | will be executed under an instrumentation environment that allows 99 | analysis of generated code and (in some cases) standard libraries. 100 | database -- Path to generated database 101 | """ 102 | # Syntactic sugar: Default location to temporary directory 103 | if location is None: 104 | location = temporary_dir() 105 | 106 | # Create and submit command 107 | args = ['database', 'create', '-l', language, '-s', source] 108 | if command is not None: 109 | if type(command) == list: 110 | command = ' '.join(map(lambda x: f'"{x}"' if ' ' in x else x, command)) 111 | args += ['-c', command] 112 | args.append(location) 113 | run(args) 114 | 115 | # Return database instance 116 | return Database(location) 117 | 118 | 119 | def analyze(self, queries, format, output): 120 | """ 121 | Analyze a database, producing meaningful results in the context of the 122 | source code. 123 | Run a query suite (or some individual queries) against a CodeQL 124 | database, producing results, styled as alerts or paths, in SARIF or 125 | another interpreted format. 126 | This command combines the effect of the codeql database run-queries 127 | and codeql database interpret-results commands. If you want to run 128 | queries whose results don't meet the requirements for being interpreted 129 | as source-code alerts, use codeql database run-queries or codeql query 130 | run instead, and then codeql bqrs decode to convert the raw results to a 131 | readable notation. 132 | """ 133 | # Support single query or list of queries 134 | if type(queries) is not list: 135 | queries = [queries] 136 | # Prepare options 137 | options = [f'--format={format}', '-o', output] 138 | if search_path is not None: 139 | options += ['--search-path', search_path] 140 | # Dispatch command 141 | self.run_command('analyze', options, post=queries) 142 | 143 | def upgrade(self): 144 | """ 145 | Upgrade a database so it is usable by the current tools. 146 | This rewrites a CodeQL database to be compatible with the QL libraries 147 | that are found on the QL pack search path, if necessary. 148 | If an upgrade is necessary, it is irreversible. The database will 149 | subsequently be unusable with the libraries that were current when it 150 | was created. 151 | """ 152 | self.run_command('upgrade') 153 | 154 | def cleanup(self): 155 | """ 156 | Compact a CodeQL database on disk. 157 | Delete temporary data, and generally make a database as small as 158 | possible on disk without degrading its future usefulness. 159 | """ 160 | self.run_command('cleanup') 161 | 162 | def bundle(self, output): 163 | """ 164 | Create a relocatable archive of a CodeQL database. 165 | A command that zips up the useful parts of the database. This will only 166 | include the mandatory components, unless the user specifically requests 167 | that results, logs, TRAP, or similar should be included. 168 | """ 169 | options = ['-o', output] 170 | self.run_command('bundle', options) -------------------------------------------------------------------------------- /codeql/query.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | CodeQL for Python. 5 | """ 6 | 7 | import os 8 | import shutil 9 | import tempfile 10 | 11 | import codeql 12 | from .common import * 13 | from utils.option import qlConfig 14 | 15 | class Query(object): 16 | def __init__(self, path): 17 | """ 18 | Arguments: 19 | path -- Location of the query file 20 | """ 21 | # Temporaries will be cleaned up on destructor 22 | self.path = path 23 | 24 | # Helpers 25 | def run_command(self, command, options=[], post=[]): 26 | run(['query', command] + options + [self.path] + post) 27 | 28 | @staticmethod 29 | def from_source(code): 30 | path = temporary_file(suffix='.ql') 31 | with open(path, mode='w') as f: 32 | f.write(code) 33 | return Query(path) 34 | 35 | @staticmethod 36 | def from_file(path): 37 | return Query(path) 38 | 39 | # Interface 40 | def compile(self): 41 | """ 42 | Compile or check QL code. 43 | Compile one or more queries. Usually the main outcome of this command is that the compiled version of the query is written 44 | to a compilation cache where it will be found when the query is later executed. Other output options are mostly for 45 | debugging. 46 | """ 47 | self.run('compile') 48 | 49 | def run(self, database, output=None): 50 | """ 51 | Run a single query. 52 | This command runs single query against a CodeQL database or raw QL dataset. 53 | By default the result of the query will be displayed on the terminal in a human-friendly rendering. If you want to do 54 | further processing of the results, we strongly recommend using the --output option to write the results to a file in an 55 | intermediate binary format, which can then be unpacked into various more machine-friendly representations by codeql 56 | bqrs decode. 57 | If your query produces results in a form that can be interpreted as source-code alerts, you may find codeql database 58 | analyze a more convenient way to run it. In particular, codeql database analyze can produce output in the SARIF format, 59 | which can be used with an variety of alert viewers. 60 | """ 61 | # Return temporary results if no output is specified 62 | output = qlConfig('bqrsfile') 63 | 64 | # Obtain actual path to database 65 | if type(database) == codeql.Database: 66 | database = database.path 67 | # Perform query and return results 68 | options = ['-o', output, '-d', database] 69 | self.run_command('run', options) 70 | return codeql.BQRS(output) -------------------------------------------------------------------------------- /config/config.ini: -------------------------------------------------------------------------------- 1 | [codeql] 2 | qlpath = /Users/pang0lin/CodeQL/ql/java/ql/test/ 3 | bqrsfile = /Users/pang0lin/CodeQL/ql/java/ql/test/query.bqrs 4 | debug = off 5 | 6 | [log] 7 | path = out/log/ -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import argparse 6 | import utils.color_print as color_print 7 | 8 | from scan.JavaScan import JavaScan 9 | from utils.check import checkEnv,checkDB, checkQL 10 | 11 | 12 | print('''Welcome to 13 | _______________.___. _________ .___ ________ .____ 14 | \______ \__ | | \_ ___ \ ____ __| _/____ \_____ \ | | 15 | | ___// | | / \ \/ / _ \ / __ |/ __ \ / / \ \| | 16 | | | \____ | \ \___( <_> ) /_/ \ ___// \_/. \ |___ 17 | |____| / ______| \______ /\____/\____ |\___ >_____\ \_/_______ \ 18 | \/ \/ \/ \/ \__> \/ 19 | ''') 20 | 21 | 22 | if __name__ == "__main__": 23 | args = argparse.ArgumentParser() 24 | args.add_argument('-d', '--database', type=str, help='CodeQL database dir of target project') 25 | args.add_argument('-s', '--skip', action="store_true", help='Skip checking environment') 26 | 27 | parse_args = args.parse_args() 28 | 29 | if not parse_args.database: 30 | args.print_help() 31 | sys.exit() 32 | 33 | # 跳过环境检测,第一次不建议跳过,后续跳过节省时间 34 | if not parse_args.skip: 35 | if not checkEnv() or not checkDB(parse_args.database): 36 | color_print.error("Environment Error") 37 | sys.exit() 38 | 39 | if not checkQL(parse_args.database): 40 | color_print.error("qlpath error,check it at config/config.ini") 41 | sys.exit() 42 | 43 | color_print.info("Environment Check Success, start to scan database.") 44 | 45 | JavaScan().run(parse_args.database) 46 | 47 | -------------------------------------------------------------------------------- /out/log/2022-11-21.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/out/log/2022-11-21.log -------------------------------------------------------------------------------- /plugins/java/BeanShellInjection.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import DataFlow::PathGraph 4 | 5 | 6 | /** A call to `Interpreter.eval`. */ 7 | class InterpreterEvalCall extends MethodAccess { 8 | InterpreterEvalCall() { 9 | this.getMethod().hasName("eval") and 10 | this.getMethod().getDeclaringType().hasQualifiedName("bsh", "Interpreter") 11 | } 12 | } 13 | 14 | /** A call to `BshScriptEvaluator.evaluate`. */ 15 | class BshScriptEvaluatorEvaluateCall extends MethodAccess { 16 | BshScriptEvaluatorEvaluateCall() { 17 | this.getMethod().hasName("evaluate") and 18 | this.getMethod() 19 | .getDeclaringType() 20 | .hasQualifiedName("org.springframework.scripting.bsh", "BshScriptEvaluator") 21 | } 22 | } 23 | 24 | /** A sink for BeanShell expression injection vulnerabilities. */ 25 | class BeanShellInjectionSink extends DataFlow::Node { 26 | BeanShellInjectionSink() { 27 | this.asExpr() = any(InterpreterEvalCall iec).getArgument(0) or 28 | this.asExpr() = any(BshScriptEvaluatorEvaluateCall bseec).getArgument(0) 29 | } 30 | } 31 | 32 | 33 | 34 | class BeanShellInjectionConfig extends TaintTracking::Configuration { 35 | BeanShellInjectionConfig() { this = "BeanShellInjectionConfig" } 36 | 37 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 38 | 39 | override predicate isSink(DataFlow::Node sink) { sink instanceof BeanShellInjectionSink } 40 | 41 | override predicate isAdditionalTaintStep(DataFlow::Node prod, DataFlow::Node succ) { 42 | exists(ClassInstanceExpr cie | 43 | cie.getConstructedType() 44 | .hasQualifiedName("org.springframework.scripting.support", "StaticScriptSource") and 45 | cie.getArgument(0) = prod.asExpr() and 46 | cie = succ.asExpr() 47 | ) 48 | or 49 | exists(MethodAccess ma | 50 | ma.getMethod().hasName("setScript") and 51 | ma.getMethod() 52 | .getDeclaringType() 53 | .hasQualifiedName("org.springframework.scripting.support", "StaticScriptSource") and 54 | ma.getArgument(0) = prod.asExpr() and 55 | ma.getQualifier() = succ.asExpr() 56 | ) 57 | } 58 | } 59 | 60 | from DataFlow::PathNode source, DataFlow::PathNode sink, BeanShellInjectionConfig conf 61 | where conf.hasFlowPath(source, sink) 62 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 63 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "BeanShell injection" 64 | -------------------------------------------------------------------------------- /plugins/java/ClientSuppliedIpUsedInSecurityCheck.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import DataFlow 3 | import semmle.code.java.frameworks.Networking 4 | import semmle.code.java.security.QueryInjection 5 | import semmle.code.java.dataflow.FlowSources 6 | import DataFlow::PathGraph 7 | 8 | 9 | /** 10 | * A data flow source of the client ip obtained according to the remote endpoint identifier specified 11 | * (`X-Forwarded-For`, `X-Real-IP`, `Proxy-Client-IP`, etc.) in the header. 12 | * 13 | * For example: `ServletRequest.getHeader("X-Forwarded-For")`. 14 | */ 15 | class ClientSuppliedIpUsedInSecurityCheck extends DataFlow::Node { 16 | ClientSuppliedIpUsedInSecurityCheck() { 17 | exists(MethodAccess ma | 18 | ma.getMethod().hasName("getHeader") and 19 | ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() in [ 20 | "x-forwarded-for", "x-real-ip", "proxy-client-ip", "wl-proxy-client-ip", 21 | "http_x_forwarded_for", "http_x_forwarded", "http_x_cluster_client_ip", "http_client_ip", 22 | "http_forwarded_for", "http_forwarded", "http_via", "remote_addr" 23 | ] and 24 | ma = this.asExpr() 25 | ) 26 | } 27 | } 28 | 29 | /** A data flow sink for ip address forgery vulnerabilities. */ 30 | abstract class ClientSuppliedIpUsedInSecurityCheckSink extends DataFlow::Node { } 31 | 32 | /** 33 | * A data flow sink for remote client ip comparison. 34 | * 35 | * For example: `if (!StringUtils.startsWith(ipAddr, "192.168.")){...` determine whether the client ip starts 36 | * with `192.168.`, and the program can be deceived by forging the ip address. 37 | */ 38 | private class CompareSink extends ClientSuppliedIpUsedInSecurityCheckSink { 39 | CompareSink() { 40 | exists(MethodAccess ma | 41 | ma.getMethod().getName() in ["equals", "equalsIgnoreCase"] and 42 | ma.getMethod().getDeclaringType() instanceof TypeString and 43 | ma.getMethod().getNumberOfParameters() = 1 and 44 | ( 45 | ma.getArgument(0) = this.asExpr() and 46 | ma.getQualifier().(CompileTimeConstantExpr).getStringValue() instanceof PrivateHostName and 47 | not ma.getQualifier().(CompileTimeConstantExpr).getStringValue() = "0:0:0:0:0:0:0:1" 48 | or 49 | ma.getQualifier() = this.asExpr() and 50 | ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() instanceof PrivateHostName and 51 | not ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "0:0:0:0:0:0:0:1" 52 | ) 53 | ) 54 | or 55 | exists(MethodAccess ma | 56 | ma.getMethod().getName() in ["contains", "startsWith"] and 57 | ma.getMethod().getDeclaringType() instanceof TypeString and 58 | ma.getMethod().getNumberOfParameters() = 1 and 59 | ma.getQualifier() = this.asExpr() and 60 | ma.getAnArgument().(CompileTimeConstantExpr).getStringValue().regexpMatch(getIpAddressRegex()) // Matches IP-address-like strings 61 | ) 62 | or 63 | exists(MethodAccess ma | 64 | ma.getMethod().hasName("startsWith") and 65 | ma.getMethod() 66 | .getDeclaringType() 67 | .hasQualifiedName(["org.apache.commons.lang3", "org.apache.commons.lang"], "StringUtils") and 68 | ma.getMethod().getNumberOfParameters() = 2 and 69 | ma.getAnArgument() = this.asExpr() and 70 | ma.getAnArgument().(CompileTimeConstantExpr).getStringValue().regexpMatch(getIpAddressRegex()) 71 | ) 72 | or 73 | exists(MethodAccess ma | 74 | ma.getMethod().getName() in ["equals", "equalsIgnoreCase"] and 75 | ma.getMethod() 76 | .getDeclaringType() 77 | .hasQualifiedName(["org.apache.commons.lang3", "org.apache.commons.lang"], "StringUtils") and 78 | ma.getMethod().getNumberOfParameters() = 2 and 79 | ma.getAnArgument() = this.asExpr() and 80 | ma.getAnArgument().(CompileTimeConstantExpr).getStringValue() instanceof PrivateHostName and 81 | not ma.getAnArgument().(CompileTimeConstantExpr).getStringValue() = "0:0:0:0:0:0:0:1" 82 | ) 83 | } 84 | } 85 | 86 | /** A data flow sink for sql operation. */ 87 | private class SqlOperationSink extends ClientSuppliedIpUsedInSecurityCheckSink { 88 | SqlOperationSink() { this instanceof QueryInjectionSink } 89 | } 90 | 91 | /** A method that split string. */ 92 | class SplitMethod extends Method { 93 | SplitMethod() { 94 | this.getNumberOfParameters() = 1 and 95 | this.hasQualifiedName("java.lang", "String", "split") 96 | } 97 | } 98 | 99 | string getIpAddressRegex() { 100 | result = 101 | "^((10\\.((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)(\\.)?)|(192\\.168\\.)|172\\.(1[6789]|2[0-9]|3[01])\\.)((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)(\\.)?((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)$" 102 | } 103 | 104 | 105 | 106 | /** 107 | * Taint-tracking configuration tracing flow from obtaining a client ip from an HTTP header to a sensitive use. 108 | */ 109 | class ClientSuppliedIpUsedInSecurityCheckConfig extends TaintTracking::Configuration { 110 | ClientSuppliedIpUsedInSecurityCheckConfig() { this = "ClientSuppliedIpUsedInSecurityCheckConfig" } 111 | 112 | override predicate isSource(DataFlow::Node source) { 113 | source instanceof ClientSuppliedIpUsedInSecurityCheck 114 | } 115 | 116 | override predicate isSink(DataFlow::Node sink) { 117 | sink instanceof ClientSuppliedIpUsedInSecurityCheckSink 118 | } 119 | 120 | /** 121 | * Splitting a header value by `,` and taking an entry other than the first is sanitizing, because 122 | * later entries may originate from more-trustworthy intermediate proxies, not the original client. 123 | */ 124 | override predicate isSanitizer(DataFlow::Node node) { 125 | exists(ArrayAccess aa, MethodAccess ma | aa.getArray() = ma | 126 | ma.getQualifier() = node.asExpr() and 127 | ma.getMethod() instanceof SplitMethod and 128 | not aa.getIndexExpr().(CompileTimeConstantExpr).getIntValue() = 0 129 | ) 130 | or 131 | node.getType() instanceof PrimitiveType 132 | or 133 | node.getType() instanceof BoxedType 134 | } 135 | } 136 | 137 | from 138 | DataFlow::PathNode source, DataFlow::PathNode sink, ClientSuppliedIpUsedInSecurityCheckConfig conf 139 | where conf.hasFlowPath(source, sink) 140 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 141 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "IP address spoofing might include code" -------------------------------------------------------------------------------- /plugins/java/CredentialsInPropertiesFile.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import experimental.semmle.code.java.frameworks.CredentialsInPropertiesFile 3 | 4 | 5 | /** 6 | * Holds if the credentials are in a non-production properties file indicated by: 7 | * a) in a non-production directory 8 | * b) with a non-production file name 9 | */ 10 | predicate isNonProdCredentials(CredentialsConfig cc) { 11 | cc.getFile().getAbsolutePath().matches(["%dev%", "%test%", "%sample%"]) 12 | } 13 | 14 | from CredentialsConfig cc 15 | where not isNonProdCredentials(cc) 16 | select cc, "CredentialsInPropertiesFile" 17 | -------------------------------------------------------------------------------- /plugins/java/DisabledRevocationChecking.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import DataFlow 4 | import DataFlow::PathGraph 5 | 6 | 7 | /** 8 | * A taint-tracking configuration for disabling revocation checking. 9 | */ 10 | class DisabledRevocationCheckingConfig extends TaintTracking::Configuration { 11 | DisabledRevocationCheckingConfig() { this = "DisabledRevocationCheckingConfig" } 12 | 13 | override predicate isSource(DataFlow::Node source) { 14 | exists(BooleanLiteral b | b.getBooleanValue() = false | source.asExpr() = b) 15 | } 16 | 17 | override predicate isSink(DataFlow::Node sink) { sink instanceof SetRevocationEnabledSink } 18 | } 19 | 20 | /** 21 | * A sink that disables revocation checking, 22 | * i.e. calling `PKIXParameters.setRevocationEnabled(false)` 23 | * without setting a custom revocation checker in `PKIXParameters`. 24 | */ 25 | class SetRevocationEnabledSink extends DataFlow::ExprNode { 26 | SetRevocationEnabledSink() { 27 | exists(MethodAccess setRevocationEnabledCall | 28 | setRevocationEnabledCall.getMethod() instanceof SetRevocationEnabledMethod and 29 | setRevocationEnabledCall.getArgument(0) = getExpr() and 30 | not exists(MethodAccess ma, Method m | m = ma.getMethod() | 31 | (m instanceof AddCertPathCheckerMethod or m instanceof SetCertPathCheckersMethod) and 32 | ma.getQualifier().(VarAccess).getVariable() = 33 | setRevocationEnabledCall.getQualifier().(VarAccess).getVariable() 34 | ) 35 | ) 36 | } 37 | } 38 | 39 | class SetRevocationEnabledMethod extends Method { 40 | SetRevocationEnabledMethod() { 41 | getDeclaringType() instanceof PKIXParameters and 42 | hasName("setRevocationEnabled") 43 | } 44 | } 45 | 46 | class AddCertPathCheckerMethod extends Method { 47 | AddCertPathCheckerMethod() { 48 | getDeclaringType() instanceof PKIXParameters and 49 | hasName("addCertPathChecker") 50 | } 51 | } 52 | 53 | class SetCertPathCheckersMethod extends Method { 54 | SetCertPathCheckersMethod() { 55 | getDeclaringType() instanceof PKIXParameters and 56 | hasName("setCertPathCheckers") 57 | } 58 | } 59 | 60 | class PKIXParameters extends RefType { 61 | PKIXParameters() { hasQualifiedName("java.security.cert", "PKIXParameters") } 62 | } 63 | 64 | 65 | 66 | from DataFlow::PathNode source, DataFlow::PathNode sink, DisabledRevocationCheckingConfig config 67 | where config.hasFlowPath(source, sink) 68 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 69 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "This disables revocation checking." 70 | -------------------------------------------------------------------------------- /plugins/java/EJBMain.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.J2EE 3 | 4 | 5 | /** 6 | * Holds if `m` is a test method indicated by: 7 | * a) in a test directory such as `src/test/java` 8 | * b) in a test package whose name has the word `test` 9 | * c) in a test class whose name has the word `test` 10 | * d) in a test class implementing a test framework such as JUnit or TestNG 11 | */ 12 | predicate isTestMethod(Method m) { 13 | m.getDeclaringType().getName().toLowerCase().matches("%test%") or // Simple check to exclude test classes to reduce FPs 14 | m.getDeclaringType().getPackage().getName().toLowerCase().matches("%test%") or // Simple check to exclude classes in test packages to reduce FPs 15 | exists(m.getLocation().getFile().getAbsolutePath().indexOf("/src/test/java")) or // Match test directory structure of build tools like maven 16 | m instanceof TestMethod // Test method of a test case implementing a test framework such as JUnit or TestNG 17 | } 18 | 19 | 20 | 21 | /** The `main` method in an Enterprise Java Bean. */ 22 | class EnterpriseBeanMainMethod extends Method { 23 | EnterpriseBeanMainMethod() { 24 | this.getDeclaringType() instanceof EnterpriseBean and 25 | this instanceof MainMethod and 26 | not isTestMethod(this) 27 | } 28 | } 29 | 30 | from EnterpriseBeanMainMethod sm 31 | select sm, "Java EE application has a main method." 32 | -------------------------------------------------------------------------------- /plugins/java/ExecTainted.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.security.ExternalProcess 4 | import semmle.code.java.security.CommandLineQuery 5 | import DataFlow::PathGraph 6 | 7 | 8 | /** The class `com.jcraft.jsch.ChannelExec`. */ 9 | private class JSchChannelExec extends RefType { 10 | JSchChannelExec() { this.hasQualifiedName("com.jcraft.jsch", "ChannelExec") } 11 | } 12 | 13 | /** A method to set an OS Command for the execution. */ 14 | private class ChannelExecSetCommandMethod extends Method, ExecCallable { 15 | ChannelExecSetCommandMethod() { 16 | this.hasName("setCommand") and 17 | this.getDeclaringType() instanceof JSchChannelExec 18 | } 19 | 20 | override int getAnExecutedArgument() { result = 0 } 21 | } 22 | 23 | 24 | 25 | // This is a clone of query `java/command-line-injection` that also includes experimental sinks. 26 | from DataFlow::PathNode source, DataFlow::PathNode sink, ArgumentToExec execArg 27 | where execTainted(source, sink, execArg) 28 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 29 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Exec Tainted" 30 | -------------------------------------------------------------------------------- /plugins/java/FilePathInjection.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.security.PathCreation 4 | import semmle.code.java.security.PathSanitizer 5 | import DataFlow::PathGraph 6 | import java 7 | private import semmle.code.java.dataflow.ExternalFlow 8 | private import semmle.code.java.dataflow.FlowSources 9 | 10 | /** The class `com.jfinal.core.Controller`. */ 11 | class JFinalController extends RefType { 12 | JFinalController() { this.hasQualifiedName("com.jfinal.core", "Controller") } 13 | } 14 | 15 | /** The method `getSessionAttr` of `JFinalController`. */ 16 | class GetSessionAttributeMethod extends Method { 17 | GetSessionAttributeMethod() { 18 | this.getName() = "getSessionAttr" and 19 | this.getDeclaringType().getASupertype*() instanceof JFinalController 20 | } 21 | } 22 | 23 | /** The method `setSessionAttr` of `JFinalController`. */ 24 | class SetSessionAttributeMethod extends Method { 25 | SetSessionAttributeMethod() { 26 | this.getName() = "setSessionAttr" and 27 | this.getDeclaringType().getASupertype*() instanceof JFinalController 28 | } 29 | } 30 | 31 | /** A request attribute getter method of `JFinalController`. */ 32 | class GetRequestAttributeMethod extends Method { 33 | GetRequestAttributeMethod() { 34 | this.getName().matches("getAttr%") and 35 | this.getDeclaringType().getASupertype*() instanceof JFinalController 36 | } 37 | } 38 | 39 | /** A request attribute setter method of `JFinalController`. */ 40 | class SetRequestAttributeMethod extends Method { 41 | SetRequestAttributeMethod() { 42 | this.getName() = ["set", "setAttr"] and 43 | this.getDeclaringType().getASupertype*() instanceof JFinalController 44 | } 45 | } 46 | 47 | /** 48 | * Value step from a setter call to a corresponding getter call relating to a 49 | * session or request attribute. 50 | */ 51 | private class SetToGetAttributeStep extends AdditionalValueStep { 52 | override predicate step(DataFlow::Node pred, DataFlow::Node succ) { 53 | exists(MethodAccess gma, MethodAccess sma | 54 | ( 55 | gma.getMethod() instanceof GetSessionAttributeMethod and 56 | sma.getMethod() instanceof SetSessionAttributeMethod 57 | or 58 | gma.getMethod() instanceof GetRequestAttributeMethod and 59 | sma.getMethod() instanceof SetRequestAttributeMethod 60 | ) and 61 | gma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = 62 | sma.getArgument(0).(CompileTimeConstantExpr).getStringValue() 63 | | 64 | pred.asExpr() = sma.getArgument(1) and 65 | succ.asExpr() = gma 66 | ) 67 | } 68 | } 69 | 70 | /** Remote flow source models relating to `JFinal`. */ 71 | private class JFinalControllerSource extends SourceModelCsv { 72 | override predicate row(string row) { 73 | row = 74 | [ 75 | "com.jfinal.core;Controller;true;getCookie" + ["", "Object", "Objects", "ToInt", "ToLong"] + 76 | ";;;ReturnValue;remote;manual", 77 | "com.jfinal.core;Controller;true;getFile" + ["", "s"] + ";;;ReturnValue;remote;manual", 78 | "com.jfinal.core;Controller;true;getHeader;;;ReturnValue;remote;manual", 79 | "com.jfinal.core;Controller;true;getKv;;;ReturnValue;remote;manual", 80 | "com.jfinal.core;Controller;true;getPara" + 81 | [ 82 | "", "Map", "ToBoolean", "ToDate", "ToInt", "ToLong", "Values", "ValuesToInt", 83 | "ValuesToLong" 84 | ] + ";;;ReturnValue;remote;manual", 85 | "com.jfinal.core;Controller;true;get" + ["", "Int", "Long", "Boolean", "Date"] + 86 | ";;;ReturnValue;remote;manual" 87 | ] 88 | } 89 | } 90 | 91 | 92 | 93 | /** A complementary sanitizer that protects against path traversal using path normalization. */ 94 | class PathNormalizeSanitizer extends MethodAccess { 95 | PathNormalizeSanitizer() { 96 | exists(RefType t | 97 | t instanceof TypePath or 98 | t.hasQualifiedName("kotlin.io", "FilesKt") 99 | | 100 | this.getMethod().getDeclaringType() = t and 101 | this.getMethod().hasName("normalize") 102 | ) 103 | or 104 | this.getMethod().getDeclaringType() instanceof TypeFile and 105 | this.getMethod().hasName(["getCanonicalPath", "getCanonicalFile"]) 106 | } 107 | } 108 | 109 | /** A node with path normalization. */ 110 | class NormalizedPathNode extends DataFlow::Node { 111 | NormalizedPathNode() { 112 | TaintTracking::localExprTaint(this.asExpr(), any(PathNormalizeSanitizer ma)) 113 | } 114 | } 115 | 116 | class InjectFilePathConfig extends TaintTracking::Configuration { 117 | InjectFilePathConfig() { this = "InjectFilePathConfig" } 118 | 119 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 120 | 121 | override predicate isSink(DataFlow::Node sink) { 122 | sink.asExpr() = any(PathCreation p).getAnInput() and 123 | not sink instanceof NormalizedPathNode 124 | } 125 | 126 | override predicate isSanitizer(DataFlow::Node node) { 127 | exists(Type t | t = node.getType() | t instanceof BoxedType or t instanceof PrimitiveType) 128 | or 129 | node instanceof PathInjectionSanitizer 130 | } 131 | } 132 | 133 | from DataFlow::PathNode source, DataFlow::PathNode sink, InjectFilePathConfig conf 134 | where conf.hasFlowPath(source, sink) 135 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 136 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "OpenStream" 137 | 138 | -------------------------------------------------------------------------------- /plugins/java/HardcodedJwtKey.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.TaintTracking 3 | import DataFlow::PathGraph 4 | 5 | private import semmle.code.java.dataflow.ExternalFlow 6 | private import semmle.code.java.dataflow.FlowSources 7 | 8 | /** The class `com.auth0.jwt.JWT`. */ 9 | class Jwt extends RefType { 10 | Jwt() { this.hasQualifiedName("com.auth0.jwt", "JWT") } 11 | } 12 | 13 | /** The class `com.auth0.jwt.JWTCreator.Builder`. */ 14 | class JwtBuilder extends RefType { 15 | JwtBuilder() { this.hasQualifiedName("com.auth0.jwt", "JWTCreator$Builder") } 16 | } 17 | 18 | /** The class `com.auth0.jwt.algorithms.Algorithm`. */ 19 | class JwtAlgorithm extends RefType { 20 | JwtAlgorithm() { this.hasQualifiedName("com.auth0.jwt.algorithms", "Algorithm") } 21 | } 22 | 23 | /** 24 | * The interface `com.auth0.jwt.interfaces.JWTVerifier` or its implementation 25 | * `com.auth0.jwt.JWTVerifier`. 26 | */ 27 | class JwtVerifier extends RefType { 28 | JwtVerifier() { 29 | this.hasQualifiedName(["com.auth0.jwt", "com.auth0.jwt.interfaces"], "JWTVerifier") 30 | } 31 | } 32 | 33 | /** A method that creates an instance of `com.auth0.jwt.algorithms.Algorithm`. */ 34 | class GetAlgorithmMethod extends Method { 35 | GetAlgorithmMethod() { 36 | this.getDeclaringType() instanceof JwtAlgorithm and 37 | this.getName().matches(["HMAC%", "ECDSA%", "RSA%"]) 38 | } 39 | } 40 | 41 | /** The `require` method of `com.auth0.jwt.JWT`. */ 42 | class RequireMethod extends Method { 43 | RequireMethod() { 44 | this.getDeclaringType() instanceof Jwt and 45 | this.hasName("require") 46 | } 47 | } 48 | 49 | /** The `sign` method of `com.auth0.jwt.JWTCreator.Builder`. */ 50 | class SignTokenMethod extends Method { 51 | SignTokenMethod() { 52 | this.getDeclaringType() instanceof JwtBuilder and 53 | this.hasName("sign") 54 | } 55 | } 56 | 57 | /** The `verify` method of `com.auth0.jwt.interfaces.JWTVerifier`. */ 58 | class VerifyTokenMethod extends Method { 59 | VerifyTokenMethod() { 60 | this.getDeclaringType() instanceof JwtVerifier and 61 | this.hasName("verify") 62 | } 63 | } 64 | 65 | /** 66 | * A data flow source for JWT token signing vulnerabilities. 67 | */ 68 | abstract class JwtKeySource extends DataFlow::Node { } 69 | 70 | /** 71 | * A data flow sink for JWT token signing vulnerabilities. 72 | */ 73 | abstract class JwtTokenSink extends DataFlow::Node { } 74 | 75 | /** 76 | * A hardcoded string literal as a source for JWT token signing vulnerabilities. 77 | */ 78 | class HardcodedKeyStringSource extends JwtKeySource { 79 | HardcodedKeyStringSource() { exists(this.asExpr().(CompileTimeConstantExpr).getStringValue()) } 80 | } 81 | 82 | /** 83 | * An expression used to sign JWT tokens as a sink of JWT token signing vulnerabilities. 84 | */ 85 | private class SignTokenSink extends JwtTokenSink { 86 | SignTokenSink() { 87 | exists(MethodAccess ma | 88 | ma.getMethod() instanceof SignTokenMethod and 89 | this.asExpr() = ma.getArgument(0) 90 | ) 91 | } 92 | } 93 | 94 | /** 95 | * An expression used to verify JWT tokens as a sink of JWT token signing vulnerabilities. 96 | */ 97 | private class VerifyTokenSink extends JwtTokenSink { 98 | VerifyTokenSink() { 99 | exists(MethodAccess ma | 100 | ma.getMethod() instanceof VerifyTokenMethod and 101 | this.asExpr() = ma.getQualifier() 102 | ) 103 | } 104 | } 105 | 106 | /** 107 | * A configuration depicting taint flow for checking JWT token signing vulnerabilities. 108 | */ 109 | class HardcodedJwtKeyConfiguration extends TaintTracking::Configuration { 110 | HardcodedJwtKeyConfiguration() { this = "Hard-coded JWT Signing Key" } 111 | 112 | override predicate isSource(DataFlow::Node source) { source instanceof JwtKeySource } 113 | 114 | override predicate isSink(DataFlow::Node sink) { sink instanceof JwtTokenSink } 115 | 116 | override predicate isAdditionalTaintStep(DataFlow::Node prev, DataFlow::Node succ) { 117 | exists(MethodAccess ma | 118 | ( 119 | ma.getMethod() instanceof GetAlgorithmMethod or 120 | ma.getMethod() instanceof RequireMethod 121 | ) and 122 | prev.asExpr() = ma.getArgument(0) and 123 | succ.asExpr() = ma 124 | ) 125 | } 126 | } 127 | 128 | /** Taint model related to verifying JWT tokens. */ 129 | private class VerificationFlowStep extends SummaryModelCsv { 130 | override predicate row(string row) { 131 | row = 132 | [ 133 | "com.auth0.jwt.interfaces;Verification;true;build;;;Argument[-1];ReturnValue;taint;manual", 134 | "com.auth0.jwt.interfaces;Verification;true;" + 135 | ["acceptLeeway", "acceptExpiresAt", "acceptNotBefore", "acceptIssuedAt", "ignoreIssuedAt"] 136 | + ";;;Argument[-1];ReturnValue;value;manual", 137 | "com.auth0.jwt.interfaces;Verification;true;with" + 138 | [ 139 | "Issuer", "Subject", "Audience", "AnyOfAudience", "ClaimPresence", "Claim", 140 | "ArrayClaim", "JWTId" 141 | ] + ";;;Argument[-1];ReturnValue;value;manual" 142 | ] 143 | } 144 | } 145 | 146 | 147 | 148 | from DataFlow::PathNode source, DataFlow::PathNode sink, HardcodedJwtKeyConfiguration cfg 149 | where cfg.hasFlowPath(source, sink) 150 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 151 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "HardcodedJwtKey" 152 | -------------------------------------------------------------------------------- /plugins/java/HashWithoutSalt.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.TaintTracking 3 | import DataFlow::PathGraph 4 | 5 | 6 | /** 7 | * Gets a regular expression for matching common names of variables 8 | * that indicate the value being held is a password. 9 | */ 10 | string getPasswordRegex() { result = "(?i).*pass(wd|word|code|phrase).*" } 11 | 12 | /** Finds variables that hold password information judging by their names. */ 13 | class PasswordVarExpr extends VarAccess { 14 | PasswordVarExpr() { 15 | exists(string name | name = this.getVariable().getName().toLowerCase() | 16 | name.regexpMatch(getPasswordRegex()) and not name.matches("%hash%") // Exclude variable names such as `passwordHash` since their values were already hashed 17 | ) 18 | } 19 | } 20 | 21 | /** Holds if `Expr` e is a direct or indirect operand of `ae`. */ 22 | predicate hasAddExprAncestor(AddExpr ae, Expr e) { ae.getAnOperand+() = e } 23 | 24 | /** The Java class `java.security.MessageDigest`. */ 25 | class MessageDigest extends RefType { 26 | MessageDigest() { this.hasQualifiedName("java.security", "MessageDigest") } 27 | } 28 | 29 | /** The method `digest()` declared in `java.security.MessageDigest`. */ 30 | class MDDigestMethod extends Method { 31 | MDDigestMethod() { 32 | this.getDeclaringType() instanceof MessageDigest and 33 | this.hasName("digest") 34 | } 35 | } 36 | 37 | /** The method `update()` declared in `java.security.MessageDigest`. */ 38 | class MDUpdateMethod extends Method { 39 | MDUpdateMethod() { 40 | this.getDeclaringType() instanceof MessageDigest and 41 | this.hasName("update") 42 | } 43 | } 44 | 45 | /** The hashing method that could taint the input. */ 46 | class MDHashMethodAccess extends MethodAccess { 47 | MDHashMethodAccess() { 48 | ( 49 | this.getMethod() instanceof MDDigestMethod or 50 | this.getMethod() instanceof MDUpdateMethod 51 | ) and 52 | this.getNumArgument() != 0 53 | } 54 | } 55 | 56 | /** 57 | * Holds if `MethodAccess` ma is a method access of `MDHashMethodAccess` or 58 | * invokes a method access of `MDHashMethodAccess` directly or indirectly. 59 | */ 60 | predicate isHashAccess(MethodAccess ma) { 61 | ma instanceof MDHashMethodAccess 62 | or 63 | exists(MethodAccess mca | 64 | ma.getMethod().calls(mca.getMethod()) and 65 | isHashAccess(mca) and 66 | DataFlow::localExprFlow(ma.getMethod().getAParameter().getAnAccess(), mca.getAnArgument()) 67 | ) 68 | } 69 | 70 | /** 71 | * Holds if there is a second method access that satisfies `isHashAccess` whose qualifier or argument 72 | * is the same as the method call `ma` that satisfies `isHashAccess`. 73 | */ 74 | predicate hasAnotherHashCall(MethodAccess ma) { 75 | isHashAccess(ma) and 76 | exists(MethodAccess ma2, VarAccess va | 77 | ma2 != ma and 78 | isHashAccess(ma2) and 79 | not va.getVariable().getType() instanceof PrimitiveType and 80 | ( 81 | ma.getQualifier() = va and 82 | ma2.getQualifier() = va.getVariable().getAnAccess() 83 | or 84 | ma.getQualifier() = va and 85 | ma2.getAnArgument() = va.getVariable().getAnAccess() 86 | or 87 | ma.getAnArgument() = va and 88 | ma2.getQualifier() = va.getVariable().getAnAccess() 89 | or 90 | ma.getAnArgument() = va and 91 | ma2.getAnArgument() = va.getVariable().getAnAccess() 92 | ) 93 | ) 94 | } 95 | 96 | /** 97 | * Holds if `MethodAccess` ma is part of a call graph that satisfies `isHashAccess` 98 | * but is not at the top of the call hierarchy. 99 | */ 100 | predicate hasHashAncestor(MethodAccess ma) { 101 | exists(MethodAccess mpa | 102 | mpa.getMethod().calls(ma.getMethod()) and 103 | isHashAccess(mpa) and 104 | DataFlow::localExprFlow(mpa.getMethod().getAParameter().getAnAccess(), ma.getAnArgument()) 105 | ) 106 | } 107 | 108 | /** Holds if `MethodAccess` ma is a hashing call without a sibling node making another hashing call. */ 109 | predicate isSingleHashMethodCall(MethodAccess ma) { 110 | isHashAccess(ma) and not hasAnotherHashCall(ma) 111 | } 112 | 113 | /** Holds if `MethodAccess` ma is a single hashing call that is not invoked by a wrapper method. */ 114 | predicate isSink(MethodAccess ma) { isSingleHashMethodCall(ma) and not hasHashAncestor(ma) } 115 | 116 | /** Sink of hashing calls. */ 117 | class HashWithoutSaltSink extends DataFlow::ExprNode { 118 | HashWithoutSaltSink() { 119 | exists(MethodAccess ma | 120 | this.asExpr() = ma.getAnArgument() and 121 | isSink(ma) 122 | ) 123 | } 124 | } 125 | 126 | /** 127 | * Taint configuration tracking flow from an expression whose name suggests it holds password data 128 | * to a method call that generates a hash without a salt. 129 | */ 130 | class HashWithoutSaltConfiguration extends TaintTracking::Configuration { 131 | HashWithoutSaltConfiguration() { this = "HashWithoutSaltConfiguration" } 132 | 133 | override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof PasswordVarExpr } 134 | 135 | override predicate isSink(DataFlow::Node sink) { sink instanceof HashWithoutSaltSink } 136 | 137 | /** 138 | * Holds if a password is concatenated with a salt then hashed together through the call `System.arraycopy(password.getBytes(), ...)`, for example, 139 | * `System.arraycopy(password.getBytes(), 0, allBytes, 0, password.getBytes().length);` 140 | * `System.arraycopy(salt, 0, allBytes, password.getBytes().length, salt.length);` 141 | * `byte[] messageDigest = md.digest(allBytes);` 142 | * Or the password is concatenated with a salt as a string. 143 | */ 144 | override predicate isSanitizer(DataFlow::Node node) { 145 | exists(MethodAccess ma | 146 | ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "System") and 147 | ma.getMethod().hasName("arraycopy") and 148 | ma.getArgument(0) = node.asExpr() 149 | ) // System.arraycopy(password.getBytes(), ...) 150 | or 151 | exists(AddExpr e | hasAddExprAncestor(e, node.asExpr())) // password+salt 152 | or 153 | exists(ConditionalExpr ce | ce.getAChildExpr() = node.asExpr()) // useSalt?password+":"+salt:password 154 | or 155 | exists(MethodAccess ma | 156 | ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") and 157 | ma.getMethod().hasName("append") and 158 | ma.getArgument(0) = node.asExpr() // stringBuilder.append(password).append(salt) 159 | ) 160 | or 161 | exists(MethodAccess ma | 162 | ma.getQualifier().(VarAccess).getVariable().getType() instanceof Interface and 163 | ma.getAnArgument() = node.asExpr() // Method access of interface type variables requires runtime determination thus not handled 164 | ) 165 | } 166 | } 167 | 168 | from DataFlow::PathNode source, DataFlow::PathNode sink, HashWithoutSaltConfiguration cc 169 | where cc.hasFlowPath(source, sink) 170 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 171 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "hashed without a salt." 172 | -------------------------------------------------------------------------------- /plugins/java/IgnoredHostnameVerification.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.security.Encryption 3 | 4 | 5 | /** A `HostnameVerifier.verify()` call that is not wrapped in another `HostnameVerifier`. */ 6 | private class HostnameVerificationCall extends MethodAccess { 7 | HostnameVerificationCall() { 8 | this.getMethod() instanceof HostnameVerifierVerify and 9 | not this.getCaller() instanceof HostnameVerifierVerify 10 | } 11 | 12 | /** Holds if the result of the call is not used. */ 13 | predicate isIgnored() { this instanceof ValueDiscardingExpr } 14 | } 15 | 16 | from HostnameVerificationCall verification 17 | where verification.isIgnored() 18 | select verification, "Ignored result of hostname verification." 19 | -------------------------------------------------------------------------------- /plugins/java/IncorrectURLVerification.ql: -------------------------------------------------------------------------------- 1 | import java 2 | 3 | 4 | /** 5 | * The Java class `android.R.string` specific to Android applications, which contains references to application specific resources defined in /res/values/strings.xml. 6 | * For example, `...example.com...` in the application com.example.android.web can be referred as R.string.host with the type com.example.android.web.R$string 7 | */ 8 | class AndroidRString extends RefType { 9 | AndroidRString() { this.hasQualifiedName(_, "R$string") } 10 | } 11 | 12 | /** 13 | * The Java class `android.net.Uri` and `java.net.URL`. 14 | */ 15 | class Uri extends RefType { 16 | Uri() { 17 | this.hasQualifiedName("android.net", "Uri") or 18 | this.hasQualifiedName("java.net", "URL") 19 | } 20 | } 21 | 22 | /** 23 | * The method `getHost()` declared in `android.net.Uri` and `java.net.URL`. 24 | */ 25 | class UriGetHostMethod extends Method { 26 | UriGetHostMethod() { 27 | this.getDeclaringType() instanceof Uri and 28 | this.hasName("getHost") and 29 | this.getNumberOfParameters() = 0 30 | } 31 | } 32 | 33 | /** 34 | * The method access with incorrect string comparison 35 | */ 36 | class HostVerificationMethodAccess extends MethodAccess { 37 | HostVerificationMethodAccess() { 38 | ( 39 | this.getMethod().hasName("endsWith") or 40 | this.getMethod().hasName("contains") or 41 | this.getMethod().hasName("indexOf") 42 | ) and 43 | this.getMethod().getNumberOfParameters() = 1 and 44 | ( 45 | this.getArgument(0).(StringLiteral).getValue().charAt(0) != "." //string constant comparison e.g. uri.getHost().endsWith("example.com") 46 | or 47 | this.getArgument(0) 48 | .(AddExpr) 49 | .getLeftOperand() 50 | .(VarAccess) 51 | .getVariable() 52 | .getAnAssignedValue() 53 | .(StringLiteral) 54 | .getValue() 55 | .charAt(0) != "." //var1+var2, check var1 starts with "." e.g. String domainName = "example"; Uri.parse(url).getHost().endsWith(domainName+".com") 56 | or 57 | this.getArgument(0).(AddExpr).getLeftOperand().(StringLiteral).getValue().charAt(0) != "." //"."+var2, check string constant "." e.g. String domainName = "example.com"; Uri.parse(url).getHost().endsWith("www."+domainName) 58 | or 59 | exists(MethodAccess ma, Method m, Field f | 60 | this.getArgument(0) = ma and 61 | ma.getMethod() = m and 62 | m.hasName("getString") and 63 | m.getDeclaringType().getQualifiedName() = "android.content.res.Resources" and 64 | ma.getArgument(0).(FieldRead).getField() = f and 65 | f.getDeclaringType() instanceof AndroidRString 66 | ) //Check resource properties in /res/values/strings.xml in Android mobile applications using res.getString(R.string.key) 67 | or 68 | this.getArgument(0) 69 | .(VarAccess) 70 | .getVariable() 71 | .getAnAssignedValue() 72 | .(StringLiteral) 73 | .getValue() 74 | .charAt(0) != "." //check variable starts with "." e.g. String domainName = "example.com"; Uri.parse(url).getHost().endsWith(domainName) 75 | ) 76 | } 77 | } 78 | 79 | from UriGetHostMethod um, MethodAccess uma, HostVerificationMethodAccess hma 80 | where hma.getQualifier() = uma and uma.getMethod() = um 81 | select hma, "Method has potentially $@.", hma.getArgument(0), "improper URL verification" 82 | -------------------------------------------------------------------------------- /plugins/java/InsecureDexLoading.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import DataFlow::PathGraph 4 | 5 | 6 | /** 7 | * A taint-tracking configuration detecting unsafe use of a 8 | * `DexClassLoader` by an Android app. 9 | */ 10 | class InsecureDexConfiguration extends TaintTracking::Configuration { 11 | InsecureDexConfiguration() { this = "Insecure Dex File Load" } 12 | 13 | override predicate isSource(DataFlow::Node source) { source instanceof InsecureDexSource } 14 | 15 | override predicate isSink(DataFlow::Node sink) { sink instanceof InsecureDexSink } 16 | 17 | override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { 18 | flowStep(pred, succ) 19 | } 20 | } 21 | 22 | /** A data flow source for insecure Dex class loading vulnerabilities. */ 23 | abstract class InsecureDexSource extends DataFlow::Node { } 24 | 25 | /** A data flow sink for insecure Dex class loading vulnerabilities. */ 26 | abstract class InsecureDexSink extends DataFlow::Node { } 27 | 28 | private predicate flowStep(DataFlow::Node pred, DataFlow::Node succ) { 29 | // propagate from a `java.io.File` via the `File.getAbsolutePath` call. 30 | exists(MethodAccess m | 31 | m.getMethod().getDeclaringType() instanceof TypeFile and 32 | m.getMethod().hasName("getAbsolutePath") and 33 | m.getQualifier() = pred.asExpr() and 34 | m = succ.asExpr() 35 | ) 36 | or 37 | // propagate from a `java.io.File` via the `File.toString` call. 38 | exists(MethodAccess m | 39 | m.getMethod().getDeclaringType() instanceof TypeFile and 40 | m.getMethod().hasName("toString") and 41 | m.getQualifier() = pred.asExpr() and 42 | m = succ.asExpr() 43 | ) 44 | or 45 | // propagate to newly created `File` if the parent directory of the new `File` is tainted 46 | exists(ConstructorCall cc | 47 | cc.getConstructedType() instanceof TypeFile and 48 | cc.getArgument(0) = pred.asExpr() and 49 | cc = succ.asExpr() 50 | ) 51 | } 52 | 53 | /** 54 | * An argument to a `DexClassLoader` call taken as a sink for 55 | * insecure Dex class loading vulnerabilities. 56 | */ 57 | private class DexClassLoader extends InsecureDexSink { 58 | DexClassLoader() { 59 | exists(ConstructorCall cc | 60 | cc.getConstructedType().hasQualifiedName("dalvik.system", "DexClassLoader") 61 | | 62 | this.asExpr() = cc.getArgument(0) 63 | ) 64 | } 65 | } 66 | 67 | /** 68 | * A `File` instance which reads from an SD card 69 | * taken as a source for insecure Dex class loading vulnerabilities. 70 | */ 71 | private class ExternalFile extends InsecureDexSource { 72 | ExternalFile() { 73 | exists(ConstructorCall cc, Argument a | 74 | cc.getConstructedType() instanceof TypeFile and 75 | a = cc.getArgument(0) and 76 | a.(CompileTimeConstantExpr).getStringValue().matches("%sdcard%") 77 | | 78 | this.asExpr() = a 79 | ) 80 | } 81 | } 82 | 83 | /** 84 | * A directory or file which may be stored in an world writable directory 85 | * taken as a source for insecure Dex class loading vulnerabilities. 86 | */ 87 | private class ExternalStorageDirSource extends InsecureDexSource { 88 | ExternalStorageDirSource() { 89 | exists(Method m | 90 | m.getDeclaringType().hasQualifiedName("android.os", "Environment") and 91 | m.hasName("getExternalStorageDirectory") 92 | or 93 | m.getDeclaringType().hasQualifiedName("android.content", "Context") and 94 | m.hasName([ 95 | "getExternalFilesDir", "getExternalFilesDirs", "getExternalMediaDirs", 96 | "getExternalCacheDir", "getExternalCacheDirs" 97 | ]) 98 | | 99 | this.asExpr() = m.getAReference() 100 | ) 101 | } 102 | } 103 | 104 | 105 | 106 | from DataFlow::PathNode source, DataFlow::PathNode sink, InsecureDexConfiguration conf 107 | where conf.hasFlowPath(source, sink) 108 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 109 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "a value loaded from a world-writable source." 110 | -------------------------------------------------------------------------------- /plugins/java/InsecureDirectoryConfig.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.xml.WebXML 3 | 4 | 5 | /** 6 | * The default `` element in a `web.xml` file. 7 | */ 8 | private class DefaultTomcatServlet extends WebServletClass { 9 | DefaultTomcatServlet() { 10 | this.getTextValue() = "org.apache.catalina.servlets.DefaultServlet" //Default servlet of Tomcat and other servlet containers derived from Tomcat like Glassfish 11 | } 12 | } 13 | 14 | /** 15 | * The `` element in a `web.xml` file, nested under a `` element controlling directory listing. 16 | */ 17 | class DirectoryListingInitParam extends WebXmlElement { 18 | DirectoryListingInitParam() { 19 | this.getName() = "init-param" and 20 | this.getAChild("param-name").getTextValue() = "listings" and 21 | exists(WebServlet servlet | 22 | this.getParent() = servlet and 23 | servlet.getAChild("servlet-class") instanceof DefaultTomcatServlet 24 | ) 25 | } 26 | 27 | /** 28 | * Check the `` element (true - enabled, false - disabled) 29 | */ 30 | predicate isListingEnabled() { 31 | this.getAChild("param-value").getTextValue().toLowerCase() = "true" 32 | } 33 | } 34 | 35 | from DirectoryListingInitParam initp 36 | where initp.isListingEnabled() 37 | select initp, "Directory listing should be disabled to mitigate filename and path disclosure." 38 | -------------------------------------------------------------------------------- /plugins/java/InsecureLdapEndpoint.ql: -------------------------------------------------------------------------------- 1 | import java 2 | 3 | 4 | /** The method to set a system property. */ 5 | class SetSystemPropertyMethod extends Method { 6 | SetSystemPropertyMethod() { 7 | this.hasName("setProperty") and 8 | this.getDeclaringType().hasQualifiedName("java.lang", "System") 9 | } 10 | } 11 | 12 | /** The class `java.util.Hashtable`. */ 13 | class TypeHashtable extends Class { 14 | TypeHashtable() { this.getSourceDeclaration().hasQualifiedName("java.util", "Hashtable") } 15 | } 16 | 17 | /** 18 | * The method to set Java properties either through `setProperty` declared in the class `Properties` 19 | * or `put` declared in its parent class `HashTable`. 20 | */ 21 | class SetPropertyMethod extends Method { 22 | SetPropertyMethod() { 23 | this.getDeclaringType().getAnAncestor() instanceof TypeHashtable and 24 | this.hasName(["put", "setProperty"]) 25 | } 26 | } 27 | 28 | /** The `setProperties` method declared in `java.lang.System`. */ 29 | class SetSystemPropertiesMethod extends Method { 30 | SetSystemPropertiesMethod() { 31 | this.hasName("setProperties") and 32 | this.getDeclaringType().hasQualifiedName("java.lang", "System") 33 | } 34 | } 35 | 36 | /** 37 | * Holds if `Expr` expr is evaluated to the string literal 38 | * `com.sun.jndi.ldap.object.disableEndpointIdentification`. 39 | */ 40 | predicate isPropertyDisableLdapEndpointId(Expr expr) { 41 | expr.(CompileTimeConstantExpr).getStringValue() = 42 | "com.sun.jndi.ldap.object.disableEndpointIdentification" 43 | or 44 | exists(Field f | 45 | expr = f.getAnAccess() and 46 | f.getAnAssignedValue().(StringLiteral).getValue() = 47 | "com.sun.jndi.ldap.object.disableEndpointIdentification" 48 | ) 49 | } 50 | 51 | /** Holds if an expression is evaluated to the boolean value true. */ 52 | predicate isBooleanTrue(Expr expr) { 53 | expr.(CompileTimeConstantExpr).getStringValue() = "true" // "true" 54 | or 55 | expr.(BooleanLiteral).getBooleanValue() = true // true 56 | or 57 | exists(MethodAccess ma | 58 | expr = ma and 59 | ma.getMethod() instanceof ToStringMethod and 60 | ma.getQualifier().(FieldAccess).getField().hasName("TRUE") and 61 | ma.getQualifier() 62 | .(FieldAccess) 63 | .getField() 64 | .getDeclaringType() 65 | .hasQualifiedName("java.lang", "Boolean") // Boolean.TRUE.toString() 66 | ) 67 | } 68 | 69 | /** Holds if `ma` is in a test class or method. */ 70 | predicate isTestMethod(MethodAccess ma) { 71 | ma.getEnclosingCallable() instanceof TestMethod or 72 | ma.getEnclosingCallable().getDeclaringType() instanceof TestClass or 73 | ma.getEnclosingCallable().getDeclaringType().getPackage().getName().matches("%test%") or 74 | ma.getEnclosingCallable().getDeclaringType().getName().toLowerCase().matches("%test%") 75 | } 76 | 77 | /** Holds if `MethodAccess` ma disables SSL endpoint check. */ 78 | predicate isInsecureSslEndpoint(MethodAccess ma) { 79 | ( 80 | ma.getMethod() instanceof SetSystemPropertyMethod and 81 | isPropertyDisableLdapEndpointId(ma.getArgument(0)) and 82 | isBooleanTrue(ma.getArgument(1)) //com.sun.jndi.ldap.object.disableEndpointIdentification=true 83 | or 84 | ma.getMethod() instanceof SetSystemPropertiesMethod and 85 | exists(MethodAccess ma2 | 86 | ma2.getMethod() instanceof SetPropertyMethod and 87 | isPropertyDisableLdapEndpointId(ma2.getArgument(0)) and 88 | isBooleanTrue(ma2.getArgument(1)) and //com.sun.jndi.ldap.object.disableEndpointIdentification=true 89 | ma2.getQualifier().(VarAccess).getVariable().getAnAccess() = ma.getArgument(0) // systemProps.setProperties(properties) 90 | ) 91 | ) 92 | } 93 | 94 | from MethodAccess ma 95 | where 96 | isInsecureSslEndpoint(ma) and 97 | not isTestMethod(ma) 98 | select ma, "LDAPS configuration allows insecure endpoint identification." 99 | -------------------------------------------------------------------------------- /plugins/java/InsecureRmiJmxEnvironmentConfiguration.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.DataFlow 3 | import semmle.code.java.Maps 4 | 5 | 6 | /** Holds if `constructor` instantiates an RMI or JMX server. */ 7 | predicate isRmiOrJmxServerCreateConstructor(Constructor constructor) { 8 | constructor 9 | .getDeclaringType() 10 | .hasQualifiedName("javax.management.remote.rmi", "RMIConnectorServer") 11 | } 12 | 13 | /** Holds if `method` creates an RMI or JMX server. */ 14 | predicate isRmiOrJmxServerCreateMethod(Method method) { 15 | method.getName() = "newJMXConnectorServer" and 16 | method.getDeclaringType().hasQualifiedName("javax.management.remote", "JMXConnectorServerFactory") 17 | } 18 | 19 | /** 20 | * Models flow from the qualifier of a 21 | * `map.put("jmx.remote.rmi.server.credential.types", value)` call 22 | * to an RMI or JMX initialisation call. 23 | */ 24 | class SafeFlow extends DataFlow::Configuration { 25 | SafeFlow() { this = "MapToPutCredentialstypeConfiguration" } 26 | 27 | override predicate isSource(DataFlow::Node source) { putsCredentialtypesKey(source.asExpr()) } 28 | 29 | override predicate isSink(DataFlow::Node sink) { 30 | exists(Call c | 31 | isRmiOrJmxServerCreateConstructor(c.getCallee()) or 32 | isRmiOrJmxServerCreateMethod(c.getCallee()) 33 | | 34 | sink.asExpr() = c.getArgument(1) 35 | ) 36 | } 37 | 38 | /** 39 | * Holds if a `put` call on `qualifier` puts a key match 40 | * into the map. 41 | */ 42 | private predicate putsCredentialtypesKey(Expr qualifier) { 43 | exists(MapPutCall put | 44 | put.getKey().(CompileTimeConstantExpr).getStringValue() = 45 | [ 46 | "jmx.remote.rmi.server.credential.types", 47 | "jmx.remote.rmi.server.credentials.filter.pattern" 48 | ] 49 | or 50 | put.getKey() 51 | .(FieldAccess) 52 | .getField() 53 | .hasQualifiedName("javax.management.remote.rmi", "RMIConnectorServer", 54 | ["CREDENTIAL_TYPES", "CREDENTIALS_FILTER_PATTERN"]) 55 | | 56 | put.getQualifier() = qualifier and 57 | put.getMethod().(MapMethod).getReceiverKeyType() instanceof TypeString and 58 | put.getMethod().(MapMethod).getReceiverValueType() instanceof TypeObject 59 | ) 60 | } 61 | } 62 | 63 | /** Gets a string describing why the application is vulnerable, depending on if the vulnerability is present due to a) a null environment b) an insecurely set environment map */ 64 | string getRmiResult(Expr e) { 65 | // We got a Map so we have a source and a sink node 66 | if e instanceof NullLiteral 67 | then 68 | result = 69 | "RMI/JMX server initialized with a null environment. Missing type restriction in RMI authentication method exposes the application to deserialization attacks." 70 | else 71 | result = 72 | "RMI/JMX server initialized with insecure environment $@, which never restricts accepted client objects to 'java.lang.String'. This exposes to deserialization attacks against the RMI authentication method." 73 | } 74 | 75 | from Call c, Expr envArg 76 | where 77 | (isRmiOrJmxServerCreateConstructor(c.getCallee()) or isRmiOrJmxServerCreateMethod(c.getCallee())) and 78 | envArg = c.getArgument(1) and 79 | not any(SafeFlow conf).hasFlowToExpr(envArg) 80 | select c, getRmiResult(envArg), envArg, envArg.toString() 81 | -------------------------------------------------------------------------------- /plugins/java/InsecureSpringActuatorConfig.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.configfiles.ConfigFiles 3 | import semmle.code.xml.MavenPom 4 | 5 | 6 | /** The parent node of the `org.springframework.boot` group. */ 7 | class SpringBootParent extends Parent { 8 | SpringBootParent() { this.getGroup().getValue() = "org.springframework.boot" } 9 | } 10 | 11 | /** Class of Spring Boot dependencies. */ 12 | class SpringBootPom extends Pom { 13 | SpringBootPom() { this.getParentElement() instanceof SpringBootParent } 14 | 15 | /** Holds if the Spring Boot Actuator module `spring-boot-starter-actuator` is used in the project. */ 16 | predicate isSpringBootActuatorUsed() { 17 | this.getADependency().getArtifact().getValue() = "spring-boot-starter-actuator" 18 | } 19 | 20 | /** 21 | * Holds if the Spring Boot Security module is used in the project, which brings in other security 22 | * related libraries. 23 | */ 24 | predicate isSpringBootSecurityUsed() { 25 | this.getADependency().getArtifact().getValue() = "spring-boot-starter-security" 26 | } 27 | } 28 | 29 | /** The properties file `application.properties`. */ 30 | class ApplicationProperties extends ConfigPair { 31 | ApplicationProperties() { this.getFile().getBaseName() = "application.properties" } 32 | } 33 | 34 | /** The configuration property `management.security.enabled`. */ 35 | class ManagementSecurityConfig extends ApplicationProperties { 36 | ManagementSecurityConfig() { this.getNameElement().getName() = "management.security.enabled" } 37 | 38 | /** Gets the whitespace-trimmed value of this property. */ 39 | string getValue() { result = this.getValueElement().getValue().trim() } 40 | 41 | /** Holds if `management.security.enabled` is set to `false`. */ 42 | predicate hasSecurityDisabled() { this.getValue() = "false" } 43 | 44 | /** Holds if `management.security.enabled` is set to `true`. */ 45 | predicate hasSecurityEnabled() { this.getValue() = "true" } 46 | } 47 | 48 | /** The configuration property `management.endpoints.web.exposure.include`. */ 49 | class ManagementEndPointInclude extends ApplicationProperties { 50 | ManagementEndPointInclude() { 51 | this.getNameElement().getName() = "management.endpoints.web.exposure.include" 52 | } 53 | 54 | /** Gets the whitespace-trimmed value of this property. */ 55 | string getValue() { result = this.getValueElement().getValue().trim() } 56 | } 57 | 58 | /** 59 | * Holds if `ApplicationProperties` ap of a repository managed by `SpringBootPom` pom 60 | * has a vulnerable configuration of Spring Boot Actuator management endpoints. 61 | */ 62 | predicate hasConfidentialEndPointExposed(SpringBootPom pom, ApplicationProperties ap) { 63 | pom.isSpringBootActuatorUsed() and 64 | not pom.isSpringBootSecurityUsed() and 65 | ap.getFile() 66 | .getParentContainer() 67 | .getAbsolutePath() 68 | .matches(pom.getFile().getParentContainer().getAbsolutePath() + "%") and // in the same sub-directory 69 | exists(string springBootVersion | springBootVersion = pom.getParentElement().getVersionString() | 70 | springBootVersion.regexpMatch("1\\.[0-4].*") and // version 1.0, 1.1, ..., 1.4 71 | not exists(ManagementSecurityConfig me | 72 | me.hasSecurityEnabled() and me.getFile() = ap.getFile() 73 | ) 74 | or 75 | springBootVersion.matches("1.5%") and // version 1.5 76 | exists(ManagementSecurityConfig me | me.hasSecurityDisabled() and me.getFile() = ap.getFile()) 77 | or 78 | springBootVersion.matches("2.%") and //version 2.x 79 | exists(ManagementEndPointInclude mi | 80 | mi.getFile() = ap.getFile() and 81 | ( 82 | mi.getValue() = "*" // all endpoints are enabled 83 | or 84 | mi.getValue() 85 | .matches([ 86 | "%dump%", "%trace%", "%logfile%", "%shutdown%", "%startup%", "%mappings%", "%env%", 87 | "%beans%", "%sessions%" 88 | ]) // confidential endpoints to check although all endpoints apart from '/health' and '/info' are considered sensitive by Spring 89 | ) 90 | ) 91 | ) 92 | } 93 | 94 | from SpringBootPom pom, ApplicationProperties ap, Dependency d 95 | where 96 | hasConfidentialEndPointExposed(pom, ap) and 97 | d = pom.getADependency() and 98 | d.getArtifact().getValue() = "spring-boot-starter-actuator" 99 | select d, "Insecure configuration of Spring Boot Actuator exposes sensitive endpoints." 100 | -------------------------------------------------------------------------------- /plugins/java/InsecureTomcatConfig.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.xml.WebXML 3 | 4 | 5 | private class HttpOnlyConfig extends WebContextParameter { 6 | HttpOnlyConfig() { this.getParamName().getValue() = "useHttpOnly" } 7 | 8 | string getParamValueElementValue() { result = this.getParamValue().getValue() } 9 | 10 | predicate isHttpOnlySet() { this.getParamValueElementValue().toLowerCase() = "false" } 11 | } 12 | 13 | from HttpOnlyConfig config 14 | where config.isHttpOnlySet() 15 | select config, 16 | "'httpOnly' should be enabled in tomcat config file to help mitigate cross-site scripting (XSS) attacks." 17 | -------------------------------------------------------------------------------- /plugins/java/InsecureWebResourceResponse.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.controlflow.Guards 3 | import semmle.code.java.dataflow.FlowSources 4 | import semmle.code.java.dataflow.TaintTracking 5 | import semmle.code.java.security.PathSanitizer 6 | import DataFlow::PathGraph 7 | 8 | private import semmle.code.java.dataflow.DataFlow 9 | private import semmle.code.java.dataflow.ExternalFlow 10 | private import semmle.code.java.dataflow.FlowSteps 11 | private import semmle.code.java.frameworks.android.WebView 12 | 13 | /** 14 | * The Android class `android.webkit.WebResourceRequest` for handling web requests. 15 | */ 16 | class WebResourceRequest extends RefType { 17 | WebResourceRequest() { this.hasQualifiedName("android.webkit", "WebResourceRequest") } 18 | } 19 | 20 | /** 21 | * The Android class `android.webkit.WebResourceResponse` for rendering web responses. 22 | */ 23 | class WebResourceResponse extends RefType { 24 | WebResourceResponse() { this.hasQualifiedName("android.webkit", "WebResourceResponse") } 25 | } 26 | 27 | /** The `shouldInterceptRequest` method of a class implementing `WebViewClient`. */ 28 | class ShouldInterceptRequestMethod extends Method { 29 | ShouldInterceptRequestMethod() { 30 | this.hasName("shouldInterceptRequest") and 31 | this.getDeclaringType().getASupertype*() instanceof TypeWebViewClient 32 | } 33 | } 34 | 35 | /** A method call to `WebView.setWebViewClient`. */ 36 | class SetWebViewClientMethodAccess extends MethodAccess { 37 | SetWebViewClientMethodAccess() { 38 | this.getMethod().hasName("setWebViewClient") and 39 | this.getMethod().getDeclaringType().getASupertype*() instanceof TypeWebView 40 | } 41 | } 42 | 43 | /** A sink representing the data argument of a call to the constructor of `WebResourceResponse`. */ 44 | class WebResourceResponseSink extends DataFlow::Node { 45 | WebResourceResponseSink() { 46 | exists(ConstructorCall cc | 47 | cc.getConstructedType() instanceof WebResourceResponse and 48 | ( 49 | this.asExpr() = cc.getArgument(2) and cc.getNumArgument() = 3 // WebResourceResponse(String mimeType, String encoding, InputStream data) 50 | or 51 | this.asExpr() = cc.getArgument(5) and cc.getNumArgument() = 6 // WebResourceResponse(String mimeType, String encoding, int statusCode, String reasonPhrase, Map responseHeaders, InputStream data) 52 | ) 53 | ) 54 | } 55 | } 56 | 57 | /** 58 | * A value step from the URL argument of `WebView::loadUrl` to the URL parameter of 59 | * `WebViewClient::shouldInterceptRequest`. 60 | */ 61 | private class FetchUrlStep extends AdditionalValueStep { 62 | override predicate step(DataFlow::Node pred, DataFlow::Node succ) { 63 | exists( 64 | // webview.loadUrl(url) -> webview.setWebViewClient(new WebViewClient() { shouldInterceptRequest(view, url) }); 65 | MethodAccess lma, ShouldInterceptRequestMethod im, SetWebViewClientMethodAccess sma 66 | | 67 | sma.getArgument(0).getType() = im.getDeclaringType().getASupertype*() and 68 | lma.getMethod() instanceof WebViewLoadUrlMethod and 69 | lma.getQualifier().getType() = sma.getQualifier().getType() and 70 | pred.asExpr() = lma.getArgument(0) and 71 | succ.asParameter() = im.getParameter(1) 72 | ) 73 | } 74 | } 75 | 76 | /** Value/taint steps relating to url loading and file reading in an Android application. */ 77 | private class LoadUrlSummaries extends SummaryModelCsv { 78 | override predicate row(string row) { 79 | row = 80 | [ 81 | "java.io;FileInputStream;true;FileInputStream;;;Argument[0];Argument[-1];taint;manual", 82 | "android.webkit;WebResourceRequest;false;getUrl;;;Argument[-1];ReturnValue;taint;manual" 83 | ] 84 | } 85 | } 86 | 87 | 88 | 89 | class InsecureWebResourceResponseConfig extends TaintTracking::Configuration { 90 | InsecureWebResourceResponseConfig() { this = "InsecureWebResourceResponseConfig" } 91 | 92 | override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } 93 | 94 | override predicate isSink(DataFlow::Node sink) { sink instanceof WebResourceResponseSink } 95 | 96 | override predicate isSanitizer(DataFlow::Node node) { node instanceof PathInjectionSanitizer } 97 | } 98 | 99 | from DataFlow::PathNode source, DataFlow::PathNode sink, InsecureWebResourceResponseConfig conf 100 | where conf.hasFlowPath(source, sink) 101 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 102 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Leaking arbitrary content in Android" 103 | -------------------------------------------------------------------------------- /plugins/java/InsufficientKeySize.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.security.Encryption 3 | import semmle.code.java.dataflow.TaintTracking 4 | 5 | 6 | /** The Java class `java.security.spec.ECGenParameterSpec`. */ 7 | class ECGenParameterSpec extends RefType { 8 | ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") } 9 | } 10 | 11 | /** The `init` method declared in `javax.crypto.KeyGenerator`. */ 12 | class KeyGeneratorInitMethod extends Method { 13 | KeyGeneratorInitMethod() { 14 | this.getDeclaringType() instanceof KeyGenerator and 15 | this.hasName("init") 16 | } 17 | } 18 | 19 | /** The `initialize` method declared in `java.security.KeyPairGenerator`. */ 20 | class KeyPairGeneratorInitMethod extends Method { 21 | KeyPairGeneratorInitMethod() { 22 | this.getDeclaringType() instanceof KeyPairGenerator and 23 | this.hasName("initialize") 24 | } 25 | } 26 | 27 | /** Returns the key size in the EC algorithm string */ 28 | bindingset[algorithm] 29 | int getECKeySize(string algorithm) { 30 | algorithm.matches("sec%") and // specification such as "secp256r1" 31 | result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt() 32 | or 33 | algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2" 34 | result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt() 35 | or 36 | (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2" 37 | result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt() 38 | } 39 | 40 | /** Taint configuration tracking flow from a key generator to a `init` method call. */ 41 | class KeyGeneratorInitConfiguration extends TaintTracking::Configuration { 42 | KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" } 43 | 44 | override predicate isSource(DataFlow::Node source) { 45 | source.asExpr() instanceof JavaxCryptoKeyGenerator 46 | } 47 | 48 | override predicate isSink(DataFlow::Node sink) { 49 | exists(MethodAccess ma | 50 | ma.getMethod() instanceof KeyGeneratorInitMethod and 51 | sink.asExpr() = ma.getQualifier() 52 | ) 53 | } 54 | } 55 | 56 | /** Taint configuration tracking flow from a keypair generator to a `initialize` method call. */ 57 | class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration { 58 | KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" } 59 | 60 | override predicate isSource(DataFlow::Node source) { 61 | source.asExpr() instanceof JavaSecurityKeyPairGenerator 62 | } 63 | 64 | override predicate isSink(DataFlow::Node sink) { 65 | exists(MethodAccess ma | 66 | ma.getMethod() instanceof KeyPairGeneratorInitMethod and 67 | sink.asExpr() = ma.getQualifier() 68 | ) 69 | } 70 | } 71 | 72 | /** Holds if a symmetric `KeyGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */ 73 | bindingset[type] 74 | predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) { 75 | ma.getMethod() instanceof KeyGeneratorInitMethod and 76 | exists( 77 | JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source, 78 | DataFlow::PathNode dest 79 | | 80 | jcg.getAlgoSpec().(StringLiteral).getValue() = type and 81 | source.getNode().asExpr() = jcg and 82 | dest.getNode().asExpr() = ma.getQualifier() and 83 | cc.hasFlowPath(source, dest) 84 | ) and 85 | ma.getArgument(0).(IntegerLiteral).getIntValue() < 128 and 86 | msg = "Key size should be at least 128 bits for " + type + " encryption." 87 | } 88 | 89 | /** Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */ 90 | predicate hasShortAESKey(MethodAccess ma, string msg) { hasShortSymmetricKey(ma, msg, "AES") } 91 | 92 | /** Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */ 93 | bindingset[type] 94 | predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) { 95 | ma.getMethod() instanceof KeyPairGeneratorInitMethod and 96 | exists( 97 | JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc, 98 | DataFlow::PathNode source, DataFlow::PathNode dest 99 | | 100 | jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and 101 | source.getNode().asExpr() = jpg and 102 | dest.getNode().asExpr() = ma.getQualifier() and 103 | kc.hasFlowPath(source, dest) 104 | ) and 105 | ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and 106 | msg = "Key size should be at least 2048 bits for " + type + " encryption." 107 | } 108 | 109 | /** Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */ 110 | predicate hasShortDsaKeyPair(MethodAccess ma, string msg) { 111 | hasShortAsymmetricKeyPair(ma, msg, "DSA") or hasShortAsymmetricKeyPair(ma, msg, "DH") 112 | } 113 | 114 | /** Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */ 115 | predicate hasShortRsaKeyPair(MethodAccess ma, string msg) { 116 | hasShortAsymmetricKeyPair(ma, msg, "RSA") 117 | } 118 | 119 | /** Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */ 120 | predicate hasShortECKeyPair(MethodAccess ma, string msg) { 121 | ma.getMethod() instanceof KeyPairGeneratorInitMethod and 122 | exists( 123 | JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc, 124 | DataFlow::PathNode source, DataFlow::PathNode dest, ClassInstanceExpr cie 125 | | 126 | jpg.getAlgoSpec().(StringLiteral).getValue().matches("EC%") and // ECC variants such as ECDH and ECDSA 127 | source.getNode().asExpr() = jpg and 128 | dest.getNode().asExpr() = ma.getQualifier() and 129 | kc.hasFlowPath(source, dest) and 130 | DataFlow::localExprFlow(cie, ma.getArgument(0)) and 131 | ma.getArgument(0).getType() instanceof ECGenParameterSpec and 132 | getECKeySize(cie.getArgument(0).(StringLiteral).getValue()) < 256 133 | ) and 134 | msg = "Key size should be at least 256 bits for EC encryption." 135 | } 136 | 137 | from Expr e, string msg 138 | where 139 | hasShortAESKey(e, msg) or 140 | hasShortDsaKeyPair(e, msg) or 141 | hasShortRsaKeyPair(e, msg) or 142 | hasShortECKeyPair(e, msg) 143 | select e, msg 144 | -------------------------------------------------------------------------------- /plugins/java/JShellInjection.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import DataFlow::PathGraph 4 | 5 | 6 | /** A sink for JShell expression injection vulnerabilities. */ 7 | class JShellInjectionSink extends DataFlow::Node { 8 | JShellInjectionSink() { 9 | this.asExpr() = any(JShellEvalCall jsec).getArgument(0) 10 | or 11 | this.asExpr() = any(SourceCodeAnalysisWrappersCall scawc).getArgument(0) 12 | } 13 | } 14 | 15 | /** A call to `JShell.eval`. */ 16 | private class JShellEvalCall extends MethodAccess { 17 | JShellEvalCall() { 18 | this.getMethod().hasName("eval") and 19 | this.getMethod().getDeclaringType().hasQualifiedName("jdk.jshell", "JShell") and 20 | this.getMethod().getNumberOfParameters() = 1 21 | } 22 | } 23 | 24 | /** A call to `SourceCodeAnalysis.wrappers`. */ 25 | private class SourceCodeAnalysisWrappersCall extends MethodAccess { 26 | SourceCodeAnalysisWrappersCall() { 27 | this.getMethod().hasName("wrappers") and 28 | this.getMethod().getDeclaringType().hasQualifiedName("jdk.jshell", "SourceCodeAnalysis") and 29 | this.getMethod().getNumberOfParameters() = 1 30 | } 31 | } 32 | 33 | /** A call to `SourceCodeAnalysis.analyzeCompletion`. */ 34 | class SourceCodeAnalysisAnalyzeCompletionCall extends MethodAccess { 35 | SourceCodeAnalysisAnalyzeCompletionCall() { 36 | this.getMethod().hasName("analyzeCompletion") and 37 | this.getMethod() 38 | .getDeclaringType() 39 | .getAnAncestor() 40 | .hasQualifiedName("jdk.jshell", "SourceCodeAnalysis") and 41 | this.getMethod().getNumberOfParameters() = 1 42 | } 43 | } 44 | 45 | /** A call to `CompletionInfo.source` or `CompletionInfo.remaining`. */ 46 | class CompletionInfoSourceOrRemainingCall extends MethodAccess { 47 | CompletionInfoSourceOrRemainingCall() { 48 | this.getMethod().getName() in ["source", "remaining"] and 49 | this.getMethod() 50 | .getDeclaringType() 51 | .getAnAncestor() 52 | .hasQualifiedName("jdk.jshell", "SourceCodeAnalysis$CompletionInfo") and 53 | this.getMethod().getNumberOfParameters() = 0 54 | } 55 | } 56 | 57 | 58 | 59 | class JShellInjectionConfiguration extends TaintTracking::Configuration { 60 | JShellInjectionConfiguration() { this = "JShellInjectionConfiguration" } 61 | 62 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 63 | 64 | override predicate isSink(DataFlow::Node sink) { sink instanceof JShellInjectionSink } 65 | 66 | override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { 67 | exists(SourceCodeAnalysisAnalyzeCompletionCall scaacc | 68 | scaacc.getArgument(0) = pred.asExpr() and scaacc = succ.asExpr() 69 | ) 70 | or 71 | exists(CompletionInfoSourceOrRemainingCall cisorc | 72 | cisorc.getQualifier() = pred.asExpr() and cisorc = succ.asExpr() 73 | ) 74 | } 75 | } 76 | 77 | from DataFlow::PathNode source, DataFlow::PathNode sink, JShellInjectionConfiguration conf 78 | where conf.hasFlowPath(source, sink) 79 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 80 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "JShell injection " 81 | -------------------------------------------------------------------------------- /plugins/java/JakartaExpressionInjection.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.dataflow.TaintTracking 4 | import DataFlow::PathGraph 5 | 6 | 7 | /** 8 | * Holds if `fromNode` to `toNode` is a dataflow step that returns data from 9 | * a bean by calling one of its getters. 10 | */ 11 | predicate hasGetterFlow(DataFlow::Node fromNode, DataFlow::Node toNode) { 12 | exists(MethodAccess ma, Method m | ma.getMethod() = m | 13 | m instanceof GetterMethod and 14 | ma.getQualifier() = fromNode.asExpr() and 15 | ma = toNode.asExpr() 16 | ) 17 | } 18 | 19 | 20 | 21 | /** 22 | * A taint-tracking configuration for unsafe user input 23 | * that is used to construct and evaluate an expression. 24 | */ 25 | class JakartaExpressionInjectionConfig extends TaintTracking::Configuration { 26 | JakartaExpressionInjectionConfig() { this = "JakartaExpressionInjectionConfig" } 27 | 28 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 29 | 30 | override predicate isSink(DataFlow::Node sink) { sink instanceof ExpressionEvaluationSink } 31 | 32 | override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { 33 | any(TaintPropagatingCall c).taintFlow(fromNode, toNode) or 34 | hasGetterFlow(fromNode, toNode) 35 | } 36 | } 37 | 38 | /** 39 | * A sink for Expresssion Language injection vulnerabilities, 40 | * i.e. method calls that run evaluation of an expression. 41 | */ 42 | private class ExpressionEvaluationSink extends DataFlow::ExprNode { 43 | ExpressionEvaluationSink() { 44 | exists(MethodAccess ma, Method m, Expr taintFrom | 45 | ma.getMethod() = m and taintFrom = this.asExpr() 46 | | 47 | m.getDeclaringType() instanceof ValueExpression and 48 | m.hasName(["getValue", "setValue"]) and 49 | ma.getQualifier() = taintFrom 50 | or 51 | m.getDeclaringType() instanceof MethodExpression and 52 | m.hasName("invoke") and 53 | ma.getQualifier() = taintFrom 54 | or 55 | m.getDeclaringType() instanceof LambdaExpression and 56 | m.hasName("invoke") and 57 | ma.getQualifier() = taintFrom 58 | or 59 | m.getDeclaringType() instanceof ELProcessor and 60 | m.hasName(["eval", "getValue", "setValue"]) and 61 | ma.getArgument(0) = taintFrom 62 | or 63 | m.getDeclaringType() instanceof ELProcessor and 64 | m.hasName("setVariable") and 65 | ma.getArgument(1) = taintFrom 66 | ) 67 | } 68 | } 69 | 70 | /** 71 | * Defines method calls that propagate tainted expressions. 72 | */ 73 | private class TaintPropagatingCall extends Call { 74 | Expr taintFromExpr; 75 | 76 | TaintPropagatingCall() { 77 | taintFromExpr = this.getArgument(1) and 78 | ( 79 | exists(Method m | this.(MethodAccess).getMethod() = m | 80 | m.getDeclaringType() instanceof ExpressionFactory and 81 | m.hasName(["createValueExpression", "createMethodExpression"]) and 82 | taintFromExpr.getType() instanceof TypeString 83 | ) 84 | or 85 | exists(Constructor c | this.(ConstructorCall).getConstructor() = c | 86 | c.getDeclaringType() instanceof LambdaExpression and 87 | taintFromExpr.getType() instanceof ValueExpression 88 | ) 89 | ) 90 | } 91 | 92 | /** 93 | * Holds if `fromNode` to `toNode` is a dataflow step that propagates 94 | * tainted data. 95 | */ 96 | predicate taintFlow(DataFlow::Node fromNode, DataFlow::Node toNode) { 97 | fromNode.asExpr() = taintFromExpr and toNode.asExpr() = this 98 | } 99 | } 100 | 101 | private class JakartaType extends RefType { 102 | JakartaType() { this.getPackage().hasName(["javax.el", "jakarta.el"]) } 103 | } 104 | 105 | private class ELProcessor extends JakartaType { 106 | ELProcessor() { this.hasName("ELProcessor") } 107 | } 108 | 109 | private class ExpressionFactory extends JakartaType { 110 | ExpressionFactory() { this.hasName("ExpressionFactory") } 111 | } 112 | 113 | private class ValueExpression extends JakartaType { 114 | ValueExpression() { this.hasName("ValueExpression") } 115 | } 116 | 117 | private class MethodExpression extends JakartaType { 118 | MethodExpression() { this.hasName("MethodExpression") } 119 | } 120 | 121 | private class LambdaExpression extends JakartaType { 122 | LambdaExpression() { this.hasName("LambdaExpression") } 123 | } 124 | 125 | 126 | 127 | from DataFlow::PathNode source, DataFlow::PathNode sink, JakartaExpressionInjectionConfig conf 128 | where conf.hasFlowPath(source, sink) 129 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 130 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Jakarta Expression Language injection", 131 | source.getNode(), "this user input" 132 | -------------------------------------------------------------------------------- /plugins/java/JxBrowserWithoutCertValidation.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.security.Encryption 3 | import semmle.code.java.dataflow.DataFlow 4 | 5 | 6 | /* 7 | * This query is version specific to JxBrowser < 6.24. The version is indirectly detected. 8 | * In version 6.x.x the `Browser` class is in a different package compared to version 7.x.x. 9 | */ 10 | 11 | /** 12 | * Holds if a safe JxBrowser 6.x.x version is used, such as version 6.24. 13 | * This is detected by the the presence of the `addBoundsListener` in the `Browser` class. 14 | */ 15 | private predicate isSafeJxBrowserVersion() { 16 | exists(Method m | m.getDeclaringType() instanceof JxBrowser | m.hasName("addBoundsListener")) 17 | } 18 | 19 | /** The `com.teamdev.jxbrowser.chromium.Browser` class. */ 20 | private class JxBrowser extends RefType { 21 | JxBrowser() { this.hasQualifiedName("com.teamdev.jxbrowser.chromium", "Browser") } 22 | } 23 | 24 | /** The `setLoadHandler` method on the `com.teamdev.jxbrowser.chromium.Browser` class. */ 25 | private class JxBrowserSetLoadHandler extends Method { 26 | JxBrowserSetLoadHandler() { 27 | this.hasName("setLoadHandler") and this.getDeclaringType() instanceof JxBrowser 28 | } 29 | } 30 | 31 | /** The `com.teamdev.jxbrowser.chromium.LoadHandler` interface. */ 32 | private class JxBrowserLoadHandler extends RefType { 33 | JxBrowserLoadHandler() { this.hasQualifiedName("com.teamdev.jxbrowser.chromium", "LoadHandler") } 34 | } 35 | 36 | private predicate isOnCertificateErrorMethodSafe(Method m) { 37 | forex(ReturnStmt rs | rs.getEnclosingCallable() = m | 38 | rs.getResult().(CompileTimeConstantExpr).getBooleanValue() = true 39 | ) 40 | } 41 | 42 | /** A class that securely implements the `com.teamdev.jxbrowser.chromium.LoadHandler` interface. */ 43 | private class JxBrowserSafeLoadHandler extends RefType { 44 | JxBrowserSafeLoadHandler() { 45 | this.getASupertype() instanceof JxBrowserLoadHandler and 46 | exists(Method m | m.hasName("onCertificateError") and m.getDeclaringType() = this | 47 | isOnCertificateErrorMethodSafe(m) 48 | ) 49 | } 50 | } 51 | 52 | /** 53 | * Models flow from the source `new Browser()` to a sink `browser.setLoadHandler(loadHandler)` where `loadHandler` 54 | * has been determined to be safe. 55 | */ 56 | private class JxBrowserFlowConfiguration extends DataFlow::Configuration { 57 | JxBrowserFlowConfiguration() { this = "JxBrowserFlowConfiguration" } 58 | 59 | override predicate isSource(DataFlow::Node src) { 60 | exists(ClassInstanceExpr newJxBrowser | newJxBrowser.getConstructedType() instanceof JxBrowser | 61 | newJxBrowser = src.asExpr() 62 | ) 63 | } 64 | 65 | override predicate isSink(DataFlow::Node sink) { 66 | exists(MethodAccess ma | ma.getMethod() instanceof JxBrowserSetLoadHandler | 67 | ma.getArgument(0).getType() instanceof JxBrowserSafeLoadHandler and 68 | ma.getQualifier() = sink.asExpr() 69 | ) 70 | } 71 | } 72 | 73 | from JxBrowserFlowConfiguration cfg, DataFlow::Node src 74 | where 75 | cfg.isSource(src) and 76 | not cfg.hasFlow(src, _) and 77 | not isSafeJxBrowserVersion() 78 | select src, "This JxBrowser instance may not check HTTPS certificates." 79 | -------------------------------------------------------------------------------- /plugins/java/JythonInjection.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.frameworks.spring.SpringController 4 | import DataFlow::PathGraph 5 | 6 | 7 | /** The class `org.python.util.PythonInterpreter`. */ 8 | class PythonInterpreter extends RefType { 9 | PythonInterpreter() { this.hasQualifiedName("org.python.util", "PythonInterpreter") } 10 | } 11 | 12 | /** A method that evaluates, compiles or executes a Jython expression. */ 13 | class InterpretExprMethod extends Method { 14 | InterpretExprMethod() { 15 | this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and 16 | this.getName().matches(["exec%", "run%", "eval", "compile"]) 17 | } 18 | } 19 | 20 | /** The class `org.python.core.BytecodeLoader`. */ 21 | class BytecodeLoader extends RefType { 22 | BytecodeLoader() { this.hasQualifiedName("org.python.core", "BytecodeLoader") } 23 | } 24 | 25 | /** Holds if a Jython expression if evaluated, compiled or executed. */ 26 | predicate runsCode(MethodAccess ma, Expr sink) { 27 | exists(Method m | m = ma.getMethod() | 28 | m instanceof InterpretExprMethod and 29 | sink = ma.getArgument(0) 30 | ) 31 | } 32 | 33 | /** A method that loads Java class data. */ 34 | class LoadClassMethod extends Method { 35 | LoadClassMethod() { 36 | this.getDeclaringType().getAnAncestor*() instanceof BytecodeLoader and 37 | this.hasName(["makeClass", "makeCode"]) 38 | } 39 | } 40 | 41 | /** 42 | * Holds if `ma` is a call to a class-loading method, and `sink` is the byte array 43 | * representing the class to be loaded. 44 | */ 45 | predicate loadsClass(MethodAccess ma, Expr sink) { 46 | exists(Method m, int i | m = ma.getMethod() | 47 | m instanceof LoadClassMethod and 48 | m.getParameter(i).getType() instanceof Array and // makeClass(java.lang.String name, byte[] data, ...) 49 | sink = ma.getArgument(i) 50 | ) 51 | } 52 | 53 | /** The class `org.python.core.Py`. */ 54 | class Py extends RefType { 55 | Py() { this.hasQualifiedName("org.python.core", "Py") } 56 | } 57 | 58 | /** A method declared on class `Py` or one of its descendants that compiles Python code. */ 59 | class PyCompileMethod extends Method { 60 | PyCompileMethod() { 61 | this.getDeclaringType().getAnAncestor*() instanceof Py and 62 | this.getName().matches("compile%") 63 | } 64 | } 65 | 66 | /** Holds if source code is compiled with `PyCompileMethod`. */ 67 | predicate compile(MethodAccess ma, Expr sink) { 68 | exists(Method m | m = ma.getMethod() | 69 | m instanceof PyCompileMethod and 70 | sink = ma.getArgument(0) 71 | ) 72 | } 73 | 74 | /** An expression loaded by Jython. */ 75 | class CodeInjectionSink extends DataFlow::ExprNode { 76 | MethodAccess methodAccess; 77 | 78 | CodeInjectionSink() { 79 | runsCode(methodAccess, this.getExpr()) or 80 | loadsClass(methodAccess, this.getExpr()) or 81 | compile(methodAccess, this.getExpr()) 82 | } 83 | 84 | MethodAccess getMethodAccess() { result = methodAccess } 85 | } 86 | 87 | /** 88 | * A taint configuration for tracking flow from `RemoteFlowSource` to a Jython method call 89 | * `CodeInjectionSink` that executes injected code. 90 | */ 91 | class CodeInjectionConfiguration extends TaintTracking::Configuration { 92 | CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } 93 | 94 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 95 | 96 | override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } 97 | } 98 | 99 | from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf 100 | where conf.hasFlowPath(source, sink) 101 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 102 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Jython evaluate" 103 | -------------------------------------------------------------------------------- /plugins/java/LocalThreadResourceAbuse.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.DataFlow 3 | private import semmle.code.java.dataflow.ExternalFlow 4 | import semmle.code.java.dataflow.FlowSteps 5 | import semmle.code.java.controlflow.Guards 6 | import semmle.code.java.dataflow.FlowSources 7 | import DataFlow::PathGraph 8 | 9 | 10 | /** `java.lang.Math` data model for value comparison in the new CSV format. */ 11 | private class MathCompDataModel extends SummaryModelCsv { 12 | override predicate row(string row) { 13 | row = 14 | [ 15 | "java.lang;Math;false;min;;;Argument[0..1];ReturnValue;value;manual", 16 | "java.lang;Math;false;max;;;Argument[0..1];ReturnValue;value;manual" 17 | ] 18 | } 19 | } 20 | 21 | /** Thread pause data model in the new CSV format. */ 22 | private class PauseThreadDataModel extends SinkModelCsv { 23 | override predicate row(string row) { 24 | row = 25 | [ 26 | "java.lang;Thread;true;sleep;;;Argument[0];thread-pause;manual", 27 | "java.util.concurrent;TimeUnit;true;sleep;;;Argument[0];thread-pause;manual" 28 | ] 29 | } 30 | } 31 | 32 | /** A sink representing methods pausing a thread. */ 33 | class PauseThreadSink extends DataFlow::Node { 34 | PauseThreadSink() { sinkNode(this, "thread-pause") } 35 | } 36 | 37 | private predicate lessThanGuard(Guard g, Expr e, boolean branch) { 38 | e = g.(ComparisonExpr).getLesserOperand() and 39 | branch = true 40 | or 41 | e = g.(ComparisonExpr).getGreaterOperand() and 42 | branch = false 43 | } 44 | 45 | /** A sanitizer for lessThan check. */ 46 | class LessThanSanitizer extends DataFlow::Node { 47 | LessThanSanitizer() { this = DataFlow::BarrierGuard::getABarrierNode() } 48 | } 49 | 50 | /** Value step from the constructor call of a `Runnable` to the instance parameter (this) of `run`. */ 51 | private class RunnableStartToRunStep extends AdditionalValueStep { 52 | override predicate step(DataFlow::Node pred, DataFlow::Node succ) { 53 | exists(ConstructorCall cc, Method m | 54 | m.getDeclaringType() = cc.getConstructedType().getSourceDeclaration() and 55 | cc.getConstructedType().getAnAncestor().hasQualifiedName("java.lang", "Runnable") and 56 | m.hasName("run") 57 | | 58 | pred.asExpr() = cc and 59 | succ.(DataFlow::InstanceParameterNode).getEnclosingCallable() = m 60 | ) 61 | } 62 | } 63 | 64 | /** 65 | * Value step from the constructor call of a `ProgressListener` of Apache File Upload to the 66 | * instance parameter (this) of `update`. 67 | */ 68 | private class ApacheFileUploadProgressUpdateStep extends AdditionalValueStep { 69 | override predicate step(DataFlow::Node pred, DataFlow::Node succ) { 70 | exists(ConstructorCall cc, Method m | 71 | m.getDeclaringType() = cc.getConstructedType().getSourceDeclaration() and 72 | cc.getConstructedType() 73 | .getAnAncestor() 74 | .hasQualifiedName(["org.apache.commons.fileupload", "org.apache.commons.fileupload2"], 75 | "ProgressListener") and 76 | m.hasName("update") 77 | | 78 | pred.asExpr() = cc and 79 | succ.(DataFlow::InstanceParameterNode).getEnclosingCallable() = m 80 | ) 81 | } 82 | } 83 | 84 | 85 | 86 | /** The `getInitParameter` method of servlet or JSF. */ 87 | class GetInitParameter extends Method { 88 | GetInitParameter() { 89 | ( 90 | this.getDeclaringType() 91 | .getAnAncestor() 92 | .hasQualifiedName(["javax.servlet", "jakarta.servlet"], 93 | ["FilterConfig", "Registration", "ServletConfig", "ServletContext"]) or 94 | this.getDeclaringType() 95 | .getAnAncestor() 96 | .hasQualifiedName(["javax.faces.context", "jakarta.faces.context"], "ExternalContext") 97 | ) and 98 | this.getName() = "getInitParameter" 99 | } 100 | } 101 | 102 | /** An access to the `getInitParameter` method. */ 103 | class GetInitParameterAccess extends MethodAccess { 104 | GetInitParameterAccess() { this.getMethod() instanceof GetInitParameter } 105 | } 106 | 107 | /* Init parameter input of a Java EE web application. */ 108 | class InitParameterInput extends LocalUserInput { 109 | InitParameterInput() { this.asExpr() instanceof GetInitParameterAccess } 110 | } 111 | 112 | /** Taint configuration of uncontrolled thread resource consumption from local user input. */ 113 | class ThreadResourceAbuse extends TaintTracking::Configuration { 114 | ThreadResourceAbuse() { this = "ThreadResourceAbuse" } 115 | 116 | override predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput } 117 | 118 | override predicate isSink(DataFlow::Node sink) { sink instanceof PauseThreadSink } 119 | 120 | override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { 121 | any(AdditionalValueStep r).step(pred, succ) 122 | } 123 | 124 | override predicate isSanitizer(DataFlow::Node node) { 125 | exists( 126 | MethodAccess ma // Math.min(sleepTime, MAX_INTERVAL) 127 | | 128 | ma.getMethod().hasQualifiedName("java.lang", "Math", "min") and 129 | node.asExpr() = ma.getAnArgument() 130 | ) 131 | or 132 | node instanceof LessThanSanitizer // if (sleepTime > 0 && sleepTime < 5000) { ... } 133 | } 134 | } 135 | 136 | from DataFlow::PathNode source, DataFlow::PathNode sink, ThreadResourceAbuse conf 137 | where conf.hasFlowPath(source, sink) 138 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 139 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Possible uncontrolled resource consumption" -------------------------------------------------------------------------------- /plugins/java/NFEAndroidDoS.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.frameworks.android.Intent 3 | import semmle.code.java.dataflow.FlowSources 4 | import semmle.code.java.NumberFormatException 5 | import DataFlow::PathGraph 6 | 7 | 8 | /** 9 | * Taint configuration tracking flow from untrusted inputs to number conversion calls in exported Android compononents. 10 | */ 11 | class NfeLocalDoSConfiguration extends TaintTracking::Configuration { 12 | NfeLocalDoSConfiguration() { this = "NFELocalDoSConfiguration" } 13 | 14 | /** Holds if source is a remote flow source */ 15 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 16 | 17 | /** Holds if NFE is thrown but not caught */ 18 | override predicate isSink(DataFlow::Node sink) { 19 | exists(Expr e | 20 | e.getEnclosingCallable().getDeclaringType().(ExportableAndroidComponent).isExported() and 21 | throwsNfe(e) and 22 | not exists(TryStmt t | 23 | t.getBlock() = e.getAnEnclosingStmt() and 24 | catchesNfe(t) 25 | ) and 26 | sink.asExpr() = e 27 | ) 28 | } 29 | } 30 | 31 | from DataFlow::PathNode source, DataFlow::PathNode sink, NfeLocalDoSConfiguration conf 32 | where conf.hasFlowPath(source, sink) 33 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 34 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 35 | "Uncaught NumberFormatException in an exported Android component" 36 | -------------------------------------------------------------------------------- /plugins/java/OpenStream.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name openStream called on URLs created from remote source 3 | * @description Calling openStream on URLs created from remote source 4 | * can lead to local file disclosure. 5 | * @kind path-problem 6 | * @problem.severity warning 7 | * @precision medium 8 | * @id java/openstream-called-on-tainted-url 9 | * @tags security 10 | * external/cwe/cwe-036 11 | */ 12 | 13 | import java 14 | import semmle.code.java.dataflow.TaintTracking 15 | import semmle.code.java.dataflow.FlowSources 16 | import semmle.code.java.dataflow.ExternalFlow 17 | import DataFlow::PathGraph 18 | 19 | class UrlConstructor extends ClassInstanceExpr { 20 | UrlConstructor() { this.getConstructor().getDeclaringType() instanceof TypeUrl } 21 | 22 | Expr stringArg() { 23 | // Query only in URL's that were constructed by calling the single parameter string constructor. 24 | this.getConstructor().getNumberOfParameters() = 1 and 25 | this.getConstructor().getParameter(0).getType() instanceof TypeString and 26 | result = this.getArgument(0) 27 | } 28 | } 29 | 30 | class RemoteUrlToOpenStreamFlowConfig extends TaintTracking::Configuration { 31 | RemoteUrlToOpenStreamFlowConfig() { this = "OpenStream::RemoteURLToOpenStreamFlowConfig" } 32 | 33 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 34 | 35 | override predicate isSink(DataFlow::Node sink) { 36 | exists(MethodAccess m | 37 | sink.asExpr() = m.getQualifier() and (m.getMethod() instanceof UrlOpenConnectionMethod or m.getMethod() instanceof UrlOpenStreamMethod) 38 | ) 39 | or 40 | sinkNode(sink, "url-open-stream") 41 | } 42 | 43 | override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { 44 | exists(UrlConstructor u | 45 | node1.asExpr() = u.stringArg() and 46 | node2.asExpr() = u 47 | ) 48 | } 49 | } 50 | 51 | from DataFlow::PathNode source, DataFlow::PathNode sink, MethodAccess call 52 | where 53 | sink.getNode().asExpr() = call.getQualifier() and 54 | any(RemoteUrlToOpenStreamFlowConfig c).hasFlowPath(source, sink) 55 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 56 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 57 | "URL on which openStream is called may have been constructed from remote source." -------------------------------------------------------------------------------- /plugins/java/PasswordInConfigurationFile.ql: -------------------------------------------------------------------------------- 1 | import java 2 | 3 | 4 | /** Holds if the attribute value is not a cleartext password */ 5 | bindingset[value] 6 | predicate isNotPassword(string value) { 7 | value = "" // Empty string 8 | or 9 | value.regexpMatch("\\$\\{.*\\}") // Variable placeholder ${password} 10 | or 11 | value.matches("%=") // A basic check of encrypted passwords ending with padding characters, which could be improved to be more accurate. 12 | } 13 | 14 | /** Holds if the attribute value has an embedded password */ 15 | bindingset[value] 16 | predicate hasEmbeddedPassword(string value) { 17 | exists(string password | 18 | password = value.regexpCapture("(?is).*(pwd|password)\\s*=([^;:,]*).*", 2).trim() and 19 | not isNotPassword(password) 20 | ) 21 | } 22 | 23 | from XmlAttribute nameAttr 24 | where 25 | nameAttr.getName().toLowerCase() in ["password", "pwd"] and 26 | not isNotPassword(nameAttr.getValue().trim()) // Attribute name "password" or "pwd" 27 | or 28 | exists( 29 | XmlAttribute valueAttr // name/value pair like 30 | | 31 | valueAttr.getElement() = nameAttr.getElement() and 32 | nameAttr.getName().toLowerCase() = "name" and 33 | nameAttr.getValue().toLowerCase() in ["password", "pwd"] and 34 | valueAttr.getName().toLowerCase() = "value" and 35 | not isNotPassword(valueAttr.getValue().trim()) 36 | ) 37 | or 38 | hasEmbeddedPassword(nameAttr.getValue().trim()) // Attribute value matches password pattern 39 | select nameAttr, "Avoid plaintext passwords in configuration files." 40 | -------------------------------------------------------------------------------- /plugins/java/RegexInjection.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.dataflow.TaintTracking 4 | import DataFlow::PathGraph 5 | 6 | 7 | /** 8 | * A data flow sink for untrusted user input used to construct regular expressions. 9 | */ 10 | class RegexSink extends DataFlow::ExprNode { 11 | RegexSink() { 12 | exists(MethodAccess ma, Method m | m = ma.getMethod() | 13 | ( 14 | m.getDeclaringType() instanceof TypeString and 15 | ( 16 | ma.getArgument(0) = this.asExpr() and 17 | m.hasName(["matches", "split", "replaceFirst", "replaceAll"]) 18 | ) 19 | or 20 | m.getDeclaringType().hasQualifiedName("java.util.regex", "Pattern") and 21 | ( 22 | ma.getArgument(0) = this.asExpr() and 23 | m.hasName(["compile", "matches"]) 24 | ) 25 | or 26 | m.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "RegExUtils") and 27 | ( 28 | ma.getArgument(1) = this.asExpr() and 29 | m.getParameterType(1) instanceof TypeString and 30 | m.hasName([ 31 | "removeAll", "removeFirst", "removePattern", "replaceAll", "replaceFirst", 32 | "replacePattern" 33 | ]) 34 | ) 35 | ) 36 | ) 37 | } 38 | } 39 | 40 | abstract class Sanitizer extends DataFlow::ExprNode { } 41 | 42 | /** 43 | * A call to a function whose name suggests that it escapes regular 44 | * expression meta-characters. 45 | */ 46 | class RegExpSanitizationCall extends Sanitizer { 47 | RegExpSanitizationCall() { 48 | exists(string calleeName, string sanitize, string regexp | 49 | calleeName = this.asExpr().(Call).getCallee().getName() and 50 | sanitize = "(?:escape|saniti[sz]e)" and 51 | regexp = "regexp?" 52 | | 53 | calleeName 54 | .regexpMatch("(?i)(" + sanitize + ".*" + regexp + ".*)" + "|(" + regexp + ".*" + sanitize + 55 | ".*)") 56 | ) 57 | } 58 | } 59 | 60 | /** 61 | * A taint-tracking configuration for untrusted user input used to construct regular expressions. 62 | */ 63 | class RegexInjectionConfiguration extends TaintTracking::Configuration { 64 | RegexInjectionConfiguration() { this = "RegexInjectionConfiguration" } 65 | 66 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 67 | 68 | override predicate isSink(DataFlow::Node sink) { sink instanceof RegexSink } 69 | 70 | override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } 71 | } 72 | 73 | from DataFlow::PathNode source, DataFlow::PathNode sink, RegexInjectionConfiguration c 74 | where c.hasFlowPath(source, sink) 75 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 76 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "This regular expression injection" 77 | -------------------------------------------------------------------------------- /plugins/java/ScriptInjection.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import DataFlow::PathGraph 4 | 5 | 6 | /** A method of ScriptEngine that allows code injection. */ 7 | class ScriptEngineMethod extends Method { 8 | ScriptEngineMethod() { 9 | this.getDeclaringType().getAnAncestor().hasQualifiedName("javax.script", "ScriptEngine") and 10 | this.hasName("eval") 11 | or 12 | this.getDeclaringType().getAnAncestor().hasQualifiedName("javax.script", "Compilable") and 13 | this.hasName("compile") 14 | or 15 | this.getDeclaringType().getAnAncestor().hasQualifiedName("javax.script", "ScriptEngineFactory") and 16 | this.hasName(["getProgram", "getMethodCallSyntax"]) 17 | } 18 | } 19 | 20 | /** The context class `org.mozilla.javascript.Context` of Rhino Java Script Engine. */ 21 | class RhinoContext extends RefType { 22 | RhinoContext() { this.hasQualifiedName("org.mozilla.javascript", "Context") } 23 | } 24 | 25 | /** A method that evaluates a Rhino expression with `org.mozilla.javascript.Context`. */ 26 | class RhinoEvaluateExpressionMethod extends Method { 27 | RhinoEvaluateExpressionMethod() { 28 | this.getDeclaringType().getAnAncestor*() instanceof RhinoContext and 29 | this.hasName([ 30 | "evaluateString", "evaluateReader", "compileFunction", "compileReader", "compileString" 31 | ]) 32 | } 33 | } 34 | 35 | /** 36 | * A method that compiles a Rhino expression with 37 | * `org.mozilla.javascript.optimizer.ClassCompiler`. 38 | */ 39 | class RhinoCompileClassMethod extends Method { 40 | RhinoCompileClassMethod() { 41 | this.getDeclaringType() 42 | .getAnAncestor() 43 | .hasQualifiedName("org.mozilla.javascript.optimizer", "ClassCompiler") and 44 | this.hasName("compileToClassFiles") 45 | } 46 | } 47 | 48 | /** 49 | * A method that defines a Java class from a Rhino expression with 50 | * `org.mozilla.javascript.GeneratedClassLoader`. 51 | */ 52 | class RhinoDefineClassMethod extends Method { 53 | RhinoDefineClassMethod() { 54 | this.getDeclaringType() 55 | .getAnAncestor() 56 | .hasQualifiedName("org.mozilla.javascript", "GeneratedClassLoader") and 57 | this.hasName("defineClass") 58 | } 59 | } 60 | 61 | /** 62 | * Holds if `ma` is a call to a `ScriptEngineMethod` and `sink` is an argument that 63 | * will be executed. 64 | */ 65 | predicate isScriptArgument(MethodAccess ma, Expr sink) { 66 | exists(ScriptEngineMethod m | 67 | m = ma.getMethod() and 68 | if m.getDeclaringType().getAnAncestor().hasQualifiedName("javax.script", "ScriptEngineFactory") 69 | then sink = ma.getArgument(_) // all arguments allow script injection 70 | else sink = ma.getArgument(0) 71 | ) 72 | } 73 | 74 | /** 75 | * Holds if a Rhino expression evaluation method is vulnerable to code injection. 76 | */ 77 | predicate evaluatesRhinoExpression(MethodAccess ma, Expr sink) { 78 | exists(RhinoEvaluateExpressionMethod m | m = ma.getMethod() | 79 | ( 80 | if ma.getMethod().getName() = "compileReader" 81 | then sink = ma.getArgument(0) // The first argument is the input reader 82 | else sink = ma.getArgument(1) // The second argument is the JavaScript or Java input 83 | ) and 84 | not exists(MethodAccess ca | 85 | ca.getMethod().hasName(["initSafeStandardObjects", "setClassShutter"]) and // safe mode or `ClassShutter` constraint is enforced 86 | ma.getQualifier() = ca.getQualifier().(VarAccess).getVariable().getAnAccess() 87 | ) 88 | ) 89 | } 90 | 91 | /** 92 | * Holds if a Rhino expression compilation method is vulnerable to code injection. 93 | */ 94 | predicate compilesScript(MethodAccess ma, Expr sink) { 95 | exists(RhinoCompileClassMethod m | m = ma.getMethod() | sink = ma.getArgument(0)) 96 | } 97 | 98 | /** 99 | * Holds if a Rhino class loading method is vulnerable to code injection. 100 | */ 101 | predicate definesRhinoClass(MethodAccess ma, Expr sink) { 102 | exists(RhinoDefineClassMethod m | m = ma.getMethod() | sink = ma.getArgument(1)) 103 | } 104 | 105 | /** A script injection sink. */ 106 | class ScriptInjectionSink extends DataFlow::ExprNode { 107 | MethodAccess methodAccess; 108 | 109 | ScriptInjectionSink() { 110 | isScriptArgument(methodAccess, this.getExpr()) or 111 | evaluatesRhinoExpression(methodAccess, this.getExpr()) or 112 | compilesScript(methodAccess, this.getExpr()) or 113 | definesRhinoClass(methodAccess, this.getExpr()) 114 | } 115 | 116 | /** An access to the method associated with this sink. */ 117 | MethodAccess getMethodAccess() { result = methodAccess } 118 | } 119 | 120 | /** 121 | * A taint tracking configuration that tracks flow from `RemoteFlowSource` to an argument 122 | * of a method call that executes injected script. 123 | */ 124 | class ScriptInjectionConfiguration extends TaintTracking::Configuration { 125 | ScriptInjectionConfiguration() { this = "ScriptInjectionConfiguration" } 126 | 127 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 128 | 129 | override predicate isSink(DataFlow::Node sink) { sink instanceof ScriptInjectionSink } 130 | } 131 | 132 | from DataFlow::PathNode source, DataFlow::PathNode sink, ScriptInjectionConfiguration conf 133 | where conf.hasFlowPath(source, sink) 134 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 135 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 136 | "Java Script Engine evaluate" 137 | -------------------------------------------------------------------------------- /plugins/java/SensitiveGetQuery.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.dataflow.TaintTracking 4 | import semmle.code.java.security.SensitiveActions 5 | import DataFlow::PathGraph 6 | 7 | 8 | /** A variable that holds sensitive information judging by its name. */ 9 | class SensitiveInfoExpr extends Expr { 10 | SensitiveInfoExpr() { 11 | exists(Variable v | this = v.getAnAccess() | 12 | v.getName().regexpMatch(getCommonSensitiveInfoRegex()) and 13 | not v.getName().matches("token%") // exclude ^token.* since sensitive tokens are usually in the form of accessToken, authToken, ... 14 | ) 15 | } 16 | } 17 | 18 | /** Holds if `m` is a method of some override of `HttpServlet.doGet`. */ 19 | private predicate isGetServletMethod(Method m) { 20 | isServletRequestMethod(m) and m.getName() = "doGet" 21 | } 22 | 23 | /** The `doGet` method of `HttpServlet`. */ 24 | class DoGetServletMethod extends Method { 25 | DoGetServletMethod() { isGetServletMethod(this) } 26 | } 27 | 28 | /** Holds if `ma` is (perhaps indirectly) called from the `doGet` method of `HttpServlet`. */ 29 | predicate isReachableFromServletDoGet(MethodAccess ma) { 30 | ma.getEnclosingCallable() instanceof DoGetServletMethod 31 | or 32 | exists(Method pm, MethodAccess pma | 33 | ma.getEnclosingCallable() = pm and 34 | pma.getMethod() = pm and 35 | isReachableFromServletDoGet(pma) 36 | ) 37 | } 38 | 39 | /** Source of GET servlet requests. */ 40 | class RequestGetParamSource extends DataFlow::ExprNode { 41 | RequestGetParamSource() { 42 | exists(MethodAccess ma | 43 | isRequestGetParamMethod(ma) and 44 | ma = this.asExpr() and 45 | isReachableFromServletDoGet(ma) 46 | ) 47 | } 48 | } 49 | 50 | /** A taint configuration tracking flow from the `ServletRequest` of a GET request handler to an expression whose name suggests it holds security-sensitive data. */ 51 | class SensitiveGetQueryConfiguration extends TaintTracking::Configuration { 52 | SensitiveGetQueryConfiguration() { this = "SensitiveGetQueryConfiguration" } 53 | 54 | override predicate isSource(DataFlow::Node source) { source instanceof RequestGetParamSource } 55 | 56 | override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof SensitiveInfoExpr } 57 | 58 | /** Holds if the node is in a servlet method other than `doGet`. */ 59 | override predicate isSanitizer(DataFlow::Node node) { 60 | isServletRequestMethod(node.getEnclosingCallable()) and 61 | not isGetServletMethod(node.getEnclosingCallable()) 62 | } 63 | } 64 | 65 | from DataFlow::PathNode source, DataFlow::PathNode sink, SensitiveGetQueryConfiguration c 66 | where c.hasFlowPath(source, sink) 67 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 68 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 69 | "Uses the GET request method to transmit sensitive information." 70 | -------------------------------------------------------------------------------- /plugins/java/SpringBootActuators.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import java 3 | 4 | /** The class `org.springframework.security.config.annotation.web.builders.HttpSecurity`. */ 5 | class TypeHttpSecurity extends Class { 6 | TypeHttpSecurity() { 7 | this.hasQualifiedName("org.springframework.security.config.annotation.web.builders", 8 | "HttpSecurity") 9 | } 10 | } 11 | 12 | /** 13 | * The class 14 | * `org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer`. 15 | */ 16 | class TypeAuthorizedUrl extends Class { 17 | TypeAuthorizedUrl() { 18 | this.hasQualifiedName("org.springframework.security.config.annotation.web.configurers", 19 | "ExpressionUrlAuthorizationConfigurer$AuthorizedUrl<>") 20 | } 21 | } 22 | 23 | /** 24 | * The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`. 25 | */ 26 | class TypeAbstractRequestMatcherRegistry extends Class { 27 | TypeAbstractRequestMatcherRegistry() { 28 | this.hasQualifiedName("org.springframework.security.config.annotation.web", 29 | "AbstractRequestMatcherRegistry>") 30 | } 31 | } 32 | 33 | /** 34 | * The class `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest`. 35 | */ 36 | class TypeEndpointRequest extends Class { 37 | TypeEndpointRequest() { 38 | this.hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet", 39 | "EndpointRequest") 40 | } 41 | } 42 | 43 | /** A call to `EndpointRequest.toAnyEndpoint` method. */ 44 | class ToAnyEndpointCall extends MethodAccess { 45 | ToAnyEndpointCall() { 46 | this.getMethod().hasName("toAnyEndpoint") and 47 | this.getMethod().getDeclaringType() instanceof TypeEndpointRequest 48 | } 49 | } 50 | 51 | /** 52 | * A call to `HttpSecurity.requestMatcher` method with argument `RequestMatcher.toAnyEndpoint()`. 53 | */ 54 | class RequestMatcherCall extends MethodAccess { 55 | RequestMatcherCall() { 56 | this.getMethod().hasName("requestMatcher") and 57 | this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and 58 | this.getArgument(0) instanceof ToAnyEndpointCall 59 | } 60 | } 61 | 62 | /** 63 | * A call to `HttpSecurity.requestMatchers` method with lambda argument 64 | * `RequestMatcher.toAnyEndpoint()`. 65 | */ 66 | class RequestMatchersCall extends MethodAccess { 67 | RequestMatchersCall() { 68 | this.getMethod().hasName("requestMatchers") and 69 | this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and 70 | this.getArgument(0).(LambdaExpr).getExprBody() instanceof ToAnyEndpointCall 71 | } 72 | } 73 | 74 | /** A call to `HttpSecurity.authorizeRequests` method. */ 75 | class AuthorizeRequestsCall extends MethodAccess { 76 | AuthorizeRequestsCall() { 77 | this.getMethod().hasName("authorizeRequests") and 78 | this.getMethod().getDeclaringType() instanceof TypeHttpSecurity 79 | } 80 | } 81 | 82 | /** A call to `AuthorizedUrl.permitAll` method. */ 83 | class PermitAllCall extends MethodAccess { 84 | PermitAllCall() { 85 | this.getMethod().hasName("permitAll") and 86 | this.getMethod().getDeclaringType() instanceof TypeAuthorizedUrl 87 | } 88 | 89 | /** Holds if `permitAll` is called on request(s) mapped to actuator endpoint(s). */ 90 | predicate permitsSpringBootActuators() { 91 | exists(AuthorizeRequestsCall authorizeRequestsCall | 92 | // .requestMatcher(EndpointRequest).authorizeRequests([...]).[...] 93 | authorizeRequestsCall.getQualifier() instanceof RequestMatcherCall 94 | or 95 | // .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...] 96 | authorizeRequestsCall.getQualifier() instanceof RequestMatchersCall 97 | | 98 | // [...].authorizeRequests(r -> r.anyRequest().permitAll()) or 99 | // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll()) 100 | authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = this and 101 | ( 102 | this.getQualifier() instanceof AnyRequestCall or 103 | this.getQualifier() instanceof RegistryRequestMatchersCall 104 | ) 105 | or 106 | // [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or 107 | // [...].authorizeRequests().anyRequest().permitAll() 108 | authorizeRequestsCall.getNumArgument() = 0 and 109 | exists(RegistryRequestMatchersCall registryRequestMatchersCall | 110 | registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and 111 | this.getQualifier() = registryRequestMatchersCall 112 | ) 113 | or 114 | exists(AnyRequestCall anyRequestCall | 115 | anyRequestCall.getQualifier() = authorizeRequestsCall and 116 | this.getQualifier() = anyRequestCall 117 | ) 118 | ) 119 | or 120 | exists(AuthorizeRequestsCall authorizeRequestsCall | 121 | // http.authorizeRequests([...]).[...] 122 | authorizeRequestsCall.getQualifier() instanceof VarAccess 123 | | 124 | // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll()) 125 | authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = this and 126 | this.getQualifier() instanceof RegistryRequestMatchersCall 127 | or 128 | // [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or 129 | authorizeRequestsCall.getNumArgument() = 0 and 130 | exists(RegistryRequestMatchersCall registryRequestMatchersCall | 131 | registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and 132 | this.getQualifier() = registryRequestMatchersCall 133 | ) 134 | ) 135 | } 136 | } 137 | 138 | /** A call to `AbstractRequestMatcherRegistry.anyRequest` method. */ 139 | class AnyRequestCall extends MethodAccess { 140 | AnyRequestCall() { 141 | this.getMethod().hasName("anyRequest") and 142 | this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry 143 | } 144 | } 145 | 146 | /** 147 | * A call to `AbstractRequestMatcherRegistry.requestMatchers` method with an argument 148 | * `RequestMatcher.toAnyEndpoint()`. 149 | */ 150 | class RegistryRequestMatchersCall extends MethodAccess { 151 | RegistryRequestMatchersCall() { 152 | this.getMethod().hasName("requestMatchers") and 153 | this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry and 154 | this.getAnArgument() instanceof ToAnyEndpointCall 155 | } 156 | } 157 | 158 | 159 | 160 | from PermitAllCall permitAllCall 161 | where permitAllCall.permitsSpringBootActuators() 162 | select permitAllCall, "Unauthenticated access to Spring Boot actuator is allowed." 163 | -------------------------------------------------------------------------------- /plugins/java/SpringUrlRedirect.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import experimental.semmle.code.java.security.SpringUrlRedirect 3 | import semmle.code.java.dataflow.FlowSources 4 | import semmle.code.java.controlflow.Guards 5 | import DataFlow::PathGraph 6 | 7 | 8 | private predicate startsWithSanitizer(Guard g, Expr e, boolean branch) { 9 | g.(MethodAccess).getMethod().hasName("startsWith") and 10 | g.(MethodAccess).getMethod().getDeclaringType() instanceof TypeString and 11 | g.(MethodAccess).getMethod().getNumberOfParameters() = 1 and 12 | e = g.(MethodAccess).getQualifier() and 13 | branch = true 14 | } 15 | 16 | class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration { 17 | SpringUrlRedirectFlowConfig() { this = "SpringUrlRedirectFlowConfig" } 18 | 19 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 20 | 21 | override predicate isSink(DataFlow::Node sink) { sink instanceof SpringUrlRedirectSink } 22 | 23 | override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { 24 | springUrlRedirectTaintStep(fromNode, toNode) 25 | } 26 | 27 | override predicate isSanitizer(DataFlow::Node node) { 28 | // Exclude the case where the left side of the concatenated string is not `redirect:`. 29 | // E.g: `String url = "/path?token=" + request.getParameter("token");` 30 | // Note this is quite a broad sanitizer (it will also sanitize the right-hand side of `url = "http://" + request.getParameter("token")`); 31 | // Consider making this stricter in future. 32 | exists(AddExpr ae | 33 | ae.getRightOperand() = node.asExpr() and 34 | not ae instanceof RedirectBuilderExpr 35 | ) 36 | or 37 | exists(MethodAccess ma, int index | 38 | ma.getMethod().hasName("format") and 39 | ma.getMethod().getDeclaringType() instanceof TypeString and 40 | ma.getArgument(index) = node.asExpr() and 41 | ( 42 | index != 0 and 43 | not ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().regexpMatch("^%s.*") 44 | ) 45 | ) 46 | or 47 | nonLocationHeaderSanitizer(node) 48 | or 49 | node = DataFlow::BarrierGuard::getABarrierNode() 50 | } 51 | } 52 | 53 | from DataFlow::PathNode source, DataFlow::PathNode sink, SpringUrlRedirectFlowConfig conf 54 | where conf.hasFlowPath(source, sink) 55 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 56 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Potentially untrusted URL redirection" -------------------------------------------------------------------------------- /plugins/java/SpringViewManipulation.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.dataflow.TaintTracking 4 | import semmle.code.java.frameworks.spring.Spring 5 | import semmle.code.java.dataflow.DataFlow 6 | import DataFlow::PathGraph 7 | 8 | 9 | /** 10 | * `WebRequest` interface is a source of tainted data. 11 | */ 12 | class WebRequestSource extends DataFlow::Node { 13 | WebRequestSource() { 14 | exists(MethodAccess ma, Method m | ma.getMethod() = m | 15 | m.getDeclaringType() instanceof WebRequest and 16 | ( 17 | m.hasName("getHeader") or 18 | m.hasName("getHeaderValues") or 19 | m.hasName("getHeaderNames") or 20 | m.hasName("getParameter") or 21 | m.hasName("getParameterValues") or 22 | m.hasName("getParameterNames") or 23 | m.hasName("getParameterMap") 24 | ) and 25 | ma = asExpr() 26 | ) 27 | } 28 | } 29 | 30 | class WebRequest extends RefType { 31 | WebRequest() { hasQualifiedName("org.springframework.web.context.request", "WebRequest") } 32 | } 33 | 34 | 35 | 36 | /** Holds if `Thymeleaf` templating engine is used in the project. */ 37 | predicate thymeleafIsUsed() { 38 | exists(Pom p | 39 | p.getADependency().getArtifact().getValue() in [ 40 | "spring-boot-starter-thymeleaf", "thymeleaf-spring4", "springmvc-xml-thymeleaf", 41 | "thymeleaf-spring5" 42 | ] 43 | ) 44 | or 45 | exists(SpringBean b | b.getClassNameRaw().matches("org.thymeleaf.spring%")) 46 | } 47 | 48 | /** Models methods from the `javax.portlet.RenderState` package which return data from externally controlled sources. */ 49 | class PortletRenderRequestMethod extends Method { 50 | PortletRenderRequestMethod() { 51 | exists(RefType c, Interface t | 52 | c.extendsOrImplements*(t) and 53 | t.hasQualifiedName("javax.portlet", "RenderState") and 54 | this = c.getAMethod() 55 | | 56 | this.hasName([ 57 | "getCookies", "getParameter", "getRenderParameters", "getParameterNames", 58 | "getParameterValues", "getParameterMap" 59 | ]) 60 | ) 61 | } 62 | } 63 | 64 | /** 65 | * A taint-tracking configuration for unsafe user input 66 | * that can lead to Spring View Manipulation vulnerabilities. 67 | */ 68 | class SpringViewManipulationConfig extends TaintTracking::Configuration { 69 | SpringViewManipulationConfig() { this = "Spring View Manipulation Config" } 70 | 71 | override predicate isSource(DataFlow::Node source) { 72 | source instanceof RemoteFlowSource or 73 | source instanceof WebRequestSource or 74 | source.asExpr().(MethodAccess).getMethod() instanceof PortletRenderRequestMethod 75 | } 76 | 77 | override predicate isSink(DataFlow::Node sink) { sink instanceof SpringViewManipulationSink } 78 | 79 | override predicate isSanitizer(DataFlow::Node node) { 80 | // Block flows like 81 | // ``` 82 | // a = "redirect:" + taint` 83 | // ``` 84 | exists(AddExpr e, StringLiteral sl | 85 | node.asExpr() = e.getControlFlowNode().getASuccessor*() and 86 | sl = e.getLeftOperand*() and 87 | sl.getValue().matches(["redirect:%", "ajaxredirect:%", "forward:%"]) 88 | ) 89 | or 90 | // Block flows like 91 | // ``` 92 | // x.append("redirect:"); 93 | // x.append(tainted()); 94 | // return x.toString(); 95 | // 96 | // "redirect:".concat(taint) 97 | // 98 | // String.format("redirect:%s",taint); 99 | // ``` 100 | exists(Call ca, StringLiteral sl | 101 | ( 102 | sl = ca.getArgument(_) 103 | or 104 | sl = ca.getQualifier() 105 | ) and 106 | ca = getAStringCombiningCall() and 107 | sl.getValue().matches(["redirect:%", "ajaxredirect:%", "forward:%"]) 108 | | 109 | exists(Call cc | DataFlow::localExprFlow(ca.getQualifier(), cc.getQualifier()) | 110 | cc = node.asExpr() 111 | ) 112 | ) 113 | } 114 | } 115 | 116 | private Call getAStringCombiningCall() { 117 | exists(StringCombiningMethod m | result = m.getAReference()) 118 | } 119 | 120 | abstract private class StringCombiningMethod extends Method { } 121 | 122 | private class AppendableAppendMethod extends StringCombiningMethod { 123 | AppendableAppendMethod() { 124 | exists(RefType t | 125 | t.hasQualifiedName("java.lang", "Appendable") and 126 | this.getDeclaringType().extendsOrImplements*(t) and 127 | this.hasName("append") 128 | ) 129 | } 130 | } 131 | 132 | private class StringConcatMethod extends StringCombiningMethod { 133 | StringConcatMethod() { 134 | this.getDeclaringType() instanceof TypeString and 135 | this.hasName("concat") 136 | } 137 | } 138 | 139 | private class StringFormatMethod extends StringCombiningMethod { 140 | StringFormatMethod() { 141 | this.getDeclaringType() instanceof TypeString and 142 | this.hasName("format") 143 | } 144 | } 145 | 146 | /** 147 | * A sink for Spring View Manipulation vulnerabilities, 148 | */ 149 | class SpringViewManipulationSink extends DataFlow::ExprNode { 150 | SpringViewManipulationSink() { 151 | exists(ReturnStmt r, SpringRequestMappingMethod m | 152 | r.getResult() = this.asExpr() and 153 | m.getBody().getAStmt() = r and 154 | not m.isResponseBody() and 155 | r.getResult().getType() instanceof TypeString 156 | ) 157 | or 158 | exists(ConstructorCall c | c.getConstructedType() instanceof ModelAndView | 159 | this.asExpr() = c.getArgument(0) and 160 | c.getConstructor().getParameterType(0) instanceof TypeString 161 | ) 162 | or 163 | exists(SpringModelAndViewSetViewNameCall c | this.asExpr() = c.getArgument(0)) 164 | } 165 | } 166 | 167 | 168 | 169 | from DataFlow::PathNode source, DataFlow::PathNode sink, SpringViewManipulationConfig conf 170 | where 171 | thymeleafIsUsed() and 172 | conf.hasFlowPath(source, sink) 173 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 174 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Potential Spring Expression Language injection" 175 | -------------------------------------------------------------------------------- /plugins/java/ThreadResourceAbuse.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.DataFlow 3 | private import semmle.code.java.dataflow.ExternalFlow 4 | import semmle.code.java.dataflow.FlowSteps 5 | import semmle.code.java.controlflow.Guards 6 | import semmle.code.java.dataflow.FlowSources 7 | import DataFlow::PathGraph 8 | 9 | 10 | /** `java.lang.Math` data model for value comparison in the new CSV format. */ 11 | private class MathCompDataModel extends SummaryModelCsv { 12 | override predicate row(string row) { 13 | row = 14 | [ 15 | "java.lang;Math;false;min;;;Argument[0..1];ReturnValue;value;manual", 16 | "java.lang;Math;false;max;;;Argument[0..1];ReturnValue;value;manual" 17 | ] 18 | } 19 | } 20 | 21 | /** Thread pause data model in the new CSV format. */ 22 | private class PauseThreadDataModel extends SinkModelCsv { 23 | override predicate row(string row) { 24 | row = 25 | [ 26 | "java.lang;Thread;true;sleep;;;Argument[0];thread-pause;manual", 27 | "java.util.concurrent;TimeUnit;true;sleep;;;Argument[0];thread-pause;manual" 28 | ] 29 | } 30 | } 31 | 32 | /** A sink representing methods pausing a thread. */ 33 | class PauseThreadSink extends DataFlow::Node { 34 | PauseThreadSink() { sinkNode(this, "thread-pause") } 35 | } 36 | 37 | private predicate lessThanGuard(Guard g, Expr e, boolean branch) { 38 | e = g.(ComparisonExpr).getLesserOperand() and 39 | branch = true 40 | or 41 | e = g.(ComparisonExpr).getGreaterOperand() and 42 | branch = false 43 | } 44 | 45 | /** A sanitizer for lessThan check. */ 46 | class LessThanSanitizer extends DataFlow::Node { 47 | LessThanSanitizer() { this = DataFlow::BarrierGuard::getABarrierNode() } 48 | } 49 | 50 | /** Value step from the constructor call of a `Runnable` to the instance parameter (this) of `run`. */ 51 | private class RunnableStartToRunStep extends AdditionalValueStep { 52 | override predicate step(DataFlow::Node pred, DataFlow::Node succ) { 53 | exists(ConstructorCall cc, Method m | 54 | m.getDeclaringType() = cc.getConstructedType().getSourceDeclaration() and 55 | cc.getConstructedType().getAnAncestor().hasQualifiedName("java.lang", "Runnable") and 56 | m.hasName("run") 57 | | 58 | pred.asExpr() = cc and 59 | succ.(DataFlow::InstanceParameterNode).getEnclosingCallable() = m 60 | ) 61 | } 62 | } 63 | 64 | /** 65 | * Value step from the constructor call of a `ProgressListener` of Apache File Upload to the 66 | * instance parameter (this) of `update`. 67 | */ 68 | private class ApacheFileUploadProgressUpdateStep extends AdditionalValueStep { 69 | override predicate step(DataFlow::Node pred, DataFlow::Node succ) { 70 | exists(ConstructorCall cc, Method m | 71 | m.getDeclaringType() = cc.getConstructedType().getSourceDeclaration() and 72 | cc.getConstructedType() 73 | .getAnAncestor() 74 | .hasQualifiedName(["org.apache.commons.fileupload", "org.apache.commons.fileupload2"], 75 | "ProgressListener") and 76 | m.hasName("update") 77 | | 78 | pred.asExpr() = cc and 79 | succ.(DataFlow::InstanceParameterNode).getEnclosingCallable() = m 80 | ) 81 | } 82 | } 83 | 84 | 85 | 86 | /** Taint configuration of uncontrolled thread resource consumption. */ 87 | class ThreadResourceAbuse extends TaintTracking::Configuration { 88 | ThreadResourceAbuse() { this = "ThreadResourceAbuse" } 89 | 90 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 91 | 92 | override predicate isSink(DataFlow::Node sink) { sink instanceof PauseThreadSink } 93 | 94 | override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { 95 | any(AdditionalValueStep r).step(pred, succ) 96 | } 97 | 98 | override predicate isSanitizer(DataFlow::Node node) { 99 | exists( 100 | MethodAccess ma // Math.min(sleepTime, MAX_INTERVAL) 101 | | 102 | ma.getMethod().hasQualifiedName("java.lang", "Math", "min") and 103 | node.asExpr() = ma.getAnArgument() 104 | ) 105 | or 106 | node instanceof LessThanSanitizer // if (sleepTime > 0 && sleepTime < 5000) { ... } 107 | } 108 | } 109 | 110 | from DataFlow::PathNode source, DataFlow::PathNode sink, ThreadResourceAbuse conf 111 | where conf.hasFlowPath(source, sink) 112 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 113 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 114 | "Vulnerability of uncontrolled resource consumption" 115 | -------------------------------------------------------------------------------- /plugins/java/TimingAttackAgainstHeader.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.dataflow.TaintTracking 4 | import DataFlow::PathGraph 5 | 6 | 7 | /** A static method that uses a non-constant-time algorithm for comparing inputs. */ 8 | private class NonConstantTimeComparisonCall extends StaticMethodAccess { 9 | NonConstantTimeComparisonCall() { 10 | this.getMethod() 11 | .hasQualifiedName("org.apache.commons.lang3", "StringUtils", 12 | ["equals", "equalsAny", "equalsAnyIgnoreCase", "equalsIgnoreCase"]) 13 | } 14 | } 15 | 16 | /** Methods that use a non-constant-time algorithm for comparing inputs. */ 17 | private class NonConstantTimeEqualsCall extends MethodAccess { 18 | NonConstantTimeEqualsCall() { 19 | this.getMethod() 20 | .hasQualifiedName("java.lang", "String", ["equals", "contentEquals", "equalsIgnoreCase"]) 21 | } 22 | } 23 | 24 | private predicate isNonConstantEqualsCallArgument(Expr e) { 25 | exists(NonConstantTimeEqualsCall call | e = [call.getQualifier(), call.getArgument(0)]) 26 | } 27 | 28 | private predicate isNonConstantComparisonCallArgument(Expr p) { 29 | exists(NonConstantTimeComparisonCall call | p = [call.getArgument(0), call.getArgument(1)]) 30 | } 31 | 32 | class ClientSuppliedIpTokenCheck extends DataFlow::Node { 33 | ClientSuppliedIpTokenCheck() { 34 | exists(MethodAccess ma | 35 | ma.getMethod().hasName("getHeader") and 36 | ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() in [ 37 | "x-auth-token", "x-csrf-token", "http_x_csrf_token", "x-csrf-param", "x-csrf-header", 38 | "http_x_csrf_token", "x-api-key", "authorization", "proxy-authorization" 39 | ] and 40 | ma = this.asExpr() 41 | ) 42 | } 43 | } 44 | 45 | class NonConstantTimeComparisonConfig extends TaintTracking::Configuration { 46 | NonConstantTimeComparisonConfig() { this = "NonConstantTimeComparisonConfig" } 47 | 48 | override predicate isSource(DataFlow::Node source) { 49 | source instanceof ClientSuppliedIpTokenCheck 50 | } 51 | 52 | override predicate isSink(DataFlow::Node sink) { 53 | isNonConstantEqualsCallArgument(sink.asExpr()) or 54 | isNonConstantComparisonCallArgument(sink.asExpr()) 55 | } 56 | } 57 | 58 | from DataFlow::PathNode source, DataFlow::PathNode sink, NonConstantTimeComparisonConfig conf 59 | where conf.hasFlowPath(source, sink) 60 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 61 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Possible timing attack 62 | -------------------------------------------------------------------------------- /plugins/java/UncaughtServletException.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.dataflow.TaintTracking 4 | import semmle.code.java.frameworks.Servlets 5 | import semmle.code.xml.WebXML 6 | import DataFlow::PathGraph 7 | 8 | 9 | /** Holds if a given exception type is caught. */ 10 | private predicate exceptionIsCaught(TryStmt t, RefType exType) { 11 | exists(CatchClause cc, LocalVariableDeclExpr v | 12 | t.getACatchClause() = cc and 13 | cc.getVariable() = v and 14 | v.getType().(RefType).getADescendant() = exType and // Detect the case that a subclass exception is thrown but its parent class is declared in the catch clause. 15 | not exists( 16 | ThrowStmt ts // Detect the edge case that exception is caught then rethrown without processing in a catch clause 17 | | 18 | ts.getEnclosingStmt() = cc.getBlock() and 19 | ts.getExpr() = v.getAnAccess() 20 | ) 21 | ) 22 | } 23 | 24 | /** Servlet methods of `javax.servlet.http.Servlet` and subtypes. */ 25 | private predicate isServletMethod(Callable c) { 26 | c.getDeclaringType() instanceof ServletClass and 27 | c.getNumberOfParameters() = 2 and 28 | c.getParameter(1).getType() instanceof ServletResponse and 29 | c.getName() in [ 30 | "doGet", "doPost", "doPut", "doDelete", "doHead", "doOptions", "doTrace", "service" 31 | ] 32 | } 33 | 34 | /** Holds if `web.xml` has an error page configured. */ 35 | private predicate hasErrorPage() { 36 | exists(WebErrorPage wep | wep.getPageLocation().getValue() != "") 37 | } 38 | 39 | /** Sink of uncaught exceptions, which shall be IO exceptions or runtime exceptions since other exception types must be explicitly caught. */ 40 | class UncaughtServletExceptionSink extends DataFlow::ExprNode { 41 | UncaughtServletExceptionSink() { 42 | exists(Method m, MethodAccess ma | ma.getMethod() = m | 43 | isServletMethod(ma.getEnclosingCallable()) and 44 | exists(m.getAThrownExceptionType()) and // The called method might plausibly throw an exception. 45 | ma.getAnArgument() = this.getExpr() and 46 | not exists(TryStmt t | 47 | t.getBlock() = ma.getAnEnclosingStmt() and 48 | exceptionIsCaught(t, m.getAThrownExceptionType()) 49 | ) 50 | ) 51 | } 52 | } 53 | 54 | /** Taint configuration of uncaught exceptions caused by user provided data from `RemoteFlowSource` */ 55 | class UncaughtServletExceptionConfiguration extends TaintTracking::Configuration { 56 | UncaughtServletExceptionConfiguration() { this = "UncaughtServletException" } 57 | 58 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 59 | 60 | override predicate isSink(DataFlow::Node sink) { sink instanceof UncaughtServletExceptionSink } 61 | } 62 | 63 | from DataFlow::PathNode source, DataFlow::PathNode sink, UncaughtServletExceptionConfiguration c 64 | where c.hasFlowPath(source, sink) and not hasErrorPage() 65 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 66 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "May throw uncaught exception." 67 | -------------------------------------------------------------------------------- /plugins/java/UnsafeDeserializationRmi.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.TaintTracking 3 | import semmle.code.java.frameworks.Rmi 4 | import DataFlow::PathGraph 5 | 6 | 7 | /** 8 | * A method that binds a name to a remote object. 9 | */ 10 | private class BindMethod extends Method { 11 | BindMethod() { 12 | ( 13 | getDeclaringType().hasQualifiedName("java.rmi", "Naming") or 14 | getDeclaringType().hasQualifiedName("java.rmi.registry", "Registry") 15 | ) and 16 | hasName(["bind", "rebind"]) 17 | } 18 | } 19 | 20 | /** 21 | * Holds if `type` has an vulnerable remote method. 22 | */ 23 | private predicate hasVulnerableMethod(RefType type) { 24 | exists(RemoteCallableMethod m, Type parameterType | 25 | m.getDeclaringType() = type and parameterType = m.getAParamType() 26 | | 27 | not parameterType instanceof PrimitiveType and 28 | not parameterType instanceof TypeString and 29 | not parameterType instanceof TypeObjectInputStream 30 | ) 31 | } 32 | 33 | /** 34 | * A taint-tracking configuration for unsafe remote objects 35 | * that are vulnerable to deserialization attacks. 36 | */ 37 | private class BindingUnsafeRemoteObjectConfig extends TaintTracking::Configuration { 38 | BindingUnsafeRemoteObjectConfig() { this = "BindingUnsafeRemoteObjectConfig" } 39 | 40 | override predicate isSource(DataFlow::Node source) { 41 | exists(ConstructorCall cc | cc = source.asExpr() | 42 | hasVulnerableMethod(cc.getConstructedType().getAnAncestor()) 43 | ) 44 | } 45 | 46 | override predicate isSink(DataFlow::Node sink) { 47 | exists(MethodAccess ma | ma.getArgument(1) = sink.asExpr() | 48 | ma.getMethod() instanceof BindMethod 49 | ) 50 | } 51 | 52 | override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { 53 | exists(MethodAccess ma, Method m | m = ma.getMethod() | 54 | m.getDeclaringType().hasQualifiedName("java.rmi.server", "UnicastRemoteObject") and 55 | m.hasName("exportObject") and 56 | not m.getParameterType([2, 4]).(RefType).hasQualifiedName("java.io", "ObjectInputFilter") and 57 | ma.getArgument(0) = fromNode.asExpr() and 58 | ma = toNode.asExpr() 59 | ) 60 | } 61 | } 62 | 63 | from DataFlow::PathNode source, DataFlow::PathNode sink, BindingUnsafeRemoteObjectConfig conf 64 | where conf.hasFlowPath(source, sink) 65 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 66 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Unsafe deserialization in a remote object." 67 | -------------------------------------------------------------------------------- /plugins/java/UnsafeReflection.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import DataFlow 3 | import semmle.code.java.Reflection 4 | import semmle.code.java.dataflow.FlowSources 5 | import semmle.code.java.dataflow.DataFlow 6 | import semmle.code.java.controlflow.Guards 7 | import DataFlow::PathGraph 8 | 9 | 10 | /** 11 | * A call to `java.lang.reflect.Method.invoke`. 12 | */ 13 | class MethodInvokeCall extends MethodAccess { 14 | MethodInvokeCall() { this.getMethod().hasQualifiedName("java.lang.reflect", "Method", "invoke") } 15 | } 16 | 17 | /** 18 | * Unsafe reflection sink (the qualifier or method arguments to `Constructor.newInstance(...)` or `Method.invoke(...)`) 19 | */ 20 | class UnsafeReflectionSink extends DataFlow::ExprNode { 21 | UnsafeReflectionSink() { 22 | exists(MethodAccess ma | 23 | ( 24 | ma.getMethod().hasQualifiedName("java.lang.reflect", "Constructor<>", "newInstance") or 25 | ma instanceof MethodInvokeCall 26 | ) and 27 | this.asExpr() = [ma.getQualifier(), ma.getAnArgument()] 28 | ) 29 | } 30 | } 31 | 32 | /** 33 | * Holds if `fromNode` to `toNode` is a dataflow step that looks like resolving a class. 34 | * A method probably resolves a class if it takes a string, returns a Class 35 | * and its name contains "resolve", "load", etc. 36 | */ 37 | predicate looksLikeResolveClassStep(DataFlow::Node fromNode, DataFlow::Node toNode) { 38 | exists(MethodAccess ma, Method m, int i, Expr arg | 39 | m = ma.getMethod() and arg = ma.getArgument(i) 40 | | 41 | m.getReturnType() instanceof TypeClass and 42 | m.getName().toLowerCase() = ["resolve", "load", "class", "type"] and 43 | arg.getType() instanceof TypeString and 44 | arg = fromNode.asExpr() and 45 | ma = toNode.asExpr() 46 | ) 47 | } 48 | 49 | /** 50 | * Holds if `fromNode` to `toNode` is a dataflow step that looks like instantiating a class. 51 | * A method probably instantiates a class if it is external, takes a Class, returns an Object 52 | * and its name contains "instantiate" or similar terms. 53 | */ 54 | predicate looksLikeInstantiateClassStep(DataFlow::Node fromNode, DataFlow::Node toNode) { 55 | exists(MethodAccess ma, Method m, int i, Expr arg | 56 | m = ma.getMethod() and arg = ma.getArgument(i) 57 | | 58 | m.getReturnType() instanceof TypeObject and 59 | m.getName().toLowerCase() = ["instantiate", "instance", "create", "make", "getbean"] and 60 | arg.getType() instanceof TypeClass and 61 | arg = fromNode.asExpr() and 62 | ma = toNode.asExpr() 63 | ) 64 | } 65 | 66 | 67 | 68 | private predicate containsSanitizer(Guard g, Expr e, boolean branch) { 69 | g.(MethodAccess).getMethod().hasName("contains") and 70 | e = g.(MethodAccess).getArgument(0) and 71 | branch = true 72 | } 73 | 74 | private predicate equalsSanitizer(Guard g, Expr e, boolean branch) { 75 | g.(MethodAccess).getMethod().hasName("equals") and 76 | e = [g.(MethodAccess).getArgument(0), g.(MethodAccess).getQualifier()] and 77 | branch = true 78 | } 79 | 80 | class UnsafeReflectionConfig extends TaintTracking::Configuration { 81 | UnsafeReflectionConfig() { this = "UnsafeReflectionConfig" } 82 | 83 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 84 | 85 | override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeReflectionSink } 86 | 87 | override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { 88 | // Argument -> return of Class.forName, ClassLoader.loadClass 89 | exists(ReflectiveClassIdentifierMethodAccess rcimac | 90 | rcimac.getArgument(0) = pred.asExpr() and rcimac = succ.asExpr() 91 | ) 92 | or 93 | // Qualifier -> return of Class.getDeclaredConstructors/Methods and similar 94 | exists(MethodAccess ma | 95 | ( 96 | ma instanceof ReflectiveConstructorsAccess or 97 | ma instanceof ReflectiveMethodsAccess 98 | ) and 99 | ma.getQualifier() = pred.asExpr() and 100 | ma = succ.asExpr() 101 | ) 102 | or 103 | // Qualifier -> return of Object.getClass 104 | exists(MethodAccess ma | 105 | ma.getMethod().hasName("getClass") and 106 | ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "Object") and 107 | ma.getQualifier() = pred.asExpr() and 108 | ma = succ.asExpr() 109 | ) 110 | or 111 | // Argument -> return of methods that look like Class.forName 112 | looksLikeResolveClassStep(pred, succ) 113 | or 114 | // Argument -> return of methods that look like `Object getInstance(Class c)` 115 | looksLikeInstantiateClassStep(pred, succ) 116 | or 117 | // Qualifier -> return of Constructor.newInstance, Class.newInstance 118 | exists(NewInstance ni | 119 | ni.getQualifier() = pred.asExpr() and 120 | ni = succ.asExpr() 121 | ) 122 | } 123 | 124 | override predicate isSanitizer(DataFlow::Node node) { 125 | node = DataFlow::BarrierGuard::getABarrierNode() or 126 | node = DataFlow::BarrierGuard::getABarrierNode() 127 | } 128 | } 129 | 130 | private Expr getAMethodArgument(MethodAccess reflectiveCall) { 131 | result = reflectiveCall.(NewInstance).getAnArgument() 132 | or 133 | result = reflectiveCall.(MethodInvokeCall).getAnArgument() 134 | } 135 | 136 | from 137 | DataFlow::PathNode source, DataFlow::PathNode sink, UnsafeReflectionConfig conf, 138 | MethodAccess reflectiveCall 139 | where 140 | conf.hasFlowPath(source, sink) and 141 | sink.getNode().asExpr() = reflectiveCall.getQualifier() and 142 | conf.hasFlowToExpr(getAMethodArgument(reflectiveCall)) 143 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 144 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Unsafe reflection" 145 | -------------------------------------------------------------------------------- /plugins/java/UnsafeSpringExporterInConfigurationClass.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import java 3 | 4 | /** 5 | * Holds if `type` is `RemoteInvocationSerializingExporter`. 6 | */ 7 | predicate isRemoteInvocationSerializingExporter(RefType type) { 8 | type.getAnAncestor() 9 | .hasQualifiedName("org.springframework.remoting.rmi", 10 | ["RemoteInvocationSerializingExporter", "RmiBasedExporter"]) or 11 | type.getAnAncestor().hasQualifiedName("org.springframework.remoting.caucho", "HessianExporter") 12 | } 13 | 14 | 15 | 16 | /** 17 | * Holds if `type` is a Spring configuration that declares beans. 18 | */ 19 | private predicate isConfiguration(RefType type) { 20 | type.hasAnnotation("org.springframework.context.annotation", "Configuration") or 21 | isConfigurationAnnotation(type.getAnAnnotation()) 22 | } 23 | 24 | /** 25 | * Holds if `annotation` is a Java annotations that declares a Spring configuration. 26 | */ 27 | private predicate isConfigurationAnnotation(Annotation annotation) { 28 | isConfiguration(annotation.getType()) or 29 | isConfigurationAnnotation(annotation.getType().getAnAnnotation()) 30 | } 31 | 32 | /** 33 | * A method that initializes a unsafe bean based on `RemoteInvocationSerializingExporter`. 34 | */ 35 | private class UnsafeBeanInitMethod extends Method { 36 | string identifier; 37 | 38 | UnsafeBeanInitMethod() { 39 | isRemoteInvocationSerializingExporter(this.getReturnType()) and 40 | isConfiguration(this.getDeclaringType()) and 41 | exists(Annotation a | this.getAnAnnotation() = a | 42 | a.getType().hasQualifiedName("org.springframework.context.annotation", "Bean") and 43 | if a.getValue("name") instanceof StringLiteral 44 | then identifier = a.getValue("name").(StringLiteral).getValue() 45 | else identifier = this.getName() 46 | ) 47 | } 48 | 49 | /** 50 | * Gets this bean's name if given by the `Bean` annotation, or this method's identifier otherwise. 51 | */ 52 | string getBeanIdentifier() { result = identifier } 53 | } 54 | 55 | from UnsafeBeanInitMethod method 56 | select method, 57 | "Unsafe deserialization in a Spring exporter bean '" + method.getBeanIdentifier() + "'." 58 | -------------------------------------------------------------------------------- /plugins/java/UnsafeSpringExporterInXMLConfiguration.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.frameworks.spring.SpringBean 3 | import java 4 | 5 | /** 6 | * Holds if `type` is `RemoteInvocationSerializingExporter`. 7 | */ 8 | predicate isRemoteInvocationSerializingExporter(RefType type) { 9 | type.getAnAncestor() 10 | .hasQualifiedName("org.springframework.remoting.rmi", 11 | ["RemoteInvocationSerializingExporter", "RmiBasedExporter"]) or 12 | type.getAnAncestor().hasQualifiedName("org.springframework.remoting.caucho", "HessianExporter") 13 | } 14 | 15 | 16 | 17 | from SpringBean bean 18 | where isRemoteInvocationSerializingExporter(bean.getClass()) 19 | select bean, "Unsafe deserialization in a Spring exporter bean '" + bean.getBeanIdentifier() + "'." 20 | -------------------------------------------------------------------------------- /plugins/java/UnsafeTlsVersion.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.security.Encryption 3 | import semmle.code.java.dataflow.TaintTracking 4 | import DataFlow 5 | import PathGraph 6 | import DataFlow::PathGraph 7 | 8 | 9 | /** 10 | * A taint-tracking configuration for unsafe SSL and TLS versions. 11 | */ 12 | class UnsafeTlsVersionConfig extends TaintTracking::Configuration { 13 | UnsafeTlsVersionConfig() { this = "UnsafeTlsVersion::UnsafeTlsVersionConfig" } 14 | 15 | override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof UnsafeTlsVersion } 16 | 17 | override predicate isSink(DataFlow::Node sink) { 18 | sink instanceof SslContextGetInstanceSink or 19 | sink instanceof CreateSslParametersSink or 20 | sink instanceof SslParametersSetProtocolsSink or 21 | sink instanceof SetEnabledProtocolsSink 22 | } 23 | } 24 | 25 | /** 26 | * A sink that sets protocol versions in `SSLContext`, 27 | * i.e `SSLContext.getInstance(protocol)`. 28 | */ 29 | class SslContextGetInstanceSink extends DataFlow::ExprNode { 30 | SslContextGetInstanceSink() { 31 | exists(StaticMethodAccess ma, Method m | m = ma.getMethod() | 32 | m.getDeclaringType() instanceof SslContext and 33 | m.hasName("getInstance") and 34 | ma.getArgument(0) = asExpr() 35 | ) 36 | } 37 | } 38 | 39 | /** 40 | * A sink that creates `SSLParameters` with specified protocols, 41 | * i.e. `new SSLParameters(ciphersuites, protocols)`. 42 | */ 43 | class CreateSslParametersSink extends DataFlow::ExprNode { 44 | CreateSslParametersSink() { 45 | exists(ConstructorCall cc | cc.getConstructedType() instanceof SslParameters | 46 | cc.getArgument(1) = asExpr() 47 | ) 48 | } 49 | } 50 | 51 | /** 52 | * A sink that sets protocol versions for `SSLParameters`, 53 | * i.e. `parameters.setProtocols(versions)`. 54 | */ 55 | class SslParametersSetProtocolsSink extends DataFlow::ExprNode { 56 | SslParametersSetProtocolsSink() { 57 | exists(MethodAccess ma, Method m | m = ma.getMethod() | 58 | m.getDeclaringType() instanceof SslParameters and 59 | m.hasName("setProtocols") and 60 | ma.getArgument(0) = asExpr() 61 | ) 62 | } 63 | } 64 | 65 | /** 66 | * A sink that sets protocol versions for `SSLSocket`, `SSLServerSocket`, and `SSLEngine`, 67 | * i.e. `socket.setEnabledProtocols(versions)` or `engine.setEnabledProtocols(versions)`. 68 | */ 69 | class SetEnabledProtocolsSink extends DataFlow::ExprNode { 70 | SetEnabledProtocolsSink() { 71 | exists(MethodAccess ma, Method m, RefType type | 72 | m = ma.getMethod() and type = m.getDeclaringType() 73 | | 74 | ( 75 | type instanceof SslSocket or 76 | type instanceof SslServerSocket or 77 | type instanceof SslEngine 78 | ) and 79 | m.hasName("setEnabledProtocols") and 80 | ma.getArgument(0) = asExpr() 81 | ) 82 | } 83 | } 84 | 85 | /** 86 | * Insecure SSL and TLS versions supported by JSSE. 87 | */ 88 | class UnsafeTlsVersion extends StringLiteral { 89 | UnsafeTlsVersion() { 90 | getValue() = "SSL" or 91 | getValue() = "SSLv2" or 92 | getValue() = "SSLv3" or 93 | getValue() = "TLS" or 94 | getValue() = "TLSv1" or 95 | getValue() = "TLSv1.1" 96 | } 97 | } 98 | 99 | class SslServerSocket extends RefType { 100 | SslServerSocket() { hasQualifiedName("javax.net.ssl", "SSLServerSocket") } 101 | } 102 | 103 | 104 | 105 | from DataFlow::PathNode source, DataFlow::PathNode sink, UnsafeTlsVersionConfig conf 106 | where conf.hasFlowPath(source, sink) 107 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 108 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "TLS version is unsafe." 109 | -------------------------------------------------------------------------------- /plugins/java/UnvalidatedCors.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.frameworks.Servlets 4 | import semmle.code.java.dataflow.TaintTracking 5 | import semmle.code.java.dataflow.TaintTracking2 6 | import DataFlow::PathGraph 7 | 8 | 9 | /** 10 | * Holds if `header` sets `Access-Control-Allow-Credentials` to `true`. This ensures fair chances of exploitability. 11 | */ 12 | private predicate setsAllowCredentials(MethodAccess header) { 13 | ( 14 | header.getMethod() instanceof ResponseSetHeaderMethod or 15 | header.getMethod() instanceof ResponseAddHeaderMethod 16 | ) and 17 | header.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() = 18 | "access-control-allow-credentials" and 19 | header.getArgument(1).(CompileTimeConstantExpr).getStringValue().toLowerCase() = "true" 20 | } 21 | 22 | private class CorsProbableCheckAccess extends MethodAccess { 23 | CorsProbableCheckAccess() { 24 | getMethod().hasName("contains") and 25 | getMethod().getDeclaringType().getASourceSupertype*() instanceof CollectionType 26 | or 27 | getMethod().hasName("containsKey") and 28 | getMethod().getDeclaringType().getASourceSupertype*() instanceof MapType 29 | or 30 | getMethod().hasName("equals") and 31 | getQualifier().getType() instanceof TypeString 32 | } 33 | } 34 | 35 | private Expr getAccessControlAllowOriginHeaderName() { 36 | result.(CompileTimeConstantExpr).getStringValue().toLowerCase() = "access-control-allow-origin" 37 | } 38 | 39 | /** 40 | * This taintflow2 configuration checks if there is a flow from source node towards CorsProbableCheckAccess methods. 41 | */ 42 | class CorsSourceReachesCheckConfig extends TaintTracking2::Configuration { 43 | CorsSourceReachesCheckConfig() { this = "CorsOriginConfig" } 44 | 45 | override predicate isSource(DataFlow::Node source) { any(CorsOriginConfig c).hasFlow(source, _) } 46 | 47 | override predicate isSink(DataFlow::Node sink) { 48 | sink.asExpr() = any(CorsProbableCheckAccess check).getAnArgument() 49 | } 50 | } 51 | 52 | private class CorsOriginConfig extends TaintTracking::Configuration { 53 | CorsOriginConfig() { this = "CorsOriginConfig" } 54 | 55 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 56 | 57 | override predicate isSink(DataFlow::Node sink) { 58 | exists(MethodAccess corsHeader, MethodAccess allowCredentialsHeader | 59 | ( 60 | corsHeader.getMethod() instanceof ResponseSetHeaderMethod or 61 | corsHeader.getMethod() instanceof ResponseAddHeaderMethod 62 | ) and 63 | getAccessControlAllowOriginHeaderName() = corsHeader.getArgument(0) and 64 | setsAllowCredentials(allowCredentialsHeader) and 65 | corsHeader.getEnclosingCallable() = allowCredentialsHeader.getEnclosingCallable() and 66 | sink.asExpr() = corsHeader.getArgument(1) 67 | ) 68 | } 69 | } 70 | 71 | from 72 | DataFlow::PathNode source, DataFlow::PathNode sink, CorsOriginConfig conf, 73 | CorsSourceReachesCheckConfig sanconf 74 | where conf.hasFlowPath(source, sink) and not sanconf.hasFlow(source.getNode(), _) 75 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 76 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "CORS header is being set using user controlled value " 77 | -------------------------------------------------------------------------------- /plugins/java/WebComponentMain.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.frameworks.Servlets 3 | 4 | 5 | /** 6 | * Holds if `m` is a test method indicated by: 7 | * a) in a test directory such as `src/test/java` 8 | * b) in a test package whose name has the word `test` 9 | * c) in a test class whose name has the word `test` 10 | * d) in a test class implementing a test framework such as JUnit or TestNG 11 | */ 12 | predicate isTestMethod(Method m) { 13 | m.getDeclaringType().getName().toLowerCase().matches("%test%") or // Simple check to exclude test classes to reduce FPs 14 | m.getDeclaringType().getPackage().getName().toLowerCase().matches("%test%") or // Simple check to exclude classes in test packages to reduce FPs 15 | exists(m.getLocation().getFile().getAbsolutePath().indexOf("/src/test/java")) or // Match test directory structure of build tools like maven 16 | m instanceof TestMethod // Test method of a test case implementing a test framework such as JUnit or TestNG 17 | } 18 | 19 | 20 | 21 | /** The java type `javax.servlet.Filter`. */ 22 | class ServletFilterClass extends Class { 23 | ServletFilterClass() { this.getAnAncestor().hasQualifiedName("javax.servlet", "Filter") } 24 | } 25 | 26 | /** Listener class in the package `javax.servlet` and `javax.servlet.http` */ 27 | class ServletListenerClass extends Class { 28 | // Various listener classes of Java EE such as ServletContextListener. They all have a name ending with the word "Listener". 29 | ServletListenerClass() { 30 | this.getAnAncestor() 31 | .getQualifiedName() 32 | .regexpMatch([ 33 | "javax\\.servlet\\.[a-zA-Z]+Listener", "javax\\.servlet\\.http\\.[a-zA-Z]+Listener" 34 | ]) 35 | } 36 | } 37 | 38 | /** The `main` method in `Servlet` and `Action` of the Spring and Struts framework. */ 39 | class WebComponentMainMethod extends Method { 40 | WebComponentMainMethod() { 41 | ( 42 | this.getDeclaringType() instanceof ServletClass or 43 | this.getDeclaringType() instanceof ServletFilterClass or 44 | this.getDeclaringType() instanceof ServletListenerClass or 45 | this.getDeclaringType().getAnAncestor().hasQualifiedName("org.apache.struts.action", "Action") or // Struts actions 46 | this.getDeclaringType() 47 | .getAStrictAncestor() 48 | .hasQualifiedName("com.opensymphony.xwork2", "ActionSupport") or // Struts 2 actions 49 | this.getDeclaringType() 50 | .getAStrictAncestor() 51 | .hasQualifiedName("org.springframework.web.struts", "ActionSupport") or // Spring/Struts 2 actions 52 | this.getDeclaringType() 53 | .getAStrictAncestor() 54 | .hasQualifiedName("org.springframework.webflow.execution", "Action") // Spring actions 55 | ) and 56 | this instanceof MainMethod and 57 | not isTestMethod(this) 58 | } 59 | } 60 | 61 | from WebComponentMainMethod sm 62 | select sm, "Web application has a main method." 63 | -------------------------------------------------------------------------------- /plugins/java/XQueryInjection.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import DataFlow::PathGraph 4 | import java 5 | 6 | /** A call to `XQConnection.prepareExpression`. */ 7 | class XQueryParserCall extends MethodAccess { 8 | XQueryParserCall() { 9 | exists(Method m | 10 | this.getMethod() = m and 11 | m.getDeclaringType() 12 | .getASourceSupertype*() 13 | .hasQualifiedName("javax.xml.xquery", "XQConnection") and 14 | m.hasName("prepareExpression") 15 | ) 16 | } 17 | 18 | /** 19 | * Returns the first parameter of the `prepareExpression` method, which provides 20 | * the string, stream or reader to be compiled into a prepared expression. 21 | */ 22 | Expr getInput() { result = this.getArgument(0) } 23 | } 24 | 25 | /** A call to `XQPreparedExpression.executeQuery`. */ 26 | class XQueryPreparedExecuteCall extends MethodAccess { 27 | XQueryPreparedExecuteCall() { 28 | exists(Method m | 29 | this.getMethod() = m and 30 | m.hasName("executeQuery") and 31 | m.getDeclaringType() 32 | .getASourceSupertype*() 33 | .hasQualifiedName("javax.xml.xquery", "XQPreparedExpression") 34 | ) 35 | } 36 | 37 | /** Return this prepared expression. */ 38 | Expr getPreparedExpression() { result = this.getQualifier() } 39 | } 40 | 41 | /** A call to `XQExpression.executeQuery`. */ 42 | class XQueryExecuteCall extends MethodAccess { 43 | XQueryExecuteCall() { 44 | exists(Method m | 45 | this.getMethod() = m and 46 | m.hasName("executeQuery") and 47 | m.getDeclaringType() 48 | .getASourceSupertype*() 49 | .hasQualifiedName("javax.xml.xquery", "XQExpression") 50 | ) 51 | } 52 | 53 | /** Return this execute query argument. */ 54 | Expr getExecuteQueryArgument() { result = this.getArgument(0) } 55 | } 56 | 57 | /** A call to `XQExpression.executeCommand`. */ 58 | class XQueryExecuteCommandCall extends MethodAccess { 59 | XQueryExecuteCommandCall() { 60 | exists(Method m | 61 | this.getMethod() = m and 62 | m.hasName("executeCommand") and 63 | m.getDeclaringType() 64 | .getASourceSupertype*() 65 | .hasQualifiedName("javax.xml.xquery", "XQExpression") 66 | ) 67 | } 68 | 69 | /** Return this execute command argument. */ 70 | Expr getExecuteCommandArgument() { result = this.getArgument(0) } 71 | } 72 | 73 | 74 | 75 | /** 76 | * A taint-tracking configuration tracing flow from remote sources, through an XQuery parser, to its eventual execution. 77 | */ 78 | class XQueryInjectionConfig extends TaintTracking::Configuration { 79 | XQueryInjectionConfig() { this = "XQueryInjectionConfig" } 80 | 81 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 82 | 83 | override predicate isSink(DataFlow::Node sink) { 84 | sink.asExpr() = any(XQueryPreparedExecuteCall xpec).getPreparedExpression() or 85 | sink.asExpr() = any(XQueryExecuteCall xec).getExecuteQueryArgument() or 86 | sink.asExpr() = any(XQueryExecuteCommandCall xecc).getExecuteCommandArgument() 87 | } 88 | 89 | /** 90 | * Holds if taint from the input `pred` to a `prepareExpression` call flows to the returned prepared expression `succ`. 91 | */ 92 | override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { 93 | exists(XQueryParserCall parser | pred.asExpr() = parser.getInput() and succ.asExpr() = parser) 94 | } 95 | } 96 | 97 | from DataFlow::PathNode source, DataFlow::PathNode sink, XQueryInjectionConfig conf 98 | where conf.hasFlowPath(source, sink) 99 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 100 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "XQuery query injection" 101 | -------------------------------------------------------------------------------- /plugins/java/devMode.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import experimental.semmle.code.xml.StrutsXML 3 | 4 | 5 | bindingset[path] 6 | predicate isLikelyDemoProject(string path) { path.regexpMatch("(?i).*(demo|test|example).*") } 7 | 8 | from ConstantParameter c 9 | where 10 | c.getNameValue() = "struts.devMode" and 11 | c.getValueValue() = "true" and 12 | not isLikelyDemoProject(c.getFile().getRelativePath()) 13 | select c, "Enabling development mode in production environments is dangerous." 14 | -------------------------------------------------------------------------------- /plugins/java/说明.txt: -------------------------------------------------------------------------------- 1 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-665 InsecureRmiJmxEnvironmentConfiguration.ql 2 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-208 TimingAttackAgainstSignature.ql 3 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-208 PossibleTimingAttackAgainstSignature.ql 4 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-208 TimingAttackAgainstHeader.ql 5 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-600 UncaughtServletException.ql 6 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-297 InsecureLdapEndpoint.ql 7 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-297 IgnoredHostnameVerification.ql 8 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-299 DisabledRevocationChecking.ql 9 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-094 ScriptInjection.ql 10 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-094 SpringViewManipulation.ql 11 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-094 JShellInjection.ql 12 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-094 JakartaExpressionInjection.ql 13 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-094 JythonInjection.ql 14 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-094 InsecureDexLoading.ql 15 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-094 SpringImplicitViewManipulation.ql 16 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-094 BeanShellInjection.ql 17 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-652 XQueryInjection.ql 18 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-200 InsecureWebResourceResponse.ql 19 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-200 SensitiveAndroidFileLeak.ql 20 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-601 SpringUrlRedirect.ql 21 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-326 InsufficientKeySize.ql 22 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-321 HardcodedJwtKey.ql 23 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-548 InsecureDirectoryConfig.ql 24 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-327 UnsafeTlsVersion.ql 25 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-522 InsecureLdapAuth.ql 26 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-598 SensitiveGetQuery.ql 27 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-759 HashWithoutSalt.ql 28 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-555 PasswordInConfigurationFile.ql 29 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-555 CredentialsInPropertiesFile.ql 30 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-552 UnsafeUrlForward.ql 31 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-489 devMode.ql 32 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-489 WebComponentMain.ql 33 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-489 EJBMain.ql 34 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-089 MyBatisAnnotationSqlInjection.ql 35 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-089 MyBatisMapperXmlSqlInjection.ql 36 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-625 PermissiveDotRegex.ql 37 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-073 FilePathInjection.ql 38 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-020 Log4jJndiInjection.ql 39 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-016 InsecureSpringActuatorConfig.ql 40 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-016 SpringBootActuators.ql 41 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-400 LocalThreadResourceAbuse.ql 42 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-400 ThreadResourceAbuse.ql 43 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-295 JxBrowserWithoutCertValidation.ql 44 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-036 OpenStream.ql 45 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-346 UnvalidatedCors.ql 46 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-348 ClientSuppliedIpUsedInSecurityCheck.ql 47 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-939 IncorrectURLVerification.ql 48 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-352 JsonpInjection.ql 49 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-755 NFEAndroidDoS.ql 50 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-502 UnsafeSpringExporterInConfigurationClass.ql 51 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-502 UnsafeSpringExporterInXMLConfiguration.ql 52 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-502 UnsafeDeserializationRmi.ql 53 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-730 RegexInjection.ql 54 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-470 UnsafeReflection.ql 55 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-1004 InsecureTomcatConfig.ql 56 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-1004 SensitiveCookieNotHttpOnly.ql 57 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-611 XXE.ql 58 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-611 XXELocal.ql 59 | /Users/pang0lin/CodeQL/ql/java/ql/src/experimental/Security/CWE/CWE-078 ExecTainted.ql -------------------------------------------------------------------------------- /plugins/java_ext/Fastjson.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.dataflow.TaintTracking 4 | import semmle.code.java.dataflow.DataFlow 5 | 6 | class FastjsonSink extends DataFlow::Node { 7 | FastjsonSink(){ 8 | exists(MethodAccess ma,Class c | ma.getMethod().hasName("parseObject") and 9 | ma.getQualifier().getType() = c and 10 | c.hasQualifiedName("com.alibaba.fastjson", "JSON") and 11 | ma.getArgument(0) = this.asExpr() and 12 | ma.getNumArgument() = 1 13 | ) 14 | } 15 | } 16 | 17 | class FastjsonSanitizer extends DataFlow::Node { 18 | FastjsonSanitizer() { 19 | this.getType() instanceof BoxedType or this.getType() instanceof PrimitiveType 20 | } 21 | } 22 | 23 | class FastjsonUnserialize extends TaintTracking::Configuration { 24 | FastjsonUnserialize() { this = "Fastjson Unsearialize" } 25 | 26 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 27 | 28 | override predicate isSink(DataFlow::Node sink) { sink instanceof FastjsonSink } 29 | 30 | override predicate isSanitizer(DataFlow::Node node) { node instanceof FastjsonSanitizer } 31 | 32 | } 33 | 34 | 35 | from DataFlow::PathNode source, DataFlow::PathNode sink, FastjsonUnserialize conf 36 | where 37 | conf.hasFlowPath(source, sink) 38 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 39 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Potential Fastjson Unserialize Vulnerability" 40 | -------------------------------------------------------------------------------- /plugins/java_ext/SQLI1.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.TaintTracking 3 | import semmle.code.java.dataflow.FlowSources 4 | import semmle.code.java.dataflow.ExternalFlow 5 | import DataFlow::PathGraph 6 | 7 | class SQLISink extends DataFlow::Node { 8 | SQLISink(){ 9 | exists(MethodAccess ma,Class c | 10 | (ma.getMethod().getName().substring(0, 5) = "query" or 11 | ma.getMethod().getName() = "update" or 12 | ma.getMethod().getName() = "batchUpdate" or 13 | ma.getMethod().getName() = "execute" 14 | ) and 15 | ma.getQualifier().getType() = c and 16 | c.hasQualifiedName("org.springframework.jdbc.core", "JdbcTemplate") and 17 | ma.getArgument(0) = this.asExpr() 18 | ) 19 | } 20 | } 21 | 22 | class SQLIConfig extends TaintTracking::Configuration { 23 | SQLIConfig() { this = "SQLI Vulnerability" } 24 | 25 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 26 | 27 | override predicate isSink(DataFlow::Node sink) { sink instanceof SQLISink } 28 | 29 | } 30 | 31 | from DataFlow::PathNode source, DataFlow::PathNode sink, SQLIConfig conf 32 | where 33 | conf.hasFlowPath(source, sink) 34 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 35 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Potential SQLI Vulnerability" -------------------------------------------------------------------------------- /plugins/java_ext/SSRF1.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.TaintTracking 3 | import semmle.code.java.dataflow.FlowSources 4 | import semmle.code.java.dataflow.ExternalFlow 5 | import DataFlow::PathGraph 6 | 7 | class SSRFSink extends DataFlow::Node { 8 | SSRFSink(){ 9 | exists(ClassInstanceExpr cie, Class c | 10 | cie.getConstructor().getDeclaringType() = c and 11 | c.hasQualifiedName("org.apache.http", "HttpHost") and 12 | this.asExpr() = cie.getArgument(0) 13 | ) 14 | } 15 | } 16 | 17 | predicate isTaintedString(Expr expSrc, Expr expDest) { 18 | exists(MethodAccess ma | expDest = ma and 19 | ma.getQualifier() = expSrc and 20 | ma.getMethod().getName().substring(0,3) = "get" and 21 | ma.getMethod().getDeclaringType().hasQualifiedName("java.net", "URL") 22 | ) 23 | } 24 | 25 | class SSRFConfig extends TaintTracking::Configuration { 26 | SSRFConfig() { this = "SSRF Vulnerability" } 27 | 28 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 29 | 30 | override predicate isSink(DataFlow::Node sink) { sink instanceof SSRFSink } 31 | 32 | override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { 33 | isTaintedString(node1.asExpr(), node2.asExpr()) 34 | } 35 | } 36 | 37 | from DataFlow::PathNode source, DataFlow::PathNode sink, SSRFConfig conf 38 | where 39 | conf.hasFlowPath(source, sink) 40 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 41 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Potential SSRF Vulnerability" -------------------------------------------------------------------------------- /plugins/java_ext/URLRedirect.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.TaintTracking 3 | import semmle.code.java.dataflow.FlowSources 4 | import semmle.code.java.dataflow.ExternalFlow 5 | import DataFlow::PathGraph 6 | 7 | class URLRedirectSink extends DataFlow::Node { 8 | URLRedirectSink(){ 9 | exists(MethodAccess ma | 10 | ma.getMethod().getName() = "sendRedirect" and 11 | ma.getArgument(0) = this.asExpr() 12 | ) 13 | } 14 | } 15 | 16 | class URLRedirectSanitizer extends DataFlow::Node { //定义过滤类 17 | URLRedirectSanitizer() { 18 | this.toString().indexOf("+") > 0 19 | } 20 | } 21 | 22 | class URLRedirectConfig extends TaintTracking::Configuration { 23 | URLRedirectConfig() { this = "URLRedirect Vulnerability" } 24 | 25 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 26 | 27 | override predicate isSink(DataFlow::Node sink) { sink instanceof URLRedirectSink } 28 | 29 | override predicate isSanitizer(DataFlow::Node node) { node instanceof URLRedirectSanitizer } 30 | 31 | } 32 | 33 | from DataFlow::PathNode source, DataFlow::PathNode sink, URLRedirectConfig conf 34 | where 35 | conf.hasFlowPath(source, sink) 36 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 37 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Potential URLRedirect Vulnerability" -------------------------------------------------------------------------------- /plugins/java_ext/Unserialize.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.dataflow.TaintTracking 4 | import semmle.code.java.dataflow.DataFlow 5 | 6 | class UnserializeSink extends DataFlow::Node { 7 | UnserializeSink(){ 8 | exists(MethodAccess ma,Class c | ma.getMethod().hasName("readObject") and 9 | ma.getQualifier().getType() = c and 10 | c.getASupertype*().hasQualifiedName("java.io", "InputStream") and 11 | this.asExpr() = ma 12 | ) 13 | } 14 | } 15 | 16 | class UnserializeSanitizer extends DataFlow::Node { 17 | UnserializeSanitizer() { 18 | this.getType() instanceof BoxedType or this.getType() instanceof PrimitiveType 19 | } 20 | } 21 | 22 | class JavaUnserialize extends TaintTracking::Configuration { 23 | JavaUnserialize() { this = "Java Unsearialize" } 24 | 25 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 26 | 27 | override predicate isSink(DataFlow::Node sink) { sink instanceof UnserializeSink } 28 | 29 | override predicate isSanitizer(DataFlow::Node node) { node instanceof UnserializeSanitizer } 30 | 31 | } 32 | 33 | 34 | from DataFlow::PathNode source, DataFlow::PathNode sink, JavaUnserialize conf 35 | where 36 | conf.hasFlowPath(source, sink) 37 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 38 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Potential JAVA Unserialize Vulnerability" 39 | -------------------------------------------------------------------------------- /plugins/java_ext/XSS1.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.TaintTracking 3 | import semmle.code.java.dataflow.FlowSources 4 | import semmle.code.java.dataflow.ExternalFlow 5 | import DataFlow::PathGraph 6 | 7 | class XSSSink extends DataFlow::Node { 8 | XSSSink(){ 9 | exists(MethodAccess ma,Class c | 10 | ma.getMethod().getName() = "addAttribute" and 11 | ma.getQualifier().getType() = c and 12 | c.hasQualifiedName("org.springframework.web.servlet", "ModelAndView") and 13 | (ma.getArgument(0) = this.asExpr() or ma.getArgument(1) = this.asExpr()) 14 | ) 15 | or 16 | exists(MethodAccess ma,Class c | 17 | ma.getMethod().getName() = "addObject" and 18 | ma.getQualifier().getType() = c and 19 | c.hasQualifiedName("org.springframework.ui", "Model") and 20 | (ma.getArgument(0) = this.asExpr() or ma.getArgument(1) = this.asExpr()) 21 | ) 22 | or 23 | exists(Method m,Class c,ReturnStmt r | 24 | m.getAnAnnotation().toString().indexOf("ResponseBody") >= 0 and 25 | m.getReturnType() = c and 26 | c.hasQualifiedName("java.lang", "String") and 27 | r.getEnclosingCallable() = m and 28 | r.getResult() = this.asExpr() 29 | ) 30 | 31 | } 32 | } 33 | 34 | class XSSConfig extends TaintTracking::Configuration { 35 | XSSConfig() { this = "XSS Vulnerability" } 36 | 37 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 38 | 39 | override predicate isSink(DataFlow::Node sink) { sink instanceof XSSSink } 40 | 41 | } 42 | 43 | from DataFlow::PathNode source, DataFlow::PathNode sink, XSSConfig conf 44 | where 45 | conf.hasFlowPath(source, sink) 46 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 47 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Potential XSS Vulnerability" -------------------------------------------------------------------------------- /plugins/java_ext/XStream.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.FlowSources 3 | import semmle.code.java.dataflow.TaintTracking 4 | import semmle.code.java.dataflow.DataFlow 5 | 6 | class XStreamSink extends DataFlow::Node { 7 | XStreamSink(){ 8 | exists(MethodAccess ma,Class c | ma.getMethod().hasName("fromXML") and 9 | ma.getQualifier().getType() = c and 10 | c.hasQualifiedName("com.thoughtworks.xstream", "XStream") and 11 | ma.getArgument(0) = this.asExpr() 12 | ) 13 | } 14 | } 15 | predicate isTaintedString(Expr expSrc, Expr expDest) { 16 | exists(Method m, MethodAccess ma | expSrc=ma.getArgument(0) and expDest = ma and ma.getMethod() = m and m.hasName("valueOf") and m.getDeclaringType().toString()="String") 17 | } 18 | 19 | class XStreamSanitizer extends DataFlow::Node { 20 | XStreamSanitizer() { 21 | this.getType() instanceof BoxedType or this.getType() instanceof PrimitiveType 22 | } 23 | } 24 | 25 | class XStreamUnserialize extends TaintTracking::Configuration { 26 | XStreamUnserialize() { this = "XStream Unsearialize" } 27 | 28 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 29 | 30 | override predicate isSink(DataFlow::Node sink) { sink instanceof XStreamSink } 31 | 32 | override predicate isSanitizer(DataFlow::Node node) { node instanceof XStreamSanitizer } 33 | override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { 34 | isTaintedString(node1.asExpr(), node2.asExpr()) 35 | } 36 | 37 | } 38 | 39 | 40 | from DataFlow::PathNode source, DataFlow::PathNode sink, XStreamUnserialize conf 41 | where 42 | conf.hasFlowPath(source, sink) 43 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 44 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Potential XStream Unserialize Vulnerability" 45 | -------------------------------------------------------------------------------- /plugins/javascript/ExceptionXss.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Exception text reinterpreted as HTML 3 | * @description Reinterpreting text from an exception as HTML 4 | * can lead to a cross-site scripting vulnerability. 5 | * @kind path-problem 6 | * @problem.severity warning 7 | * @security-severity 6.1 8 | * @precision high 9 | * @id js/xss-through-exception 10 | * @tags security 11 | * external/cwe/cwe-079 12 | * external/cwe/cwe-116 13 | */ 14 | 15 | import javascript 16 | import semmle.javascript.security.dataflow.ExceptionXssQuery 17 | import DataFlow::PathGraph 18 | 19 | from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink 20 | where cfg.hasFlowPath(source, sink) 21 | select sink.getNode(), source, sink, 22 | "$@ is reinterpreted as HTML without escaping meta-characters.", source.getNode(), 23 | source.getNode().(Source).getDescription() -------------------------------------------------------------------------------- /plugins/javascript/ReflectedXss.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Reflected cross-site scripting 3 | * @description Writing user input directly to an HTTP response allows for 4 | * a cross-site scripting vulnerability. 5 | * @kind path-problem 6 | * @problem.severity error 7 | * @security-severity 6.1 8 | * @precision high 9 | * @id js/reflected-xss 10 | * @tags security 11 | * external/cwe/cwe-079 12 | * external/cwe/cwe-116 13 | */ 14 | 15 | import javascript 16 | import semmle.javascript.security.dataflow.ReflectedXssQuery 17 | import DataFlow::PathGraph 18 | 19 | from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink 20 | where cfg.hasFlowPath(source, sink) 21 | select sink.getNode(), source, sink, "Cross-site scripting vulnerability due to a $@.", 22 | source.getNode(), "user-provided value" -------------------------------------------------------------------------------- /plugins/javascript/StoredXss.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Stored cross-site scripting 3 | * @description Using uncontrolled stored values in HTML allows for 4 | * a stored cross-site scripting vulnerability. 5 | * @kind path-problem 6 | * @problem.severity error 7 | * @security-severity 6.1 8 | * @precision high 9 | * @id js/stored-xss 10 | * @tags security 11 | * external/cwe/cwe-079 12 | * external/cwe/cwe-116 13 | */ 14 | 15 | import javascript 16 | import semmle.javascript.security.dataflow.StoredXssQuery 17 | import DataFlow::PathGraph 18 | 19 | from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink 20 | where cfg.hasFlowPath(source, sink) 21 | select sink.getNode(), source, sink, "Stored cross-site scripting vulnerability due to $@.", 22 | source.getNode(), "stored value" -------------------------------------------------------------------------------- /plugins/javascript/UnsafeHtmlConstruction.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Unsafe HTML constructed from library input 3 | * @description Using externally controlled strings to construct HTML might allow a malicious 4 | * user to perform a cross-site scripting attack. 5 | * @kind path-problem 6 | * @problem.severity error 7 | * @security-severity 6.1 8 | * @precision high 9 | * @id js/html-constructed-from-input 10 | * @tags security 11 | * external/cwe/cwe-079 12 | * external/cwe/cwe-116 13 | */ 14 | 15 | import javascript 16 | import DataFlow::PathGraph 17 | import semmle.javascript.security.dataflow.UnsafeHtmlConstructionQuery 18 | 19 | from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Sink sinkNode 20 | where cfg.hasFlowPath(source, sink) and sink.getNode() = sinkNode 21 | select sinkNode, source, sink, 22 | "This " + sinkNode.describe() + " which depends on $@ might later allow $@.", source.getNode(), 23 | "library input", sinkNode.getSink(), sinkNode.getVulnerabilityKind().toLowerCase() -------------------------------------------------------------------------------- /plugins/javascript/UnsafeJQueryPlugin.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Unsafe jQuery plugin 3 | * @description A jQuery plugin that unintentionally constructs HTML from some of its options may be unsafe to use for clients. 4 | * @kind path-problem 5 | * @problem.severity warning 6 | * @security-severity 6.1 7 | * @precision high 8 | * @id js/unsafe-jquery-plugin 9 | * @tags security 10 | * external/cwe/cwe-079 11 | * external/cwe/cwe-116 12 | * frameworks/jquery 13 | */ 14 | 15 | import javascript 16 | import semmle.javascript.security.dataflow.UnsafeJQueryPluginQuery 17 | import DataFlow::PathGraph 18 | 19 | from 20 | Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, 21 | JQuery::JQueryPluginMethod plugin 22 | where 23 | cfg.hasFlowPath(source, sink) and 24 | source.getNode().(Source).getPlugin() = plugin 25 | select sink.getNode(), source, sink, "Potential XSS vulnerability in the $@.", plugin, 26 | "'$.fn." + plugin.getPluginName() + "' plugin" -------------------------------------------------------------------------------- /plugins/javascript/Xss.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Client-side cross-site scripting 3 | * @description Writing user input directly to the DOM allows for 4 | * a cross-site scripting vulnerability. 5 | * @kind path-problem 6 | * @problem.severity error 7 | * @security-severity 6.1 8 | * @precision high 9 | * @id js/xss 10 | * @tags security 11 | * external/cwe/cwe-079 12 | * external/cwe/cwe-116 13 | */ 14 | 15 | import javascript 16 | import semmle.javascript.security.dataflow.DomBasedXssQuery 17 | import DataFlow::PathGraph 18 | 19 | from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink 20 | where cfg.hasFlowPath(source, sink) 21 | select sink.getNode(), source, sink, 22 | sink.getNode().(Sink).getVulnerabilityKind() + " vulnerability due to $@.", source.getNode(), 23 | "user-provided value" -------------------------------------------------------------------------------- /plugins/javascript/XssThroughDom.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name DOM text reinterpreted as HTML 3 | * @description Reinterpreting text from the DOM as HTML 4 | * can lead to a cross-site scripting vulnerability. 5 | * @kind path-problem 6 | * @problem.severity warning 7 | * @security-severity 6.1 8 | * @precision high 9 | * @id js/xss-through-dom 10 | * @tags security 11 | * external/cwe/cwe-079 12 | * external/cwe/cwe-116 13 | */ 14 | 15 | import javascript 16 | import semmle.javascript.security.dataflow.XssThroughDomQuery 17 | import DataFlow::PathGraph 18 | 19 | from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink 20 | where cfg.hasFlowPath(source, sink) 21 | select sink.getNode(), source, sink, 22 | "$@ is reinterpreted as HTML without escaping meta-characters.", source.getNode(), "DOM text" -------------------------------------------------------------------------------- /plugins/test/test.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.TaintTracking 3 | import semmle.code.java.dataflow.FlowSources 4 | import semmle.code.java.dataflow.ExternalFlow 5 | import DataFlow::PathGraph 6 | 7 | class URLRedirectSink extends DataFlow::Node { 8 | URLRedirectSink(){ 9 | exists(MethodAccess ma | 10 | ma.getMethod().getName() = "sendRedirect" and 11 | ma.getArgument(0) = this.asExpr() 12 | ) 13 | } 14 | } 15 | 16 | class URLRedirectSanitizer extends DataFlow::Node { //定义过滤类 17 | URLRedirectSanitizer() { 18 | this.toString().indexOf("+") > 0 19 | } 20 | } 21 | 22 | class URLRedirectConfig extends TaintTracking::Configuration { 23 | URLRedirectConfig() { this = "URLRedirect Vulnerability" } 24 | 25 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 26 | 27 | override predicate isSink(DataFlow::Node sink) { sink instanceof URLRedirectSink } 28 | 29 | override predicate isSanitizer(DataFlow::Node node) { node instanceof URLRedirectSanitizer } 30 | 31 | } 32 | 33 | from DataFlow::PathNode source, DataFlow::PathNode sink, URLRedirectConfig conf 34 | where 35 | conf.hasFlowPath(source, sink) 36 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 37 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Potential URLRedirect Vulnerability" -------------------------------------------------------------------------------- /plugins/test/test2.ql: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.TaintTracking 3 | import semmle.code.java.dataflow.FlowSources 4 | import semmle.code.java.dataflow.ExternalFlow 5 | import DataFlow::PathGraph 6 | 7 | class SSRFSink extends DataFlow::Node { 8 | SSRFSink(){ 9 | exists(ClassInstanceExpr cie, Class c | 10 | cie.getConstructor().getDeclaringType() = c and 11 | c.hasQualifiedName("org.apache.http", "HttpHost") and 12 | this.asExpr() = cie.getArgument(0) 13 | ) 14 | } 15 | } 16 | 17 | predicate isTaintedString(Expr expSrc, Expr expDest) { 18 | exists(MethodAccess ma | expDest = ma and 19 | ma.getQualifier() = expSrc and 20 | ma.getMethod().getName().substring(0,3) = "get" and 21 | ma.getMethod().getDeclaringType().hasQualifiedName("java.net", "URL") 22 | ) 23 | } 24 | 25 | class SSRFConfig extends TaintTracking::Configuration { 26 | SSRFConfig() { this = "SSRF Vulnerability" } 27 | 28 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } 29 | 30 | override predicate isSink(DataFlow::Node sink) { sink instanceof SSRFSink } 31 | 32 | override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { 33 | isTaintedString(node1.asExpr(), node2.asExpr()) 34 | } 35 | } 36 | 37 | from DataFlow::PathNode source, DataFlow::PathNode sink, SSRFConfig conf 38 | where 39 | conf.hasFlowPath(source, sink) 40 | select source.toString(),source.getNode().getEnclosingCallable(),source.getNode().getEnclosingCallable().getFile().getAbsolutePath(), 41 | sink.toString(),source.getNode().getEnclosingCallable(), sink.getNode().getEnclosingCallable().getFile().getAbsolutePath(), "Potential SSRF Vulnerability" -------------------------------------------------------------------------------- /scan/JavaScan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import time 6 | import codeql 7 | import utils.color_print as color_print 8 | 9 | from scan.Scan import Scan 10 | 11 | 12 | class JavaScan(Scan): 13 | 14 | def __init__(self, ): 15 | Scan.__init__(self, ) 16 | # self.scan_name = ["test", ] 17 | self.scan_name = ["java", "java_ext"] 18 | 19 | def run(self, database): 20 | db = codeql.Database(database) 21 | result_flag = False 22 | result_file = time.strftime('%Y-%m-%d', time.localtime(time.time())) + "_" + str(int(time.time())) + ".csv" 23 | 24 | for scan_name in self.scan_name: 25 | plugin_path = os.path.join("plugins", scan_name) 26 | for plugin in self.getPluginList(plugin_path): 27 | print("startscan: " + plugin) 28 | results = db.query(self.getQuery(os.path.join(plugin_path, plugin))) 29 | if len(results) <= 1: 30 | continue 31 | else: 32 | color_print.info("Found {} num vulnerablity with plugin {}".format(len(results) - 1, plugin)) 33 | if result_flag == False: 34 | self.initResult(result_file) 35 | result_flag = True 36 | 37 | self.saveResult(results, result_file, plugin) 38 | 39 | if not result_flag: 40 | color_print.debug("Not Found any vulnerablity") 41 | else: 42 | color_print.debug("Result Save at path {}".format(os.path.join(self.result_path, result_file))) 43 | 44 | print("Scan Over") 45 | 46 | 47 | -------------------------------------------------------------------------------- /scan/JavaScan.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/scan/JavaScan.pyc -------------------------------------------------------------------------------- /scan/JavascriptScan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import codeql 5 | 6 | from utils.option import get 7 | from scan.Scan import Scan 8 | 9 | class JavascriptScan(Scan): 10 | 11 | def __init__(self, ): 12 | self.scan_name = "javascript" 13 | self.plugin_path = "plugins/" +self.scan_name + "/" 14 | 15 | def run(self, ): 16 | db = codeql.Database(get('dbpath')) 17 | for plugin in self.getPluginList(self.plugin_path): 18 | print("startscan " + plugin) 19 | print(db.query(self.getQuery(self.plugin_path + plugin))) 20 | -------------------------------------------------------------------------------- /scan/Scan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import json 6 | from abc import abstractmethod 7 | import utils.color_print as color_print 8 | 9 | from utils.functions import * 10 | from utils.log import log 11 | 12 | 13 | class Scan(): 14 | 15 | def __init__(self, ): 16 | self.result_path = "out/result/" 17 | 18 | # 获取插件对应的描述信息 19 | @staticmethod 20 | def getInfo(plugin_name): 21 | pass 22 | 23 | # 获取插件对应的ql查询语句 24 | @staticmethod 25 | def getQuery(plugin_name): 26 | if not plugin_name.endswith(".ql"): 27 | plugin_name += ".ql" 28 | return readFile(plugin_name) 29 | 30 | # 获取目录下的插件列表 31 | @staticmethod 32 | def getPluginList(dirpath): 33 | ret = [] 34 | for filename in dirFiles(dirpath): 35 | if filename.endswith(".ql"): 36 | ret.append(filename[:-3]) 37 | return ret 38 | 39 | # 保存扫描结果 40 | def saveResult(self, results, filename, pluginname): 41 | if len(results) <= 1: 42 | return 43 | 44 | for i in range(len(results)): 45 | if i == 0: 46 | continue 47 | result = results[i] 48 | if len(result) < 7: 49 | color_print.error("Error Result: " + json.dumps(result)) 50 | log.error("Error Result: " + json.dumps(result)) 51 | 52 | with open(os.path.join(self.result_path ,filename), 'a') as w: 53 | w.write('%s, %s, %s, %s, %s, %s, %s, %s\n' % (cvsClean(result[0]), cvsClean(result[1]),cvsClean(result[2]),cvsClean(result[3]),cvsClean(result[4]),cvsClean(result[5]),cvsClean(result[6]), pluginname, )) 54 | 55 | # 初始化保存结果的文件 56 | def initResult(self, filename): 57 | title = "Source, SourceFunction, SourcePath, Sink, SinkFunction, SinkPath, Remark, Plugin\n" 58 | with open(os.path.join(self.result_path ,filename), 'a') as w: 59 | w.write(title) 60 | 61 | 62 | # 必须实现的抽象方法,用来执行对应类型的插件扫描 63 | @abstractmethod 64 | def run(): 65 | pass 66 | 67 | -------------------------------------------------------------------------------- /scan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/scan/__init__.py -------------------------------------------------------------------------------- /scan/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/scan/__init__.pyc -------------------------------------------------------------------------------- /scan/__pycache__/JavaScan.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/scan/__pycache__/JavaScan.cpython-39.pyc -------------------------------------------------------------------------------- /scan/__pycache__/JavascriptScan.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/scan/__pycache__/JavascriptScan.cpython-39.pyc -------------------------------------------------------------------------------- /scan/__pycache__/Scan.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/scan/__pycache__/Scan.cpython-39.pyc -------------------------------------------------------------------------------- /scan/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/scan/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/utils/__init__.py -------------------------------------------------------------------------------- /utils/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/utils/__init__.pyc -------------------------------------------------------------------------------- /utils/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/utils/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /utils/__pycache__/check.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/utils/__pycache__/check.cpython-39.pyc -------------------------------------------------------------------------------- /utils/__pycache__/color_print.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/utils/__pycache__/color_print.cpython-39.pyc -------------------------------------------------------------------------------- /utils/__pycache__/functions.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/utils/__pycache__/functions.cpython-39.pyc -------------------------------------------------------------------------------- /utils/__pycache__/log.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/utils/__pycache__/log.cpython-39.pyc -------------------------------------------------------------------------------- /utils/__pycache__/option.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/utils/__pycache__/option.cpython-39.pyc -------------------------------------------------------------------------------- /utils/check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import uuid 6 | import tempfile 7 | from utils.log import log 8 | from utils.option import qlConfig 9 | from utils.functions import execute 10 | 11 | def checkQL(database): 12 | if not os.path.isdir(qlConfig("qlpath")): 13 | log.error("qlpath is not exists, check it at config/config.ini") 14 | return False 15 | 16 | # 生成临时的ql查询文件 17 | temp_path = tempfile.TemporaryDirectory(dir=qlConfig("qlpath")) 18 | ql_filecontent = '''import java\n\nselect "Hello World"\n''' 19 | ql_filepath = os.path.join(temp_path.name, uuid.uuid4().hex + ".ql") 20 | with open(ql_filepath, 'w') as w: 21 | w.write(ql_filecontent) 22 | 23 | # 查询ql文件,判断文件查询结果是否正确 24 | content = execute(f"codeql query run -d {database} {ql_filepath}") 25 | if "Hello World" in content: 26 | return True 27 | else: 28 | log.error("qlpath is not correct, check the qlpack.yml file exists. For example ~/CodeQL/ql/java/ql/test/.") 29 | return False 30 | 31 | def check_maven(): 32 | content = execute("mvn -version") 33 | if "Apache Maven" not in content: 34 | log.error("Maven is not install or the Maven is not in environment path") 35 | return False 36 | return True 37 | 38 | def check_codeql(): 39 | content = execute("codeql --version") 40 | if "release" not in content.lower(): 41 | log.error("codeql is not install or the codeql path is not in environment path") 42 | return False 43 | return True 44 | 45 | def checkEnv(): 46 | return check_codeql() and check_maven() 47 | 48 | def checkDB(database): 49 | if not os.path.isdir(database): 50 | log.error("Database dir is not exists") 51 | return False 52 | dbyml = os.path.join(database, "codeql-database.yml") 53 | dbzip = os.path.join(database, "src.zip") 54 | 55 | if not os.path.isfile(dbyml) or not os.path.isfile(dbzip): 56 | log.error("Database format error") 57 | return False 58 | return True 59 | 60 | if __name__ == "__main__": 61 | check_qlpath() 62 | -------------------------------------------------------------------------------- /utils/color_print.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | def color_print(val,style=0,font=0,back=0): 5 | print(str("\033[{0};{1};{2}m" + val + '\033[0m').format(style, font, back)) 6 | 7 | def debug(val): 8 | color_print(val, 0, 35, 47) 9 | 10 | def error(var): 11 | color_print(var, 0, 31, 47) 12 | 13 | def warning(var): 14 | color_print(var, 0, 33, 47) 15 | 16 | def info(var): 17 | color_print(var, 0, 32, 47) 18 | 19 | -------------------------------------------------------------------------------- /utils/color_print.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/utils/color_print.pyc -------------------------------------------------------------------------------- /utils/functions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import io 6 | import subprocess 7 | 8 | from utils.log import log 9 | from utils.option import qlConfig 10 | 11 | def readFile(filepath): 12 | if not os.path.isfile(filepath): 13 | return "" 14 | with open(filepath) as r: 15 | return r.read() 16 | 17 | def dirFiles(dirpath): 18 | ret = [] 19 | for filename in os.listdir(dirpath): 20 | filepath = os.path.join(dirpath, filename) 21 | if os.path.isfile(filepath): 22 | ret.append(filename) 23 | return ret 24 | 25 | 26 | def execute(cmd): 27 | proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=-1) 28 | proc.wait() 29 | stream_stdout = io.TextIOWrapper(proc.stdout, encoding='utf-8') 30 | stream_stderr = io.TextIOWrapper(proc.stderr, encoding='utf-8') 31 | str_stdout = stream_stdout.read() 32 | if qlConfig("debug").lower() == "on": 33 | str_stderr = stream_stderr.read() 34 | log.warning(str_stderr) 35 | 36 | return str_stdout 37 | 38 | 39 | def cvsClean(content): 40 | return content.replace(",", " ").replace("\n", " ") -------------------------------------------------------------------------------- /utils/log.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import logging 5 | import os 6 | import time 7 | 8 | from utils.option import logConfig 9 | 10 | log_dir = logConfig("path") 11 | 12 | if not os.path.exists(log_dir): 13 | os.mkdir(log_dir) 14 | 15 | class Logger(): 16 | def log(self): 17 | logger = logging.getLogger("logger") 18 | logger.setLevel(logging.INFO) 19 | sh = logging.StreamHandler() 20 | 21 | log_file = os.path.join(log_dir, time.strftime('%Y-%m-%d', time.localtime(time.time())) + ".log") 22 | fh = logging.FileHandler(log_file,encoding="UTF-8") 23 | 24 | # 创建格式器,并将sh,fh设置对应的格式 25 | formator = logging.Formatter(fmt = "%(asctime)s %(filename)s %(levelname)s %(message)s", 26 | datefmt="%Y/%m/%d %X") 27 | sh.setFormatter(formator) 28 | fh.setFormatter(formator) 29 | 30 | # 将处理器,添加至日志器中 31 | logger.addHandler(sh) 32 | logger.addHandler(fh) 33 | 34 | return logger 35 | 36 | log = Logger().log() 37 | -------------------------------------------------------------------------------- /utils/log.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/utils/log.pyc -------------------------------------------------------------------------------- /utils/option.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from configparser import ConfigParser 5 | 6 | def get(option, key): 7 | conn = ConfigParser() 8 | conn.read("config/config.ini") 9 | return conn.get(option, key) 10 | 11 | def qlConfig(key): 12 | return get("codeql", key) 13 | 14 | def logConfig(key): 15 | return get("log", key) 16 | -------------------------------------------------------------------------------- /utils/option.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webraybtl/codeql/3c55bd1e28b2fe660857351a6800e0143c182640/utils/option.pyc --------------------------------------------------------------------------------