├── 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
--------------------------------------------------------------------------------