coretrace-python-analyzer 0.12.0__py3-none-any.whl → 0.14.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- coretrace_python/__init__.py +1 -1
- coretrace_python/abstract/strings.py +98 -0
- coretrace_python/bundled/models/aiohttp/aiohttp_models.py +22 -10
- coretrace_python/bundled/models/django/django_models.py +19 -0
- coretrace_python/bundled/models/tornado/tornado_models.py +16 -1
- coretrace_python/bundled/security/ssrf/plugin.toml +1 -1
- coretrace_python/bundled/security/ssrf/ssrf.py +50 -3
- coretrace_python/engine.py +2 -0
- coretrace_python/plugins/detectors.py +13 -2
- coretrace_python/taint/__init__.py +2 -0
- coretrace_python/taint/engine.py +39 -12
- coretrace_python/taint/models.py +25 -0
- {coretrace_python_analyzer-0.12.0.dist-info → coretrace_python_analyzer-0.14.0.dist-info}/METADATA +1 -1
- {coretrace_python_analyzer-0.12.0.dist-info → coretrace_python_analyzer-0.14.0.dist-info}/RECORD +18 -17
- {coretrace_python_analyzer-0.12.0.dist-info → coretrace_python_analyzer-0.14.0.dist-info}/WHEEL +0 -0
- {coretrace_python_analyzer-0.12.0.dist-info → coretrace_python_analyzer-0.14.0.dist-info}/entry_points.txt +0 -0
- {coretrace_python_analyzer-0.12.0.dist-info → coretrace_python_analyzer-0.14.0.dist-info}/licenses/LICENSE +0 -0
- {coretrace_python_analyzer-0.12.0.dist-info → coretrace_python_analyzer-0.14.0.dist-info}/licenses/NOTICE +0 -0
coretrace_python/__init__.py
CHANGED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""The constant text a string starts with (architecture §18): what ``+`` and f-strings
|
|
2
|
+
build from string literals and from module-level names bound once to one. Anything
|
|
3
|
+
else, a parameter, a call result, an import, ``%`` or ``format``, is text of unknown
|
|
4
|
+
value."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from collections.abc import Mapping
|
|
9
|
+
from types import MappingProxyType
|
|
10
|
+
from typing import ClassVar
|
|
11
|
+
|
|
12
|
+
from coretrace_python.analysis import Analysis, AnalysisContext, AnyAnalysis
|
|
13
|
+
from coretrace_python.cfg import CFGError
|
|
14
|
+
from coretrace_python.hir import nodes
|
|
15
|
+
from coretrace_python.ir.lowering import (
|
|
16
|
+
MODULE_BODY,
|
|
17
|
+
LoweringError,
|
|
18
|
+
PyIRAnalysis,
|
|
19
|
+
analyzable_functions,
|
|
20
|
+
)
|
|
21
|
+
from coretrace_python.ir.model import (
|
|
22
|
+
BinaryOp,
|
|
23
|
+
BuildString,
|
|
24
|
+
Constant,
|
|
25
|
+
ForNext,
|
|
26
|
+
Global,
|
|
27
|
+
Instruction,
|
|
28
|
+
SetGlobal,
|
|
29
|
+
StoreLocal,
|
|
30
|
+
Value,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def leading_text(value: Value, defs: Mapping[Value, Instruction], strings: Mapping[str, str]) -> tuple[str, bool]:
|
|
35
|
+
"""The constant text ``value`` starts with, and whether that text is all of it, given
|
|
36
|
+
the definitions of the function's values and the module's ``strings``."""
|
|
37
|
+
|
|
38
|
+
made = defs.get(value)
|
|
39
|
+
if isinstance(made, Constant):
|
|
40
|
+
return (made.value, True) if isinstance(made.value, str) else ("", False)
|
|
41
|
+
if isinstance(made, Global):
|
|
42
|
+
known = strings.get(made.name)
|
|
43
|
+
return ("", False) if known is None else (known, True)
|
|
44
|
+
if isinstance(made, BinaryOp) and made.operator == "add":
|
|
45
|
+
left, whole = leading_text(made.left, defs, strings)
|
|
46
|
+
if not whole:
|
|
47
|
+
return left, False
|
|
48
|
+
right, whole = leading_text(made.right, defs, strings)
|
|
49
|
+
return left + right, whole
|
|
50
|
+
if isinstance(made, BuildString):
|
|
51
|
+
text = ""
|
|
52
|
+
for part in made.parts:
|
|
53
|
+
piece, whole = leading_text(part, defs, strings)
|
|
54
|
+
text += piece
|
|
55
|
+
if not whole:
|
|
56
|
+
return text, False
|
|
57
|
+
return text, True
|
|
58
|
+
return "", False
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ModuleStringsAnalysis(Analysis[Mapping[str, str]]):
|
|
62
|
+
"""The module-level names bound once, to a string literal, and never rebound: by no
|
|
63
|
+
other statement of the module body, no function assigning them as ``global``, no
|
|
64
|
+
definition of that name. A module that does not lower entirely has none."""
|
|
65
|
+
|
|
66
|
+
name: ClassVar[str] = "abstract.module_strings"
|
|
67
|
+
requires: ClassVar[frozenset[AnyAnalysis]] = frozenset({PyIRAnalysis})
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def compute(cls, ctx: AnalysisContext) -> Mapping[str, str]:
|
|
71
|
+
try:
|
|
72
|
+
functions = [ctx.get(PyIRAnalysis, function) for function in analyzable_functions(ctx.module)]
|
|
73
|
+
except (LoweringError, CFGError):
|
|
74
|
+
return MappingProxyType({})
|
|
75
|
+
body = next((function for function in functions if function.name == MODULE_BODY), None)
|
|
76
|
+
if body is None:
|
|
77
|
+
return MappingProxyType({})
|
|
78
|
+
defs = {i.result: i for block in body.blocks for i in block.instructions if i.result is not None}
|
|
79
|
+
stored: dict[str, list[Value | None]] = {}
|
|
80
|
+
for block in body.blocks:
|
|
81
|
+
for instruction in block.instructions:
|
|
82
|
+
if isinstance(instruction, StoreLocal):
|
|
83
|
+
stored.setdefault(instruction.name, []).append(instruction.value)
|
|
84
|
+
if isinstance(block.terminator, ForNext):
|
|
85
|
+
# A ``for`` binds its target to each item.
|
|
86
|
+
stored.setdefault(block.terminator.target, []).append(None)
|
|
87
|
+
rebound = {
|
|
88
|
+
i.name for function in functions for block in function.blocks for i in block.instructions
|
|
89
|
+
if isinstance(i, SetGlobal)
|
|
90
|
+
}
|
|
91
|
+
rebound |= {s.name for s in ctx.module.body if isinstance(s, nodes.Function | nodes.Class)}
|
|
92
|
+
found: dict[str, str] = {}
|
|
93
|
+
for name, values in stored.items():
|
|
94
|
+
value = values[0] if len(values) == 1 and name not in rebound else None
|
|
95
|
+
made = defs.get(value) if value is not None else None
|
|
96
|
+
if isinstance(made, Constant) and isinstance(made.value, str):
|
|
97
|
+
found[name] = made.value
|
|
98
|
+
return MappingProxyType(found)
|
|
@@ -12,6 +12,7 @@ from coretrace_python.semantic.symbols import SymbolId
|
|
|
12
12
|
from coretrace_python.taint import (
|
|
13
13
|
EntryPoint,
|
|
14
14
|
Model,
|
|
15
|
+
RequestObject,
|
|
15
16
|
RouteRegistrar,
|
|
16
17
|
Sink,
|
|
17
18
|
Source,
|
|
@@ -22,6 +23,23 @@ from coretrace_python.taint import (
|
|
|
22
23
|
_METHODS = ("get", "post", "put", "patch", "delete", "head", "options")
|
|
23
24
|
_REQUEST_CLASSES = ("aiohttp.web.Request", "aiohttp.web_request.Request")
|
|
24
25
|
_ROUTERS = ("aiohttp.web.Application.router", "aiohttp.web.UrlDispatcher")
|
|
26
|
+
# ``@routes.get('/')`` on a ``RouteTableDef``.
|
|
27
|
+
_ROUTE_TABLES = tuple(f"aiohttp.web.RouteTableDef.{method}" for method in (*_METHODS, "route", "view"))
|
|
28
|
+
# ``app.router.add_route('GET', '/', handler)``, ``app.router.add_get('/', handler)`` and
|
|
29
|
+
# ``app.add_routes([web.get('/', handler)])``, each with the argument naming the handler.
|
|
30
|
+
_ROUTE_CALLS = (
|
|
31
|
+
*((f"{router}.add_route", 2) for router in _ROUTERS),
|
|
32
|
+
*((f"{router}.add_{method}", 1) for router in _ROUTERS for method in (*_METHODS, "view")),
|
|
33
|
+
("aiohttp.web.route", 2),
|
|
34
|
+
*((f"aiohttp.web.{method}", 1) for method in (*_METHODS, "view")),
|
|
35
|
+
)
|
|
36
|
+
# What a request gives as text; its body (``json()``, ``text()``, ``read()``) may hold a
|
|
37
|
+
# structure.
|
|
38
|
+
_REQUEST_TEXT = (
|
|
39
|
+
"query", "rel_url", "url", "path", "raw_path", "path_qs", "query_string", "match_info", "headers",
|
|
40
|
+
"raw_headers", "cookies", "method", "host", "scheme", "remote", "forwarded", "content_type", "charset",
|
|
41
|
+
"if_modified_since", "post",
|
|
42
|
+
)
|
|
25
43
|
_REDIRECTS = ("HTTPFound", "HTTPMovedPermanently", "HTTPSeeOther", "HTTPTemporaryRedirect", "HTTPPermanentRedirect")
|
|
26
44
|
_CLIENT_FUNCTIONS = (
|
|
27
45
|
*(f"aiohttp.ClientSession.{method}" for method in (*_METHODS, "request")),
|
|
@@ -40,18 +58,12 @@ class AiohttpModels(ModelPlugin):
|
|
|
40
58
|
# The whole request is attacker-controlled: its query, match info, headers,
|
|
41
59
|
# cookies and body all come from the client.
|
|
42
60
|
*(TypedParameter(_sym(cls), "http") for cls in _REQUEST_CLASSES),
|
|
43
|
-
|
|
44
|
-
*(
|
|
45
|
-
# ``app.router.add_route('GET', '/', handler)`` and ``app.router.add_get('/', handler)``.
|
|
46
|
-
*(RouteRegistrar(_sym(f"{router}.add_route"), 2, "http", keyword="handler") for router in _ROUTERS),
|
|
61
|
+
*(EntryPoint(_sym(table), "http") for table in _ROUTE_TABLES),
|
|
62
|
+
*(RouteRegistrar(_sym(call), argument, "http", keyword="handler") for call, argument in _ROUTE_CALLS),
|
|
47
63
|
*(
|
|
48
|
-
|
|
49
|
-
for
|
|
50
|
-
for method in (*_METHODS, "view")
|
|
64
|
+
RequestObject(_sym(symbol), _REQUEST_TEXT)
|
|
65
|
+
for symbol in (*_REQUEST_CLASSES, *_ROUTE_TABLES, *(call for call, _ in _ROUTE_CALLS))
|
|
51
66
|
),
|
|
52
|
-
# ``app.add_routes([web.get('/', handler)])``: the route definitions name the handler.
|
|
53
|
-
RouteRegistrar(_sym("aiohttp.web.route"), 2, "http", keyword="handler"),
|
|
54
|
-
*(RouteRegistrar(_sym(f"aiohttp.web.{method}"), 1, "http", keyword="handler") for method in (*_METHODS, "view")),
|
|
55
67
|
Sink(_sym("aiohttp.web.Response"), TaintKind.HTML),
|
|
56
68
|
Sink(_sym("aiohttp.web.FileResponse"), TaintKind.PATH),
|
|
57
69
|
*(Sink(_sym(f"aiohttp.web.{exception}"), TaintKind.REDIRECT) for exception in _REDIRECTS),
|
|
@@ -17,6 +17,7 @@ from coretrace_python.taint import (
|
|
|
17
17
|
AuthorizationGuard,
|
|
18
18
|
EntryPoint,
|
|
19
19
|
Model,
|
|
20
|
+
RequestObject,
|
|
20
21
|
RouteRegistrar,
|
|
21
22
|
Sanitizer,
|
|
22
23
|
Sink,
|
|
@@ -88,6 +89,23 @@ _AUTHORIZATION_DECORATORS = (
|
|
|
88
89
|
)
|
|
89
90
|
|
|
90
91
|
|
|
92
|
+
# What a request object gives as text: the query string, form fields, headers, cookies
|
|
93
|
+
# and path. Its body, its files and REST framework's parsed ``data`` may hold a structure.
|
|
94
|
+
_REQUEST_TEXT = (
|
|
95
|
+
"GET", "POST", "COOKIES", "META", "headers", "path", "path_info", "method", "scheme", "encoding",
|
|
96
|
+
"content_type", "content_params", "resolver_match", "get_full_path", "get_full_path_info",
|
|
97
|
+
"build_absolute_uri", "get_host", "get_port", "get_signed_cookie", "query_params",
|
|
98
|
+
)
|
|
99
|
+
# Every input a request object comes from: an annotated parameter, a class-based view's
|
|
100
|
+
# method or ``self.request``, a decorated view, a view registered in a URL configuration.
|
|
101
|
+
_REQUEST_INPUTS = (
|
|
102
|
+
*_REQUEST_CLASSES,
|
|
103
|
+
*_VIEW_BASES,
|
|
104
|
+
*(f"{base}.request" for base in _VIEW_BASES),
|
|
105
|
+
*_VIEW_DECORATORS,
|
|
106
|
+
*_ROUTE_REGISTRARS,
|
|
107
|
+
)
|
|
108
|
+
|
|
91
109
|
_TARGET_ONLY = ((TaintKind.REDIRECT, (0,)),)
|
|
92
110
|
|
|
93
111
|
|
|
@@ -103,6 +121,7 @@ class DjangoModels(ModelPlugin):
|
|
|
103
121
|
# ``self.request`` in a class-based view: the attribute is inherited from the base.
|
|
104
122
|
*(Source(_sym(f"{base}.request"), "http") for base in _VIEW_BASES),
|
|
105
123
|
*(EntryPoint(_sym(decorator), "http") for decorator in _VIEW_DECORATORS),
|
|
124
|
+
*(RequestObject(_sym(symbol), _REQUEST_TEXT) for symbol in _REQUEST_INPUTS),
|
|
106
125
|
Sink(_sym("django.db.connection.cursor.execute"), TaintKind.SQL | TaintKind.CREDENTIAL, ((TaintKind.SQL, (0,)),)),
|
|
107
126
|
Sink(_sym("django.db.connection.cursor.executemany"), TaintKind.SQL | TaintKind.CREDENTIAL, ((TaintKind.SQL, (0,)),)),
|
|
108
127
|
Sink(_sym("django.db.models.expressions.RawSQL"), TaintKind.SQL | TaintKind.CREDENTIAL),
|
|
@@ -8,7 +8,16 @@ from typing import ClassVar
|
|
|
8
8
|
|
|
9
9
|
from coretrace_python.plugins import ModelPlugin
|
|
10
10
|
from coretrace_python.semantic.symbols import SymbolId
|
|
11
|
-
from coretrace_python.taint import
|
|
11
|
+
from coretrace_python.taint import (
|
|
12
|
+
TEXT_KINDS,
|
|
13
|
+
EntryPoint,
|
|
14
|
+
Model,
|
|
15
|
+
RequestObject,
|
|
16
|
+
Sanitizer,
|
|
17
|
+
Sink,
|
|
18
|
+
Source,
|
|
19
|
+
TaintKind,
|
|
20
|
+
)
|
|
12
21
|
|
|
13
22
|
_HANDLERS = ("tornado.web.RequestHandler", "tornado.websocket.WebSocketHandler")
|
|
14
23
|
# Text, which cannot hold a query operator; the request object holds the raw body too.
|
|
@@ -16,6 +25,11 @@ _TEXT_METHODS = (
|
|
|
16
25
|
"get_argument", "get_arguments", "get_query_argument", "get_query_arguments",
|
|
17
26
|
"get_body_argument", "get_body_arguments", "get_cookie", "path_args", "path_kwargs",
|
|
18
27
|
)
|
|
28
|
+
# What ``self.request`` gives as text; its body and files may hold a structure.
|
|
29
|
+
_REQUEST_TEXT = (
|
|
30
|
+
"arguments", "query_arguments", "body_arguments", "query", "uri", "path", "headers", "cookies",
|
|
31
|
+
"method", "host", "host_name", "remote_ip", "protocol", "version", "full_url",
|
|
32
|
+
)
|
|
19
33
|
_CLIENTS = ("tornado.httpclient.AsyncHTTPClient.fetch", "tornado.httpclient.HTTPClient.fetch")
|
|
20
34
|
_TARGET_ONLY = ((TaintKind.REDIRECT, (0,)),)
|
|
21
35
|
|
|
@@ -33,6 +47,7 @@ class TornadoModels(ModelPlugin):
|
|
|
33
47
|
EntryPoint(_sym("tornado.websocket.WebSocketHandler"), "http"),
|
|
34
48
|
*(Source(_sym(f"{handler}.{method}"), "http", TEXT_KINDS) for handler in _HANDLERS for method in _TEXT_METHODS),
|
|
35
49
|
*(Source(_sym(f"{handler}.request"), "http") for handler in _HANDLERS),
|
|
50
|
+
*(RequestObject(_sym(f"{handler}.request"), _REQUEST_TEXT) for handler in _HANDLERS),
|
|
36
51
|
*(Sink(_sym(f"{handler}.write"), TaintKind.HTML) for handler in _HANDLERS),
|
|
37
52
|
*(Sink(_sym(f"{handler}.finish"), TaintKind.HTML) for handler in _HANDLERS),
|
|
38
53
|
*(Sink(_sym(f"{handler}.redirect"), TaintKind.REDIRECT, _TARGET_ONLY) for handler in _HANDLERS),
|
|
@@ -1,12 +1,37 @@
|
|
|
1
|
-
"""Server-side request forgery: attacker-controlled input reaching a SSRF sink.
|
|
1
|
+
"""Server-side request forgery: attacker-controlled input reaching a SSRF sink.
|
|
2
|
+
|
|
3
|
+
How the URL is built, with ``+`` and f-strings, can prove part of its destination. Constant
|
|
4
|
+
text fixing ``scheme://host`` and ending the authority with ``/``, ``?`` or ``#`` before
|
|
5
|
+
any input keeps the input from choosing the host; the input still chooses the path, which
|
|
6
|
+
may reach a sensitive resource of that host, and a redirect may lead elsewhere, so the
|
|
7
|
+
flow is a hotspot. With the path fixed too, the input reaching the query or fragment
|
|
8
|
+
only, and redirects disabled in the call, the destination is fixed and the flow is
|
|
9
|
+
refuted. Anything unproven, a base of unknown value or a URL built in a helper function,
|
|
10
|
+
stays a vulnerability.
|
|
11
|
+
"""
|
|
2
12
|
|
|
3
13
|
from __future__ import annotations
|
|
4
14
|
|
|
15
|
+
import re
|
|
5
16
|
from typing import ClassVar
|
|
6
17
|
|
|
18
|
+
from coretrace_python.abstract.strings import ModuleStringsAnalysis, leading_text
|
|
19
|
+
from coretrace_python.analysis import AnyAnalysis
|
|
7
20
|
from coretrace_python.findings import Severity
|
|
8
|
-
from coretrace_python.
|
|
9
|
-
from coretrace_python.
|
|
21
|
+
from coretrace_python.findings.refutation import Status, Verdict
|
|
22
|
+
from coretrace_python.hir import nodes
|
|
23
|
+
from coretrace_python.ir.ssa import SSAAnalysis
|
|
24
|
+
from coretrace_python.plugins import PluginContext, TaintDetector
|
|
25
|
+
from coretrace_python.taint import TaintFlow, TaintKind
|
|
26
|
+
|
|
27
|
+
# ``scheme://`` and an authority ended by ``/``, ``?`` or ``#``. A backslash, which some
|
|
28
|
+
# URL parsers take for a slash, does not end it here.
|
|
29
|
+
_FIXED_HOST = re.compile(r"[A-Za-z][A-Za-z0-9+.-]*://[^/?#\\]+[/?#]")
|
|
30
|
+
# The same with the path fixed too: what follows is query or fragment.
|
|
31
|
+
_FIXED_PATH = re.compile(r"[A-Za-z][A-Za-z0-9+.-]*://[^/?#\\]+(?:/[^?#]*)?[?#]")
|
|
32
|
+
# A client honours the one of these keywords it knows and rejects the other with a
|
|
33
|
+
# ``TypeError``: either way, the call follows no redirect.
|
|
34
|
+
_NO_REDIRECTS = ("allow_redirects", "follow_redirects")
|
|
10
35
|
|
|
11
36
|
|
|
12
37
|
class SsrfPlugin(TaintDetector):
|
|
@@ -15,3 +40,25 @@ class SsrfPlugin(TaintDetector):
|
|
|
15
40
|
kind: ClassVar[TaintKind] = TaintKind.SSRF
|
|
16
41
|
severity: ClassVar[Severity] = Severity.HIGH
|
|
17
42
|
title: ClassVar[str] = "Server-side request forgery"
|
|
43
|
+
requires: ClassVar[frozenset[AnyAnalysis]] = TaintDetector.requires | {SSAAnalysis, ModuleStringsAnalysis}
|
|
44
|
+
|
|
45
|
+
def judge(self, ctx: PluginContext, function: nodes.Function, flow: TaintFlow, verdict: Verdict) -> Verdict:
|
|
46
|
+
if flow.through is not None:
|
|
47
|
+
return verdict # the URL is built in the callee
|
|
48
|
+
ssa = ctx.get(SSAAnalysis, function)
|
|
49
|
+
defs = {i.result: i for block in ssa.blocks for i in block.instructions if i.result is not None}
|
|
50
|
+
text, _ = leading_text(flow.argument, defs, ctx.get(ModuleStringsAnalysis))
|
|
51
|
+
host = _FIXED_HOST.match(text)
|
|
52
|
+
if host is None:
|
|
53
|
+
return verdict
|
|
54
|
+
arguments = flow.sink_arguments
|
|
55
|
+
no_redirects = arguments is not None and any(arguments.given(name) == (True, "False") for name in _NO_REDIRECTS)
|
|
56
|
+
if _FIXED_PATH.match(text) is not None:
|
|
57
|
+
if no_redirects:
|
|
58
|
+
return Verdict(flow, Status.REFUTED, f"the destination is fixed by {text!r} and redirects are disabled")
|
|
59
|
+
left = "a redirect may lead elsewhere"
|
|
60
|
+
else:
|
|
61
|
+
left = "the input chooses the path" + ("" if no_redirects else ", and a redirect may lead elsewhere")
|
|
62
|
+
if verdict.status is not Status.VULNERABILITY:
|
|
63
|
+
return verdict
|
|
64
|
+
return Verdict(flow, Status.HOTSPOT, f"the host is fixed by {host.group(0)!r}, but {left}")
|
coretrace_python/engine.py
CHANGED
|
@@ -17,6 +17,7 @@ from typing import Any, ClassVar
|
|
|
17
17
|
|
|
18
18
|
from coretrace_python import __version__
|
|
19
19
|
from coretrace_python.abstract import ConstantPropagation, HeapAnalysis, RangeAnalysis
|
|
20
|
+
from coretrace_python.abstract.strings import ModuleStringsAnalysis
|
|
20
21
|
from coretrace_python.analysis import (
|
|
21
22
|
AnalysisContext,
|
|
22
23
|
AnalysisManager,
|
|
@@ -143,6 +144,7 @@ ALL_ANALYSES: tuple[AnyAnalysis, ...] = (
|
|
|
143
144
|
DefUseAnalysis,
|
|
144
145
|
ConstantPropagation,
|
|
145
146
|
RangeAnalysis,
|
|
147
|
+
ModuleStringsAnalysis,
|
|
146
148
|
HeapAnalysis,
|
|
147
149
|
CallGraphAnalysis,
|
|
148
150
|
SummaryAnalysis,
|
|
@@ -13,13 +13,14 @@ from typing import ClassVar
|
|
|
13
13
|
|
|
14
14
|
from coretrace_python.analysis import AnyAnalysis
|
|
15
15
|
from coretrace_python.findings import Confidence, Finding, Severity
|
|
16
|
-
from coretrace_python.findings.refutation import RefutationAnalysis, Status
|
|
16
|
+
from coretrace_python.findings.refutation import RefutationAnalysis, Status, Verdict
|
|
17
|
+
from coretrace_python.hir import nodes
|
|
17
18
|
from coretrace_python.interprocedural import CallGraphAnalysis
|
|
18
19
|
from coretrace_python.ir.model import Call, Instruction, Symbol, Value
|
|
19
20
|
from coretrace_python.ir.ssa import SSAAnalysis
|
|
20
21
|
from coretrace_python.plugins.api import Plugin, PluginContext
|
|
21
22
|
from coretrace_python.semantic.symbols import SymbolId
|
|
22
|
-
from coretrace_python.taint import TaintAnalysis, TaintKind
|
|
23
|
+
from coretrace_python.taint import TaintAnalysis, TaintFlow, TaintKind
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class TaintDetector(Plugin):
|
|
@@ -40,6 +41,9 @@ class TaintDetector(Plugin):
|
|
|
40
41
|
if not flow.kinds & self.kind:
|
|
41
42
|
continue
|
|
42
43
|
verdict = verdicts.verdict(flow)
|
|
44
|
+
if verdict.status is Status.REFUTED:
|
|
45
|
+
continue
|
|
46
|
+
verdict = self.judge(ctx, function, flow, verdict)
|
|
43
47
|
if verdict.status is Status.REFUTED:
|
|
44
48
|
continue
|
|
45
49
|
message = f"{self.title}: {flow.source.label} input reaches {flow.sink.symbol}"
|
|
@@ -70,6 +74,13 @@ class TaintDetector(Plugin):
|
|
|
70
74
|
)
|
|
71
75
|
return findings
|
|
72
76
|
|
|
77
|
+
def judge(self, ctx: PluginContext, function: nodes.Function, flow: TaintFlow, verdict: Verdict) -> Verdict:
|
|
78
|
+
"""This rule's verdict on ``flow``, given the refutation's: the same, unless the rule
|
|
79
|
+
knows more about its own sinks. The refutation's verdict, which other consumers
|
|
80
|
+
of the flow read, does not change."""
|
|
81
|
+
|
|
82
|
+
return verdict
|
|
83
|
+
|
|
73
84
|
|
|
74
85
|
def _lower(confidence: Confidence) -> Confidence:
|
|
75
86
|
return {Confidence.HIGH: Confidence.MEDIUM}.get(confidence, Confidence.LOW)
|
|
@@ -17,6 +17,7 @@ from coretrace_python.taint.models import (
|
|
|
17
17
|
ModelError,
|
|
18
18
|
ModelTable,
|
|
19
19
|
NamedParameter,
|
|
20
|
+
RequestObject,
|
|
20
21
|
RouteRegistrar,
|
|
21
22
|
SafeArgument,
|
|
22
23
|
Sanitizer,
|
|
@@ -45,6 +46,7 @@ __all__ = [
|
|
|
45
46
|
"ModelTable",
|
|
46
47
|
"NamedParameter",
|
|
47
48
|
"RegisteredRoutes",
|
|
49
|
+
"RequestObject",
|
|
48
50
|
"RouteRegistrar",
|
|
49
51
|
"Routes",
|
|
50
52
|
"SafeArgument",
|
coretrace_python/taint/engine.py
CHANGED
|
@@ -74,6 +74,7 @@ from coretrace_python.semantic.scopes import ScopeAnalysis, ScopeId, ScopeTable
|
|
|
74
74
|
from coretrace_python.semantic.symbols import SymbolAnalysis, SymbolId, SymbolTable
|
|
75
75
|
from coretrace_python.source import SourceSpan
|
|
76
76
|
from coretrace_python.taint.models import (
|
|
77
|
+
TEXT_KINDS,
|
|
77
78
|
EntryPoint,
|
|
78
79
|
ModelTable,
|
|
79
80
|
SecurityModelAnalysis,
|
|
@@ -84,6 +85,9 @@ from coretrace_python.taint.models import (
|
|
|
84
85
|
from coretrace_python.taint.routes import RegisteredRoutes, Routes
|
|
85
86
|
from coretrace_python.taint.templates import EscapedTemplates
|
|
86
87
|
|
|
88
|
+
# What only a structure carries, and text does not.
|
|
89
|
+
_STRUCTURED = TaintKind.ALL & ~TEXT_KINDS
|
|
90
|
+
|
|
87
91
|
|
|
88
92
|
@dataclass(frozen=True)
|
|
89
93
|
class Taint:
|
|
@@ -274,19 +278,42 @@ class _TaintProblem(DataflowProblem[State]):
|
|
|
274
278
|
taint = Taint.none()
|
|
275
279
|
symbol = self.symbols.get(instruction.result) if instruction.result else None
|
|
276
280
|
if symbol is not None:
|
|
277
|
-
|
|
278
|
-
if source is not None:
|
|
279
|
-
taint = Taint(source.kinds, frozenset({source}))
|
|
281
|
+
taint = self.covered(symbol)
|
|
280
282
|
if isinstance(instruction, Symbol | Compare):
|
|
281
283
|
return taint
|
|
282
284
|
for operand in instruction.operands():
|
|
283
|
-
|
|
285
|
+
operand_taint = state.get(operand, Taint.none())
|
|
286
|
+
if isinstance(instruction, GetAttr):
|
|
287
|
+
operand_taint = self.attribute(operand_taint, instruction.attribute)
|
|
288
|
+
taint = taint.join(operand_taint)
|
|
284
289
|
if isinstance(instruction, BuildList | BuildTuple | BuildDict | BuildSet):
|
|
285
290
|
# ``[*xs]`` and ``{**d}`` copy the contents of what they unpack.
|
|
286
291
|
for unpacked in instruction.unpacked:
|
|
287
292
|
taint = taint.join(self.deep(unpacked, state))
|
|
288
293
|
return taint
|
|
289
294
|
|
|
295
|
+
def covered(self, symbol: SymbolId) -> Taint:
|
|
296
|
+
"""The taint of the source covering ``symbol``, if any. Below a request source, as
|
|
297
|
+
``self.request.GET`` is, it is the taint of the request attribute read."""
|
|
298
|
+
|
|
299
|
+
source = self.models.source_covering(symbol)
|
|
300
|
+
if source is None:
|
|
301
|
+
return Taint.none()
|
|
302
|
+
taint = Taint(source.kinds, frozenset({source}))
|
|
303
|
+
if symbol == source.symbol:
|
|
304
|
+
return taint
|
|
305
|
+
read = symbol.canonical_name.removeprefix(f"{source.symbol.canonical_name}.").split(".")[0]
|
|
306
|
+
return self.attribute(taint, read)
|
|
307
|
+
|
|
308
|
+
def attribute(self, taint: Taint, name: str) -> Taint:
|
|
309
|
+
"""What attribute ``name`` of a value carrying ``taint`` carries: text when every
|
|
310
|
+
source of the value is a request object listing ``name`` among its text attributes."""
|
|
311
|
+
|
|
312
|
+
requests = [self.models.request_object(source.symbol) for source in taint.sources]
|
|
313
|
+
if taint and all(request is not None and name in request.text for request in requests):
|
|
314
|
+
return taint.without(_STRUCTURED)
|
|
315
|
+
return taint
|
|
316
|
+
|
|
290
317
|
def call(self, call: Call, state: dict[Key, Taint], flows: list[TaintFlow]) -> Taint:
|
|
291
318
|
arguments = tuple(self.deep(a, state) for a in call.arguments)
|
|
292
319
|
keywords = Taint.none()
|
|
@@ -420,11 +447,7 @@ class _TaintProblem(DataflowProblem[State]):
|
|
|
420
447
|
if cleared:
|
|
421
448
|
return everything.without(TaintKind(cleared))
|
|
422
449
|
# A method on a tainted object returns tainted data (``request.args.get``).
|
|
423
|
-
|
|
424
|
-
source = self.models.source_covering(symbol)
|
|
425
|
-
if source is not None:
|
|
426
|
-
return everything.join(Taint(source.kinds, frozenset({source})))
|
|
427
|
-
return everything
|
|
450
|
+
return everything.join(state.get(call.callee, Taint.none())).join(self.covered(symbol))
|
|
428
451
|
|
|
429
452
|
def guarded(self, sink: Sink | None, arguments: Arguments) -> Sink | None:
|
|
430
453
|
"""The sink as this call makes it: an argument declared safe (``yaml.load`` with
|
|
@@ -777,7 +800,8 @@ def parameter_sources(
|
|
|
777
800
|
entry point (``self`` excepted for a method), including one registered elsewhere
|
|
778
801
|
(``routes``), and every parameter annotated with a typed-parameter symbol. An entry
|
|
779
802
|
point's parameter annotated with scalars is text: FastAPI validates it before the
|
|
780
|
-
handler runs, and a scalar cannot hold a query operator.
|
|
803
|
+
handler runs, and a scalar cannot hold a query operator. So are the URL parameters
|
|
804
|
+
a view receives after a request object."""
|
|
781
805
|
|
|
782
806
|
owner = _owner(module, function)
|
|
783
807
|
sources: dict[int, Source] = {}
|
|
@@ -786,10 +810,13 @@ def parameter_sources(
|
|
|
786
810
|
entry = function_entry_point(function, module, models, scopes, symbols, instances, routes)
|
|
787
811
|
if entry is not None:
|
|
788
812
|
first = 1 if owner is not None else 0
|
|
813
|
+
request = models.request_object(entry.symbol)
|
|
789
814
|
for index in range(first, len(function.parameters)):
|
|
790
815
|
annotation = function.parameters[index].annotation
|
|
791
|
-
|
|
792
|
-
|
|
816
|
+
text = (request is not None and index > first) or (
|
|
817
|
+
annotation is not None and _scalar(annotation, symbols, enclosing)
|
|
818
|
+
)
|
|
819
|
+
kinds = entry.kinds & ~_STRUCTURED if text else entry.kinds
|
|
793
820
|
sources[index] = Source(entry.symbol, entry.label, kinds)
|
|
794
821
|
for index, parameter in enumerate(function.parameters):
|
|
795
822
|
if parameter.annotation is None:
|
coretrace_python/taint/models.py
CHANGED
|
@@ -205,6 +205,22 @@ class TemplateRender:
|
|
|
205
205
|
keyword: str = "template_name"
|
|
206
206
|
|
|
207
207
|
|
|
208
|
+
@dataclass(frozen=True)
|
|
209
|
+
class RequestObject:
|
|
210
|
+
"""The inputs from ``symbol`` are request objects; ``symbol`` names an entry point, a
|
|
211
|
+
route registrar, a typed parameter or a source. A view receives the request first
|
|
212
|
+
(after ``self``), then URL parameters, which are text. Reading one of the request's
|
|
213
|
+
``text`` attributes (``GET``, ``headers``) gives text, while its body and files keep the
|
|
214
|
+
kinds of the request: text carries ``TEXT_KINDS``."""
|
|
215
|
+
|
|
216
|
+
symbol: SymbolId
|
|
217
|
+
text: tuple[str, ...] = ()
|
|
218
|
+
|
|
219
|
+
def __post_init__(self) -> None:
|
|
220
|
+
# Sorted: the models' repr is part of the cache key, the same in every process.
|
|
221
|
+
object.__setattr__(self, "text", tuple(sorted(set(self.text))))
|
|
222
|
+
|
|
223
|
+
|
|
208
224
|
@dataclass(frozen=True)
|
|
209
225
|
class AuthorizationGuard:
|
|
210
226
|
"""A decorator, or a condition, that restricts who reaches the code behind it; a
|
|
@@ -228,6 +244,7 @@ Model = (
|
|
|
228
244
|
| SafeArgument
|
|
229
245
|
| TemplateRender
|
|
230
246
|
| Members
|
|
247
|
+
| RequestObject
|
|
231
248
|
)
|
|
232
249
|
|
|
233
250
|
|
|
@@ -246,6 +263,7 @@ class ModelTable:
|
|
|
246
263
|
safe_arguments: tuple[SafeArgument, ...] = ()
|
|
247
264
|
template_renders: tuple[TemplateRender, ...] = ()
|
|
248
265
|
members: tuple[Members, ...] = ()
|
|
266
|
+
request_objects: tuple[RequestObject, ...] = ()
|
|
249
267
|
_by_symbol: dict[type[Model], dict[SymbolId, Model]] = field(
|
|
250
268
|
init=False, repr=False, compare=False
|
|
251
269
|
)
|
|
@@ -262,6 +280,7 @@ class ModelTable:
|
|
|
262
280
|
RouteRegistrar: {m.symbol: m for m in self.route_registrars},
|
|
263
281
|
SafeArgument: {m.symbol: m for m in self.safe_arguments},
|
|
264
282
|
TemplateRender: {m.symbol: m for m in self.template_renders},
|
|
283
|
+
RequestObject: {m.symbol: m for m in self.request_objects},
|
|
265
284
|
}
|
|
266
285
|
object.__setattr__(self, "_by_symbol", MappingProxyType(index))
|
|
267
286
|
|
|
@@ -330,6 +349,7 @@ class ModelTable:
|
|
|
330
349
|
self.safe_arguments,
|
|
331
350
|
self.template_renders,
|
|
332
351
|
self.members,
|
|
352
|
+
self.request_objects,
|
|
333
353
|
)
|
|
334
354
|
|
|
335
355
|
def sanitizer(self, symbol: SymbolId) -> Sanitizer | None:
|
|
@@ -344,6 +364,10 @@ class ModelTable:
|
|
|
344
364
|
found = self._by_symbol[TemplateRender].get(symbol)
|
|
345
365
|
return found if isinstance(found, TemplateRender) else None
|
|
346
366
|
|
|
367
|
+
def request_object(self, symbol: SymbolId) -> RequestObject | None:
|
|
368
|
+
found = self._by_symbol[RequestObject].get(symbol)
|
|
369
|
+
return found if isinstance(found, RequestObject) else None
|
|
370
|
+
|
|
347
371
|
def members_by_class(self) -> Mapping[SymbolId, Members]:
|
|
348
372
|
"""The ``Members`` models by class, as symbol resolution reads them."""
|
|
349
373
|
|
|
@@ -408,6 +432,7 @@ class SecurityModelRegistry:
|
|
|
408
432
|
safe_arguments=tuple(m for m in models if isinstance(m, SafeArgument)),
|
|
409
433
|
template_renders=tuple(m for m in models if isinstance(m, TemplateRender)),
|
|
410
434
|
members=tuple(m for m in models if isinstance(m, Members)),
|
|
435
|
+
request_objects=tuple(m for m in models if isinstance(m, RequestObject)),
|
|
411
436
|
)
|
|
412
437
|
|
|
413
438
|
|
{coretrace_python_analyzer-0.12.0.dist-info → coretrace_python_analyzer-0.14.0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coretrace-python-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: Static security analysis infrastructure for Python code
|
|
5
5
|
Project-URL: Homepage, https://github.com/CoreTrace/coretrace-python-analyzer
|
|
6
6
|
Project-URL: Repository, https://github.com/CoreTrace/coretrace-python-analyzer
|
{coretrace_python_analyzer-0.12.0.dist-info → coretrace_python_analyzer-0.14.0.dist-info}/RECORD
RENAMED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
coretrace_python/__init__.py,sha256=
|
|
1
|
+
coretrace_python/__init__.py,sha256=kY0Q5RzkkixmFjJXYSXgYgHOkh327TcAyqMH9u0a_M0,76
|
|
2
2
|
coretrace_python/__main__.py,sha256=V8DnYtRboISK2VCKm7DGHLREKvqLbqApyWlKBOs26wU,65
|
|
3
3
|
coretrace_python/cache.py,sha256=-pZv-ELm4jMCTXQWSZIobHaT9UHIjFu7IlMkOM0i4LI,13607
|
|
4
4
|
coretrace_python/cli.py,sha256=G5jhb6g6RgSRSjjS7DBgziIjUM1vTFwalJGvFHVKxrg,11225
|
|
5
|
-
coretrace_python/engine.py,sha256=
|
|
5
|
+
coretrace_python/engine.py,sha256=yICn93LZyoWOdDoZEIqoWATuhPQhLPW8ANLxJPskDwM,32371
|
|
6
6
|
coretrace_python/abstract/__init__.py,sha256=n5FNkV8Z4LmwG0r06KWdLj4N2CPSgAKwY3896tdmRgM,999
|
|
7
7
|
coretrace_python/abstract/constants.py,sha256=z4-e7IzysxzGi58wFFff1MlEoV8l7AZsB9EL-q6b7vY,9071
|
|
8
8
|
coretrace_python/abstract/heap.py,sha256=OaESXFGC0qzyhAr76xrCu4skeDVzZM4wlenQqfMTkdk,9642
|
|
9
9
|
coretrace_python/abstract/ranges.py,sha256=zEl9ee55u3E02tkYzzIvCCud5mYRKIUgZd9h_0-crCM,11547
|
|
10
|
+
coretrace_python/abstract/strings.py,sha256=G5EGlhxjrpjU9TuXQifuVO-6MNMrUtZ_4S3nkMEvIuQ,4095
|
|
10
11
|
coretrace_python/abstract/values.py,sha256=SQmgR8jo7cd8KJFDokejg-cFTjJU1CTBUPYyEvbMg-4,1739
|
|
11
12
|
coretrace_python/analysis/__init__.py,sha256=hE7lDwXOdA05_aSZaERRgMv0C2ev8jjJbz4xvrja4Og,728
|
|
12
13
|
coretrace_python/analysis/manager.py,sha256=suoIrPwimyu1AjF6BJwpqAkJBwe6XxzQu9JwV2TN2aQ,5843
|
|
@@ -19,7 +20,7 @@ coretrace_python/bundled/dependency/sample_advisories/plugin.toml,sha256=cME4pSF
|
|
|
19
20
|
coretrace_python/bundled/dependency/sample_advisories/sample_advisories.py,sha256=jw98GK6hh5RAx05t8QPcUzsFMfGigAL8418vZLprKlc,3710
|
|
20
21
|
coretrace_python/bundled/dependency/vulnerable_dependency/plugin.toml,sha256=4-OnbEb3lYVAoTfuz8mDPH4L9fHOPQ2KW1dDFAbClFk,267
|
|
21
22
|
coretrace_python/bundled/dependency/vulnerable_dependency/vulnerable_dependency.py,sha256=THXqpuffQZ8nuv1b8zQrgkA7eTI1PxhODATMzqhWeyY,3393
|
|
22
|
-
coretrace_python/bundled/models/aiohttp/aiohttp_models.py,sha256=
|
|
23
|
+
coretrace_python/bundled/models/aiohttp/aiohttp_models.py,sha256=X3ZYgbrezHbsP3D9rVt-qM1_Iecu6nT_Yz9H7-XGl4M,3422
|
|
23
24
|
coretrace_python/bundled/models/aiohttp/plugin.toml,sha256=YvU60RUBexYBtIxbXdmUhO83BoijmOe3MeQKwY8QDaM,220
|
|
24
25
|
coretrace_python/bundled/models/bottle/bottle_models.py,sha256=Y0SUMCNGiGhzcMj0uxBLhCbLFgM7sBc4XBB-C5LEuDw,2200
|
|
25
26
|
coretrace_python/bundled/models/bottle/plugin.toml,sha256=V2Ea1DcocKOWyOWm_KuzNCaPxN7ZOomSnCyo0e7WfXQ,196
|
|
@@ -29,7 +30,7 @@ coretrace_python/bundled/models/credentials/credential_models.py,sha256=Mw9ty8PD
|
|
|
29
30
|
coretrace_python/bundled/models/credentials/plugin.toml,sha256=0ut7Jc7IdcvbsZKPu6ty-z3DBlMg2CUT7JX4y4nMybo,217
|
|
30
31
|
coretrace_python/bundled/models/db_drivers/db_driver_models.py,sha256=x4SAwhcpuG5TLfhmVsuAQ1VV3lAXSNh5YHx7QuFBQyU,2339
|
|
31
32
|
coretrace_python/bundled/models/db_drivers/plugin.toml,sha256=094-QIV92x-69uy2CgMulBXfGvt48qtefLyeW2q3SRs,178
|
|
32
|
-
coretrace_python/bundled/models/django/django_models.py,sha256=
|
|
33
|
+
coretrace_python/bundled/models/django/django_models.py,sha256=Gg_qgQY4oT0CEW8RAFBoxyW2I56FSRJo5eWkZHZrGeQ,7370
|
|
33
34
|
coretrace_python/bundled/models/django/plugin.toml,sha256=_Qa2X_4IViORQkcLvLsAC5vI-2P5CxgG8VvnuNIWcFQ,214
|
|
34
35
|
coretrace_python/bundled/models/fastapi/fastapi_models.py,sha256=K2Edlo1RzYKmAJAIChNVUDc8gorAS48MGEMKNlCIGrI,1251
|
|
35
36
|
coretrace_python/bundled/models/fastapi/plugin.toml,sha256=V_R2WcEc-qkST0tTpL-cRf2FEbjK6Mk727WbJmVpWJM,200
|
|
@@ -42,7 +43,7 @@ coretrace_python/bundled/models/python_stdlib/python_stdlib.py,sha256=IyCxTHjZcd
|
|
|
42
43
|
coretrace_python/bundled/models/sqlalchemy/plugin.toml,sha256=2gRbglnMK5U2QWnzs2U2pDvmTeWmrlW7XpJ_8dRmIoY,182
|
|
43
44
|
coretrace_python/bundled/models/sqlalchemy/sqlalchemy_models.py,sha256=Gm2qjyXUrjzALy-MigUhPQyyDxYdfWlQKDZLp1C6Jqk,1697
|
|
44
45
|
coretrace_python/bundled/models/tornado/plugin.toml,sha256=u_XjJK2uKBR11zg4yGxsCDHcx2pKEQ1MBvvB6Mm0siA,222
|
|
45
|
-
coretrace_python/bundled/models/tornado/tornado_models.py,sha256=
|
|
46
|
+
coretrace_python/bundled/models/tornado/tornado_models.py,sha256=Z8U46CbDHm7rORSsP26GBhUbe5m-LMf2QNkoBKZxB1M,2651
|
|
46
47
|
coretrace_python/bundled/secrets/config_secrets/config_secrets.py,sha256=JfhWSyVbjlYD9pQYJN6qAc4Rp4IA8PP222akM4KTPU8,1283
|
|
47
48
|
coretrace_python/bundled/secrets/config_secrets/plugin.toml,sha256=dRkA3zQ9wtT2EJyqZ5zGqJ5KYdyIQtQcSvesqrb4M0Q,177
|
|
48
49
|
coretrace_python/bundled/secrets/hardcoded_secrets/hardcoded_secrets.py,sha256=7TD-Nf5S4yfoRBRmJ6Ab6v4Q2zIC2n79FRfNS3I4xos,544
|
|
@@ -63,8 +64,8 @@ coretrace_python/bundled/security/plaintext_credentials/plaintext_credentials.py
|
|
|
63
64
|
coretrace_python/bundled/security/plaintext_credentials/plugin.toml,sha256=W56wUdAdf7NNirfQhTdKrn-cCwebqmvkRJg9OVFJUyM,263
|
|
64
65
|
coretrace_python/bundled/security/sql_injection/plugin.toml,sha256=U35h6YdbbM93Ar_msWrwY98_bb6rN-qaa1e7XOtPeSo,224
|
|
65
66
|
coretrace_python/bundled/security/sql_injection/sql_injection.py,sha256=W-ppZngb9mLwRjJPGkh__8kaDqYdSrVbciv0mPdYFNE,545
|
|
66
|
-
coretrace_python/bundled/security/ssrf/plugin.toml,sha256=
|
|
67
|
-
coretrace_python/bundled/security/ssrf/ssrf.py,sha256=
|
|
67
|
+
coretrace_python/bundled/security/ssrf/plugin.toml,sha256=Uh0hvcc2bkOEKBQUw6tyuvpPQRq8_JzhWc9nVQAdm1Y,226
|
|
68
|
+
coretrace_python/bundled/security/ssrf/ssrf.py,sha256=Zk3eCu4Gp9vnR4DarG_RAm9U7hNv2ESAn_oVUVUMHF8,3441
|
|
68
69
|
coretrace_python/bundled/security/xss/plugin.toml,sha256=o6aZWoDNCG6CJ11JDa5lVR0v6NHh-Jg5QpNRxoGSJaw,185
|
|
69
70
|
coretrace_python/bundled/security/xss/xss.py,sha256=hzhoblPMivtwoWZ4v7YyAb15GGisX_imGWgxFzCDeqA,532
|
|
70
71
|
coretrace_python/bundled/syntax/dangerous_eval/dangerous_eval.py,sha256=_ktrigLDf-XTKjWtYX-_wZ9-Gb3G4bCzRhpU0Xj-c1g,723
|
|
@@ -124,7 +125,7 @@ coretrace_python/ir/printer.py,sha256=otBIhZUvB_UfB68gid_FbPcSjBUSmxH3v7vBMZZQVk
|
|
|
124
125
|
coretrace_python/ir/ssa.py,sha256=cIYbcv90PI6AOeMrw2ZWp88fy7E2ZO4P3tqjtPHra7g,11116
|
|
125
126
|
coretrace_python/plugins/__init__.py,sha256=xRKqkvkvQMmfQACHHoBioYhIyyK4HoMAVogy6EZG8tg,1398
|
|
126
127
|
coretrace_python/plugins/api.py,sha256=FcIe2c4sFB-PAPtYcJkidvdZJkr_xFKT8SvlBP5l38o,8603
|
|
127
|
-
coretrace_python/plugins/detectors.py,sha256=
|
|
128
|
+
coretrace_python/plugins/detectors.py,sha256=9c6yaLvf5nwtiiIskLd9WE_R4IE_RcqjOD5c-SH0SuI,6274
|
|
128
129
|
coretrace_python/plugins/loader.py,sha256=Use-lHTL4-IJYQBZmODC89zhJbYKGvDSLZHWjZa5NaI,3885
|
|
129
130
|
coretrace_python/plugins/manifest.py,sha256=F24pgQSwApRUL6ZihRSLv9wL_M03A_pZjrj9Zi4Ku3Q,3438
|
|
130
131
|
coretrace_python/plugins/registry.py,sha256=ixKcT2YDky96TWCFckdzKnGRmFn1dbBVMSZLPvdxvY8,1177
|
|
@@ -142,14 +143,14 @@ coretrace_python/semantic/symbols.py,sha256=G0jwsWFo2346Y1R2sztsB8HuZZm_aHfHSJXX
|
|
|
142
143
|
coretrace_python/source/__init__.py,sha256=0Y8fvtbkETnNCJR9Zq7RS5TSgF9c22pz3SSk1KdAXF0,301
|
|
143
144
|
coretrace_python/source/manager.py,sha256=JRmMOcWfS1nocbVrdcvfQTqEfIkmnb5JP857TpGywN4,2736
|
|
144
145
|
coretrace_python/source/model.py,sha256=FZnN5mlir3tzbIpWoxnn6YtRi9UcJ_y0sZM60tKr8U4,1727
|
|
145
|
-
coretrace_python/taint/__init__.py,sha256=
|
|
146
|
-
coretrace_python/taint/engine.py,sha256=
|
|
147
|
-
coretrace_python/taint/models.py,sha256=
|
|
146
|
+
coretrace_python/taint/__init__.py,sha256=n0WMjstUkrJZ2QnMgGBlJwqCZ0LHno670XRd__TEcaI,1514
|
|
147
|
+
coretrace_python/taint/engine.py,sha256=rEAbwjR5v294PqBzBlu6f9WiMvVmwV83FqdsWbT4l3c,44648
|
|
148
|
+
coretrace_python/taint/models.py,sha256=8AwPKKdmwAHmE7CxwUbtWn5w7JVkEJ06_Is7xQWTx38,17066
|
|
148
149
|
coretrace_python/taint/routes.py,sha256=UAJKXcw_sM9shf58FfDroxVO9bNSm4qrQdZknwUB0lo,6110
|
|
149
150
|
coretrace_python/taint/templates.py,sha256=8Ojb43VXuWxOVifeXRpi3_2mjZfnvIC7GQ-AW80GUYk,11148
|
|
150
|
-
coretrace_python_analyzer-0.
|
|
151
|
-
coretrace_python_analyzer-0.
|
|
152
|
-
coretrace_python_analyzer-0.
|
|
153
|
-
coretrace_python_analyzer-0.
|
|
154
|
-
coretrace_python_analyzer-0.
|
|
155
|
-
coretrace_python_analyzer-0.
|
|
151
|
+
coretrace_python_analyzer-0.14.0.dist-info/METADATA,sha256=I6joOK1gGJSOzcn3pJ9PYii-2Yb7Ak-PSK9SmQWz4vQ,3217
|
|
152
|
+
coretrace_python_analyzer-0.14.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
153
|
+
coretrace_python_analyzer-0.14.0.dist-info/entry_points.txt,sha256=wdozemiraOkbt65g_Y_zgspuZpoLxJdaCVMJt1Ft3c0,72
|
|
154
|
+
coretrace_python_analyzer-0.14.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
155
|
+
coretrace_python_analyzer-0.14.0.dist-info/licenses/NOTICE,sha256=s3VHj5ngV0ozjW_kZJpg_35ne6JwoBK0ysOjbqVtA0w,113
|
|
156
|
+
coretrace_python_analyzer-0.14.0.dist-info/RECORD,,
|
{coretrace_python_analyzer-0.12.0.dist-info → coretrace_python_analyzer-0.14.0.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|