coretrace-python-analyzer 0.19.0__py3-none-any.whl → 0.20.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.
@@ -1,4 +1,4 @@
1
1
  """CoreTrace's Python static analysis frontend."""
2
2
 
3
- __version__ = "0.19.0"
3
+ __version__ = "0.20.0"
4
4
 
@@ -8,6 +8,7 @@ from coretrace_python.abstract.constants import (
8
8
  from coretrace_python.abstract.heap import (
9
9
  ATTRIBUTES,
10
10
  ELEMENTS,
11
+ KEYS,
11
12
  MUTATORS,
12
13
  AbstractObject,
13
14
  AliasSet,
@@ -24,6 +25,7 @@ from coretrace_python.abstract.values import AbstractValue, Truth
24
25
  __all__ = [
25
26
  "ATTRIBUTES",
26
27
  "ELEMENTS",
28
+ "KEYS",
27
29
  "MUTATORS",
28
30
  "AbstractObject",
29
31
  "AbstractValue",
@@ -6,7 +6,8 @@ globals and imported symbols by name, and the fields loaded from an object by fi
6
6
  Each value points to an ``AliasSet`` of objects, computed by a flow-insensitive
7
7
  points-to fixpoint over the SSA form; each object has two ``HeapLocation`` fields,
8
8
  ``elements`` for its items and ``attributes`` for its attributes, field-insensitive
9
- within each. Taint and dependence analyses key their states by these locations, so a
9
+ within each, and a third, ``keys``, for the keys of a mapping, which only the taint
10
+ engine fills. Taint and dependence analyses key their states by these locations, so a
10
11
  store, a mutating method call or a load on any alias reads and writes the same place.
11
12
  """
12
13
 
@@ -47,6 +48,8 @@ from coretrace_python.source import SourceSpan
47
48
 
48
49
  ELEMENTS = "elements"
49
50
  ATTRIBUTES = "attributes"
51
+ # The keys of a mapping, stored by the taint engine; the points-to solver never fills it.
52
+ KEYS = "keys"
50
53
 
51
54
  # Method names that store their arguments into the receiver's elements.
52
55
  MUTATORS = frozenset(
coretrace_python/cache.py CHANGED
@@ -42,7 +42,7 @@ from coretrace_python.interprocedural import (
42
42
  from coretrace_python.semantic.symbols import SymbolId
43
43
  from coretrace_python.source import SourceId, SourceSpan
44
44
 
45
- CACHE_FORMAT = 11
45
+ CACHE_FORMAT = 12
46
46
 
47
47
 
48
48
  @dataclass(frozen=True)
@@ -368,6 +368,7 @@ def _encode_arguments(arguments: Arguments) -> dict[str, Any]:
368
368
  "positional": list(arguments.positional),
369
369
  "keywords": [[name, value] for name, value in arguments.keywords],
370
370
  "unpacked": arguments.unpacked,
371
+ "keyword_unpacked": arguments.keyword_unpacked,
371
372
  }
372
373
 
373
374
 
@@ -375,11 +376,12 @@ def _decode_arguments(data: Mapping[str, Any]) -> Arguments:
375
376
  def denoted(value: Any) -> str | None:
376
377
  return None if value is None else _string(value)
377
378
 
378
- unpacked = data["unpacked"]
379
- if not isinstance(unpacked, bool):
380
- raise TypeError(f"expected a boolean, got {unpacked!r}")
379
+ unpacked, keyword_unpacked = data["unpacked"], data["keyword_unpacked"]
380
+ if not isinstance(unpacked, bool) or not isinstance(keyword_unpacked, bool):
381
+ raise TypeError(f"expected booleans, got {unpacked!r} and {keyword_unpacked!r}")
381
382
  return Arguments(
382
383
  tuple(denoted(value) for value in data["positional"]),
383
384
  tuple((_string(name), denoted(value)) for name, value in data["keywords"]),
384
385
  unpacked,
386
+ keyword_unpacked,
385
387
  )
@@ -263,4 +263,8 @@ def _condition(entry: Mapping[str, Any]) -> Condition:
263
263
  (condition.argument is None and condition.position is None) or condition.values or condition.default
264
264
  ):
265
265
  raise ValueError("a host condition names the URL argument, by keyword or position, and nothing else")
266
+ if condition.kind == "keyword_name" and (
267
+ condition.argument is not None or condition.position is not None or condition.values or condition.default
268
+ ):
269
+ raise ValueError("a keyword_name condition carries its text and nothing else")
266
270
  return condition
@@ -23,7 +23,7 @@ from coretrace_python.findings import Confidence, Finding, Severity
23
23
  from coretrace_python.findings.refutation import Status, Verdict, Verdicts
24
24
  from coretrace_python.interprocedural import Arguments, CallSite, ExternalSymbol
25
25
  from coretrace_python.semantic.symbols import SymbolId
26
- from coretrace_python.taint import Sink, TaintFlow, TaintKind
26
+ from coretrace_python.taint import KEYWORD_NAMES, Sink, TaintFlow, TaintKind
27
27
  from coretrace_python.taint.urls import UrlProof, redirects_disabled
28
28
 
29
29
  Affected = Mapping[SymbolId, tuple[Advisory, ...]]
@@ -72,13 +72,25 @@ def check_conditions(
72
72
  a ``host`` condition, what ``url`` proves of the value the attacker's input is passed
73
73
  in (``passed_as``). A host the URL's constant text fixes rules the call out when the
74
74
  call disables redirects; otherwise a redirect may still lead to a host the attacker
75
- controls, and the condition stays pending with that uncertainty."""
75
+ controls, and the condition stays pending with that uncertainty. A ``keyword_name``
76
+ condition is met when the attacker's input is passed as the keys of a ``**`` mapping,
77
+ pending when the call expands a mapping whose keys are not established, and
78
+ contradicted by a call writing every keyword name."""
76
79
 
77
80
  if entry is None:
78
81
  return ConditionCheck()
79
82
  met: list[Condition] = []
80
83
  pending: list[Condition] = []
81
84
  for condition in entry.conditions:
85
+ if condition.kind == "keyword_name":
86
+ if any(keyword == KEYWORD_NAMES for _, keyword in passed_as):
87
+ met.append(condition)
88
+ elif arguments is None or arguments.keyword_unpacked:
89
+ pending.append(condition)
90
+ else:
91
+ names = ", ".join(sorted({name for name, _ in arguments.keywords}))
92
+ return ConditionCheck(tuple(met), tuple(pending), condition, names or None)
93
+ continue
82
94
  if condition.kind == "host":
83
95
  origin = url.origin if url is not None and _passed_in(condition, passed_as) else None
84
96
  if origin is None:
@@ -119,7 +131,8 @@ def ruled_out(module: str, site: CallSite, check: ConditionCheck) -> str:
119
131
  python.yaml.load(Loader=python.yaml.SafeLoader)``."""
120
132
 
121
133
  assert check.contradicted is not None and isinstance(site.target, ExternalSymbol)
122
- argument = check.contradicted.argument or f"#{check.contradicted.position}"
134
+ contradicted = check.contradicted
135
+ argument = contradicted.argument or ("keywords" if contradicted.kind == "keyword_name" else f"#{contradicted.position}")
123
136
  passed = f"{argument} absent" if check.passed is None else f"{argument}={check.passed}"
124
137
  return f"{module}:{site.location.start_line} {site.target.symbol}({passed})"
125
138
 
@@ -154,7 +154,10 @@ class Condition:
154
154
  ``default`` says an absent argument means a vulnerable value. A ``host`` condition
155
155
  says the attacker must choose the host of the URL passed as ``argument`` (or at
156
156
  ``position``); the engine decides it from what the URL's constant text proves. A
157
- ``semantic`` condition cannot be checked and is reported as pending review."""
157
+ ``keyword_name`` condition says the attacker must choose a keyword name of the call,
158
+ as an ORM alias expanded from a mapping; the engine decides it from the keys of what
159
+ the call expands with ``**``. A ``semantic`` condition cannot be checked and is
160
+ reported as pending review."""
158
161
 
159
162
  kind: str
160
163
  text: str
@@ -18,6 +18,7 @@ from typing import ClassVar
18
18
  from coretrace_python.analysis import Analysis, AnalysisContext, AnyAnalysis
19
19
  from coretrace_python.cfg import CFGError
20
20
  from coretrace_python.hir import nodes
21
+ from coretrace_python.ir.defuse import DefUse, def_use
21
22
  from coretrace_python.ir.lowering import (
22
23
  MODULE_BODY,
23
24
  LoweringError,
@@ -26,6 +27,7 @@ from coretrace_python.ir.lowering import (
26
27
  )
27
28
  from coretrace_python.ir.model import (
28
29
  Await,
30
+ BuildDict,
29
31
  Call,
30
32
  Constant,
31
33
  FunctionIR,
@@ -68,11 +70,15 @@ class Arguments:
68
70
  (``python.yaml.FullLoader``), a constant as Python writes it (``True``, ``'/static'``),
69
71
  or None for anything else (a parameter, an expression). ``unpacked`` says the call
70
72
  unpacks ``*args`` or ``**kwargs``, so an argument it does not give explicitly may
71
- still be given; after ``*args`` no position is known, and ``positional`` is empty."""
73
+ still be given; after ``*args`` no position is known, and ``positional`` is empty.
74
+ ``keyword_unpacked`` says the call expands a ``**mapping`` whose keys it does not
75
+ write, so a keyword name may be anything; a literal with constant keys is written out
76
+ as its keywords, and ``unpacked`` stays true for it only when ``*args`` is also given."""
72
77
 
73
78
  positional: tuple[str | None, ...] = ()
74
79
  keywords: tuple[tuple[str, str | None], ...] = ()
75
80
  unpacked: bool = False
81
+ keyword_unpacked: bool = False
76
82
 
77
83
  def given(self, keyword: str | None, position: int | None = None) -> tuple[bool, str | None] | None:
78
84
  """Whether the call gives an argument, by ``keyword`` or at ``position``, and what it
@@ -245,8 +251,45 @@ def derive_symbols(
245
251
  # passed as an argument is not recorded, so call sites stay small in the cache.
246
252
  MAX_CONSTANT_LENGTH = 64
247
253
 
254
+ _DICT = SymbolId("python.builtins.dict")
255
+
256
+
257
+ def _keyword_entries(
258
+ value: Value, symbols: Mapping[Value, SymbolId], defs: Mapping[Value, Instruction], uses: DefUse
259
+ ) -> tuple[tuple[str, Value], ...] | None:
260
+ """The keywords a ``**value`` gives when every name is established: the string keys
261
+ of a dict literal, the keywords of ``dict(...)``, through what they unpack in turn;
262
+ None when a key is computed, the mapping is built elsewhere, or anything but the
263
+ call uses it, since a store or a mutating call may add a key the literal does not
264
+ write."""
265
+
266
+ if len(uses.uses(value)) != 1:
267
+ return None
268
+ made = defs.get(value)
269
+ entries: list[tuple[str, Value]] = []
270
+ if isinstance(made, BuildDict):
271
+ for key, item in made.items:
272
+ constant = defs.get(key)
273
+ if not isinstance(constant, Constant) or not isinstance(constant.value, str):
274
+ return None
275
+ entries.append((constant.value, item))
276
+ nested = made.unpacked
277
+ elif isinstance(made, Call) and symbols.get(made.callee) == _DICT and not made.arguments and not made.starred:
278
+ nested = tuple(v for name, v in made.keywords if name is None)
279
+ entries.extend((name, v) for name, v in made.keywords if name is not None)
280
+ else:
281
+ return None
282
+ for inner in nested:
283
+ found = _keyword_entries(inner, symbols, defs, uses)
284
+ if found is None:
285
+ return None
286
+ entries.extend(found)
287
+ return tuple(entries)
288
+
248
289
 
249
- def _arguments(call: Call, symbols: Mapping[Value, SymbolId], defs: Mapping[Value, Instruction]) -> Arguments:
290
+ def _arguments(
291
+ call: Call, symbols: Mapping[Value, SymbolId], defs: Mapping[Value, Instruction], uses: DefUse
292
+ ) -> Arguments:
250
293
  def denoted(value: Value) -> str | None:
251
294
  symbol = symbols.get(value)
252
295
  if symbol is not None:
@@ -257,10 +300,22 @@ def _arguments(call: Call, symbols: Mapping[Value, SymbolId], defs: Mapping[Valu
257
300
  written = repr(made.value)
258
301
  return written if len(written) <= MAX_CONSTANT_LENGTH else None
259
302
 
303
+ keywords: list[tuple[str, str | None]] = []
304
+ keyword_unpacked = False
305
+ for name, value in call.keywords:
306
+ if name is not None:
307
+ keywords.append((name, denoted(value)))
308
+ continue
309
+ entries = _keyword_entries(value, symbols, defs, uses)
310
+ if entries is None:
311
+ keyword_unpacked = True
312
+ else:
313
+ keywords.extend((key, denoted(item)) for key, item in entries)
260
314
  return Arguments(
261
315
  () if call.starred else tuple(denoted(value) for value in call.arguments),
262
- tuple((name, denoted(value)) for name, value in call.keywords if name is not None),
263
- bool(call.starred) or any(name is None for name, _ in call.keywords),
316
+ tuple(keywords),
317
+ bool(call.starred) or keyword_unpacked,
318
+ keyword_unpacked,
264
319
  )
265
320
 
266
321
 
@@ -480,6 +535,7 @@ class CallGraphAnalysis(Analysis[CallGraph]):
480
535
  table=table,
481
536
  )
482
537
  defs = {i.result: i for block in ssa.blocks for i in block.instructions if i.result is not None}
538
+ uses = def_use(ssa)
483
539
  found: list[CallSite] = []
484
540
  for block in ssa.blocks:
485
541
  for instruction in block.instructions:
@@ -489,7 +545,7 @@ class CallGraphAnalysis(Analysis[CallGraph]):
489
545
  name,
490
546
  instruction.location,
491
547
  targets.get(instruction.callee, UnknownTarget()),
492
- _arguments(instruction, symbols[name], defs),
548
+ _arguments(instruction, symbols[name], defs, uses),
493
549
  )
494
550
  )
495
551
  sites[name] = tuple(found)
@@ -2,6 +2,7 @@
2
2
 
3
3
  from coretrace_python.semantic.symbols import Members
4
4
  from coretrace_python.taint.engine import (
5
+ KEYWORD_NAMES,
5
6
  EntryPointAnalysis,
6
7
  Taint,
7
8
  TaintAnalysis,
@@ -46,6 +47,7 @@ from coretrace_python.taint.templates import (
46
47
 
47
48
  __all__ = [
48
49
  "FILTER_FUNCTIONS",
50
+ "KEYWORD_NAMES",
49
51
  "TEXT_KINDS",
50
52
  "AuthorizationGuard",
51
53
  "BasePattern",
@@ -18,6 +18,7 @@ from typing import ClassVar
18
18
  from coretrace_python.abstract import (
19
19
  ATTRIBUTES,
20
20
  ELEMENTS,
21
+ KEYS,
21
22
  HeapAnalysis,
22
23
  HeapFacts,
23
24
  HeapLocation,
@@ -130,12 +131,18 @@ class TaintFlow:
130
131
  # What the arguments of the sink call denote, to decide the conditions on them.
131
132
  sink_arguments: Arguments | None = None
132
133
  # How the tainted value is passed to the sink call: at a position, by a keyword, or
133
- # (None, None) unpacked with ``*`` or ``**``; ``f(x, key=x)`` passes it twice.
134
+ # (None, None) unpacked with ``*`` or ``**``; ``f(x, key=x)`` passes it twice. The
135
+ # keyword ``KEYWORD_NAMES`` says the value's keys are the keyword names of the call.
134
136
  passed_as: frozenset[tuple[int | None, str | None]] = frozenset()
135
137
 
136
138
 
137
139
  Key = Value | HeapLocation
138
140
 
141
+ # The ``keyword`` of a ``passed_as`` way whose value's keys become the keyword names of
142
+ # the call: ``f(**m)`` passes the keys of ``m`` as ``(None, KEYWORD_NAMES)``.
143
+ KEYWORD_NAMES = "**"
144
+ _DICT = SymbolId("python.builtins.dict")
145
+
139
146
 
140
147
  class TaintFacts:
141
148
  def __init__(self, taints: Mapping[Key, Taint], flows: tuple[TaintFlow, ...]) -> None:
@@ -217,6 +224,14 @@ class _TaintProblem(DataflowProblem[State]):
217
224
  taint = taint.join(state.get(location, Taint.none()))
218
225
  return taint
219
226
 
227
+ def keyed(self, value: Value, state: Mapping[Key, Taint]) -> Taint:
228
+ """The taint of the keys of the mappings ``value`` points to."""
229
+
230
+ taint = Taint.none()
231
+ for location in self.heap.locations(value, KEYS):
232
+ taint = taint.join(state.get(location, Taint.none()))
233
+ return taint
234
+
220
235
  def initial(self) -> State:
221
236
  state: dict[Key, Taint] = {
222
237
  self.function.parameters[index]: Taint(source.kinds, frozenset({source}))
@@ -246,6 +261,17 @@ class _TaintProblem(DataflowProblem[State]):
246
261
  self.store(state, instruction.object, ATTRIBUTES, state.get(instruction.value, Taint.none()))
247
262
  elif isinstance(instruction, SetItem):
248
263
  self.store(state, instruction.object, ELEMENTS, state.get(instruction.value, Taint.none()))
264
+ key = state.get(instruction.key, Taint.none())
265
+ if key:
266
+ self.store(state, instruction.object, KEYS, key)
267
+ elif isinstance(instruction, BuildDict):
268
+ keys = Taint.none()
269
+ for item_key, _ in instruction.items:
270
+ keys = keys.join(state.get(item_key, Taint.none()))
271
+ for unpacked in instruction.unpacked: # ``{**d}`` copies d's keys
272
+ keys = keys.join(self.keyed(unpacked, state))
273
+ if keys:
274
+ self.store(state, instruction.result, KEYS, keys)
249
275
  if instruction.result is None:
250
276
  continue
251
277
  if isinstance(instruction, Phi):
@@ -352,6 +378,10 @@ class _TaintProblem(DataflowProblem[State]):
352
378
  project, through, arguments, keywords, everything, call, flows, state, (), bound
353
379
  )
354
380
  return self.declared_clean(target.symbol, result)
381
+ if target.symbol == _DICT:
382
+ keys = self.constructed_keys(call, state)
383
+ if keys:
384
+ self.store(state, call.result, KEYS, keys)
355
385
  symbol = target.symbol
356
386
  if not self.modelled(symbol):
357
387
  # ``get_conn().execute`` derived ``app.database.get_conn.execute``; what
@@ -376,6 +406,23 @@ class _TaintProblem(DataflowProblem[State]):
376
406
  return self.external(returned, everything, call, state, flows)
377
407
  return everything.join(state.get(call.callee, Taint.none()))
378
408
 
409
+ def constructed_keys(self, call: Call, state: Mapping[Key, Taint]) -> Taint:
410
+ """What the keys of ``dict(...)`` carry: the keys of the mapping it copies
411
+ (``dict(m)``, ``dict(**m)``) and the first element of each pair of a sequence
412
+ literal (``dict([(k, v)])``); a keyword is a constant key, and an iterable built
413
+ elsewhere (``zip(...)``, a parameter) gives keys the engine cannot establish."""
414
+
415
+ taint = Taint.none()
416
+ for value in (*call.arguments[:1], *(v for name, v in call.keywords if name is None)):
417
+ taint = taint.join(self.keyed(value, state))
418
+ made = self.defs.get(value)
419
+ if isinstance(made, BuildList | BuildTuple | BuildSet) and not made.unpacked:
420
+ for element in made.elements:
421
+ pair = self.defs.get(element)
422
+ if isinstance(pair, BuildTuple | BuildList) and pair.elements:
423
+ taint = taint.join(state.get(pair.elements[0], Taint.none()))
424
+ return taint
425
+
379
426
  def nonlocal_result(self, instruction: NonlocalResult, state: dict[Key, Taint]) -> Taint:
380
427
  """The taint a nested callee left in its nonlocal ``name``, in the caller's terms."""
381
428
 
@@ -455,6 +502,11 @@ class _TaintProblem(DataflowProblem[State]):
455
502
  self.report(
456
503
  flows, sink, self.rendered(argument, state), argument, call, None, None, None, given, keyword=name
457
504
  )
505
+ if name is None:
506
+ # The keys of a ``**mapping`` are keyword names of the call: only an
507
+ # advisory asks who chooses a name, no other sink reads one.
508
+ keys = self.keyed(argument, state).without(~TaintKind.ADVISORY)
509
+ self.report(flows, sink, keys, argument, call, None, None, None, given, keyword=KEYWORD_NAMES)
458
510
  self.template_filters(symbol, call, given, state, flows)
459
511
  cleared = self.clearing.clears(symbol, given)
460
512
  if cleared:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: coretrace-python-analyzer
3
- Version: 0.19.0
3
+ Version: 0.20.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
@@ -1,11 +1,11 @@
1
- coretrace_python/__init__.py,sha256=FWsRdqY0ZoJRj8rA--2DntUA3GU1JsrZdyrjQIAq5wE,76
1
+ coretrace_python/__init__.py,sha256=CxDlOASpblTY0nlvktbHjBs1Ntjlqj8ulKKg0N6LV6c,76
2
2
  coretrace_python/__main__.py,sha256=V8DnYtRboISK2VCKm7DGHLREKvqLbqApyWlKBOs26wU,65
3
- coretrace_python/cache.py,sha256=XnUC677v8VQvlmLA_GQO376isHn6ZdSOOA0dRua0Foo,13716
3
+ coretrace_python/cache.py,sha256=cXDJvi0GqEGwC9lK6TH_nMYIH64sgbZUToQMnf5lAA0,13908
4
4
  coretrace_python/cli.py,sha256=G5jhb6g6RgSRSjjS7DBgziIjUM1vTFwalJGvFHVKxrg,11225
5
5
  coretrace_python/engine.py,sha256=i_YckOVHZa_Mcwxgi2jsMkw8O49zHFQZ313mA_Xtl-4,37643
6
- coretrace_python/abstract/__init__.py,sha256=n5FNkV8Z4LmwG0r06KWdLj4N2CPSgAKwY3896tdmRgM,999
6
+ coretrace_python/abstract/__init__.py,sha256=UK1yHTU4fldnW0W-e-nyPEOsqCnXqGPVv7LkxfyBMII,1021
7
7
  coretrace_python/abstract/constants.py,sha256=z4-e7IzysxzGi58wFFff1MlEoV8l7AZsB9EL-q6b7vY,9071
8
- coretrace_python/abstract/heap.py,sha256=OaESXFGC0qzyhAr76xrCu4skeDVzZM4wlenQqfMTkdk,9642
8
+ coretrace_python/abstract/heap.py,sha256=uJAA0bYnN3-4fdESo6o4CzlYJSIpwlFLoyccacN6Uk8,9831
9
9
  coretrace_python/abstract/ranges.py,sha256=zEl9ee55u3E02tkYzzIvCCud5mYRKIUgZd9h_0-crCM,11547
10
10
  coretrace_python/abstract/strings.py,sha256=G5EGlhxjrpjU9TuXQifuVO-6MNMrUtZ_4S3nkMEvIuQ,4095
11
11
  coretrace_python/abstract/values.py,sha256=SQmgR8jo7cd8KJFDokejg-cFTjJU1CTBUPYyEvbMg-4,1739
@@ -94,9 +94,9 @@ coretrace_python/dataflow/__init__.py,sha256=N5uY8VTNjEk6-ARCgDkxr6uGqQIz2NbF-U9
94
94
  coretrace_python/dataflow/lattice.py,sha256=HgYGVcKVUlexvEc0yT47aQCudLxU_SWLpHhr17igkiE,1929
95
95
  coretrace_python/dataflow/solver.py,sha256=iDsW0wujhM2DXplu2JMKC46BRsroXlNETHPEoikPeG4,3475
96
96
  coretrace_python/dependency/__init__.py,sha256=BxkxnNwIEpAYtwbshCQovG-YXJ80QbwyzkmyJdbgsVg,1184
97
- coretrace_python/dependency/advisories.py,sha256=RPtqT5M_kmRVnjs53PRnDJKUsUysdJmk8KuE3sigUOk,10564
98
- coretrace_python/dependency/correlation.py,sha256=nr5FZ3yOT4HsKzvEAFlzzaPJxBc9_X-IuyOWtRjS67Q,9033
99
- coretrace_python/dependency/graph.py,sha256=U9KzdVCcM7P1W6qSSSMkzMIIVEh3qlzBpBi2FAFy0y0,18633
97
+ coretrace_python/dependency/advisories.py,sha256=t1BKlfGx6GkPd0BNAWjli8-bIJj347A5zvfuWPO4rlw,10818
98
+ coretrace_python/dependency/correlation.py,sha256=HsERzPR9A5NceRNh1dnNUO5B7saJFdXz0TrhI3c8IzY,9855
99
+ coretrace_python/dependency/graph.py,sha256=ZaDSy1h3H4AS8kwGHV768wYf_R4KcqDnxzDQMsoIa88,18847
100
100
  coretrace_python/dependency/policy.py,sha256=bsxZXL9xC0jnAzkzDh6ncnW8oKLG2H13jYmXF_q99Ng,2454
101
101
  coretrace_python/dependency/sbom.py,sha256=rzJ-t0Mwoghej8nk0N63Dpjr_wYXm6JzuOPXjD8Kqs4,3234
102
102
  coretrace_python/dependency/vex.py,sha256=bafWyziWWQP-Fjw1bvyOcOPsdFMwKy-sjMGWL7IPY3w,8048
@@ -114,7 +114,7 @@ coretrace_python/hir/__init__.py,sha256=xMpvj-RSp6bd78a_hhkqbe99-snjERkH-_Lv5o9A
114
114
  coretrace_python/hir/nodes.py,sha256=4BI0f3UhKbnz9ZIxMoKXM7ujav0BmRpD0OXaoblRxIk,12364
115
115
  coretrace_python/hir/visitors.py,sha256=JFnX09hK6trLyRcWgZ5ehOG2ywX3VvkqQ1h82nahYNg,885
116
116
  coretrace_python/interprocedural/__init__.py,sha256=q8u1eIpM55wqVtn46cxuymOn5D5DWU7K35i8Nzzbc3w,1604
117
- coretrace_python/interprocedural/callgraph.py,sha256=EEG8kh9yyFPZiiFcouR8wuIwicClSKHu-z7nLh1kKOQ,22163
117
+ coretrace_python/interprocedural/callgraph.py,sha256=a4LNbp8ZboIgwkNgWIzYHipS1yHwXvgQy33ariECRcE,24452
118
118
  coretrace_python/interprocedural/modulegraph.py,sha256=xKlO_L2mcZi0RoG9h2ulacYecqUhM5BG1RrdC6_HUk8,13036
119
119
  coretrace_python/interprocedural/summaries.py,sha256=6lybEynjtptZGGBw_3WKXx9ZZZqN96xL-r-tzo8fbMI,33862
120
120
  coretrace_python/ir/__init__.py,sha256=ShBPYYqoQFePP-mgBjGezQ2acel0-ReOHF9vCC-G_4Q,232
@@ -143,15 +143,15 @@ coretrace_python/semantic/symbols.py,sha256=G0jwsWFo2346Y1R2sztsB8HuZZm_aHfHSJXX
143
143
  coretrace_python/source/__init__.py,sha256=0Y8fvtbkETnNCJR9Zq7RS5TSgF9c22pz3SSk1KdAXF0,301
144
144
  coretrace_python/source/manager.py,sha256=JRmMOcWfS1nocbVrdcvfQTqEfIkmnb5JP857TpGywN4,2736
145
145
  coretrace_python/source/model.py,sha256=FZnN5mlir3tzbIpWoxnn6YtRi9UcJ_y0sZM60tKr8U4,1727
146
- coretrace_python/taint/__init__.py,sha256=nKpD33_4IRneAkd3Dt-j7Q-cHl47rDA_eLZFDHZVH28,1827
147
- coretrace_python/taint/engine.py,sha256=InunQrFgPq5NmiNkn0BQTTb_7D4IY_wZ2sJZn_K4iMs,51533
146
+ coretrace_python/taint/__init__.py,sha256=Da0nmvrxm9uKsmQogtHdc-FRrWo_xxpnLBDCSQ-xp9g,1867
147
+ coretrace_python/taint/engine.py,sha256=NvGhkBpBviCKE0BGHlgEgKacKC55fSxjwUwkaBz7pkk,54415
148
148
  coretrace_python/taint/models.py,sha256=pCvE-6dYKYSLK7pylxhLWpeUcMfMyXlh_1VUJbzieo8,20405
149
149
  coretrace_python/taint/routes.py,sha256=UAJKXcw_sM9shf58FfDroxVO9bNSm4qrQdZknwUB0lo,6110
150
150
  coretrace_python/taint/templates.py,sha256=gUpUvVk_UIVY2a3T5ENm2TfBuPCDhnu2lu8h50srZ4Q,43963
151
151
  coretrace_python/taint/urls.py,sha256=kb0-uOT12ww7R3Jzs_lPKg9lFs74JI-bpa9pXX105xM,3166
152
- coretrace_python_analyzer-0.19.0.dist-info/METADATA,sha256=QWl44X6mXg-aIRsdgMZIGyX-Ms3CvvpysboxggSRJi4,3217
153
- coretrace_python_analyzer-0.19.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
154
- coretrace_python_analyzer-0.19.0.dist-info/entry_points.txt,sha256=wdozemiraOkbt65g_Y_zgspuZpoLxJdaCVMJt1Ft3c0,72
155
- coretrace_python_analyzer-0.19.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
156
- coretrace_python_analyzer-0.19.0.dist-info/licenses/NOTICE,sha256=s3VHj5ngV0ozjW_kZJpg_35ne6JwoBK0ysOjbqVtA0w,113
157
- coretrace_python_analyzer-0.19.0.dist-info/RECORD,,
152
+ coretrace_python_analyzer-0.20.0.dist-info/METADATA,sha256=iC4jLQhKfefmmGcBpJSgaVFWY-SassLvisc6EoUtdz4,3217
153
+ coretrace_python_analyzer-0.20.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
154
+ coretrace_python_analyzer-0.20.0.dist-info/entry_points.txt,sha256=wdozemiraOkbt65g_Y_zgspuZpoLxJdaCVMJt1Ft3c0,72
155
+ coretrace_python_analyzer-0.20.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
156
+ coretrace_python_analyzer-0.20.0.dist-info/licenses/NOTICE,sha256=s3VHj5ngV0ozjW_kZJpg_35ne6JwoBK0ysOjbqVtA0w,113
157
+ coretrace_python_analyzer-0.20.0.dist-info/RECORD,,