sourcecode 4.5.2__py3-none-any.whl → 4.5.3__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.
Potentially problematic release.
This version of sourcecode might be problematic. Click here for more details.
- sourcecode/__init__.py +1 -1
- sourcecode/ast_extractor.py +1 -1
- sourcecode/cli.py +11 -3
- sourcecode/client_calls.py +5 -2
- sourcecode/contract_init.py +56 -15
- sourcecode/data_exposure.py +31 -2
- sourcecode/remedies.py +17 -0
- sourcecode/ris.py +27 -17
- sourcecode/security_config_scan.py +19 -4
- sourcecode/servlet_surface.py +8 -2
- sourcecode/source_text.py +103 -0
- sourcecode/token_estimate.py +1 -1
- sourcecode/verify_edit.py +2 -2
- sourcecode/verify_repo.py +2 -1
- sourcecode/verify_rules.py +32 -12
- {sourcecode-4.5.2.dist-info → sourcecode-4.5.3.dist-info}/METADATA +4 -4
- {sourcecode-4.5.2.dist-info → sourcecode-4.5.3.dist-info}/RECORD +20 -19
- {sourcecode-4.5.2.dist-info → sourcecode-4.5.3.dist-info}/WHEEL +0 -0
- {sourcecode-4.5.2.dist-info → sourcecode-4.5.3.dist-info}/entry_points.txt +0 -0
- {sourcecode-4.5.2.dist-info → sourcecode-4.5.3.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/ast_extractor.py
CHANGED
|
@@ -1252,7 +1252,7 @@ class AstExtractor:
|
|
|
1252
1252
|
stat = path.stat()
|
|
1253
1253
|
if stat.st_size > self.max_file_size:
|
|
1254
1254
|
return FileContract(
|
|
1255
|
-
path=
|
|
1255
|
+
path=rel_path,
|
|
1256
1256
|
language=language,
|
|
1257
1257
|
extraction_method="heuristic",
|
|
1258
1258
|
limitations=[f"file_too_large: {stat.st_size} bytes > {self.max_file_size}"],
|
sourcecode/cli.py
CHANGED
|
@@ -2533,9 +2533,17 @@ def main(
|
|
|
2533
2533
|
[l for l in _uc_r.stdout.splitlines() if l.strip()]
|
|
2534
2534
|
)
|
|
2535
2535
|
_patched["git_context"]["uncommitted_files"] = _uc_count
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2536
|
+
# Provenance goes in `_meta`, never inside the payload
|
|
2537
|
+
# block. Written into `git_context` it changed that
|
|
2538
|
+
# block's *shape* depending on whether the answer came
|
|
2539
|
+
# from cache, so one request answered two ways compared
|
|
2540
|
+
# unequal on a key that describes the run rather than
|
|
2541
|
+
# the repository (surfaced by C3-40, which made an
|
|
2542
|
+
# untracked file dirty again and put this path back in
|
|
2543
|
+
# reach).
|
|
2544
|
+
_meta_gc = _patched.setdefault("_meta", {})
|
|
2545
|
+
if isinstance(_meta_gc, dict):
|
|
2546
|
+
_meta_gc["stale_fields_refreshed"] = ["git_context.uncommitted_files"]
|
|
2539
2547
|
_cache_hit_content = _json_gc.dumps(
|
|
2540
2548
|
_patched, indent=2, ensure_ascii=False
|
|
2541
2549
|
)
|
sourcecode/client_calls.py
CHANGED
|
@@ -185,9 +185,12 @@ def extract_client_calls(
|
|
|
185
185
|
except OSError:
|
|
186
186
|
continue
|
|
187
187
|
try:
|
|
188
|
-
|
|
188
|
+
# POSIX like every other published path (C2-24). A client-usage row
|
|
189
|
+
# exists to be joined against a route's declaring file, and a join
|
|
190
|
+
# on path fails silently when the two sides spell it differently.
|
|
191
|
+
relative = file_path.relative_to(base).as_posix()
|
|
189
192
|
except ValueError:
|
|
190
|
-
relative =
|
|
193
|
+
relative = Path(file_path).as_posix()
|
|
191
194
|
for match in _VERB_CALL.finditer(text):
|
|
192
195
|
receiver = match.group("receiver") or ""
|
|
193
196
|
if not _HTTP_RECEIVER.search(receiver):
|
sourcecode/contract_init.py
CHANGED
|
@@ -125,24 +125,52 @@ def _security_rules(cir: Any) -> tuple[list[dict], list[dict]]:
|
|
|
125
125
|
return proposed, rejected
|
|
126
126
|
|
|
127
127
|
|
|
128
|
-
def
|
|
129
|
-
"""
|
|
130
|
-
|
|
128
|
+
def _rule_catalogue() -> "list[tuple[str, str]]":
|
|
129
|
+
"""Every rule this build ships, as `(pattern_id, severity)`.
|
|
130
|
+
|
|
131
|
+
Read from `rule_catalog.RULES` — the one authority `--help`, the README and
|
|
132
|
+
the rule reference already render from, and the one a test binds to the
|
|
133
|
+
pattern registries so a new rule cannot exist without a row.
|
|
134
|
+
|
|
135
|
+
C1-24's second half: this list used to be built from the two *pattern
|
|
136
|
+
registries* alone, which stop at SEC-004. SEC-005, SEC-006 and SEC-007 are
|
|
137
|
+
produced by `security_config_scan` and appeared in **neither** the proposed
|
|
138
|
+
nor the rejected list — a rule could be silently outside contract derivation
|
|
139
|
+
with nothing saying so, which is the failure mode a "not proposed, and why"
|
|
140
|
+
list exists to prevent.
|
|
141
|
+
"""
|
|
142
|
+
from sourcecode.rule_catalog import RULES
|
|
143
|
+
from sourcecode.spring_security_audit import (
|
|
144
|
+
_DEFAULT_SECURITY_PATTERNS,
|
|
145
|
+
SecurityScanner,
|
|
146
|
+
)
|
|
131
147
|
from sourcecode.spring_tx_analyzer import _DEFAULT_TX_PATTERNS
|
|
148
|
+
|
|
149
|
+
severities: "dict[str, str]" = {}
|
|
150
|
+
for pattern in (*_DEFAULT_TX_PATTERNS, *_DEFAULT_SECURITY_PATTERNS):
|
|
151
|
+
pattern_id = getattr(pattern, "pattern_id", "")
|
|
152
|
+
if pattern_id:
|
|
153
|
+
severities[pattern_id] = getattr(pattern, "severity", "low")
|
|
154
|
+
# The configuration rules carry their severity on the scanner that emits
|
|
155
|
+
# them, not on a pattern object; same authority, different shape.
|
|
156
|
+
severities.update(getattr(SecurityScanner, "_SEVERITY", {}) or {})
|
|
157
|
+
return sorted(
|
|
158
|
+
(rule.id, severities.get(rule.id, "low")) for rule in RULES if rule.id
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _finding_rules(cir: Any, root: "Optional[Path]" = None) -> tuple[list[dict], list[dict]]:
|
|
163
|
+
"""`forbid_finding` for audit patterns that fire nowhere today."""
|
|
132
164
|
from sourcecode.verify_rules import _audit_findings
|
|
133
165
|
|
|
134
|
-
|
|
166
|
+
# Same `root` the gate evaluates with: without it the configuration rules
|
|
167
|
+
# cannot fire, and every one of them would be proposed as "fires nowhere".
|
|
168
|
+
findings = _audit_findings(cir, root)
|
|
135
169
|
firing = Counter(f.pattern_id for f in findings)
|
|
136
170
|
|
|
137
171
|
proposed: list[dict] = []
|
|
138
172
|
rejected: list[dict] = []
|
|
139
|
-
|
|
140
|
-
(getattr(p, "pattern_id", ""), getattr(p, "severity", "low"))
|
|
141
|
-
for p in (*_DEFAULT_TX_PATTERNS, *_DEFAULT_SECURITY_PATTERNS)
|
|
142
|
-
]
|
|
143
|
-
for pattern_id, severity in sorted(set(catalogue)):
|
|
144
|
-
if not pattern_id:
|
|
145
|
-
continue
|
|
173
|
+
for pattern_id, severity in _rule_catalogue():
|
|
146
174
|
count = firing.get(pattern_id, 0)
|
|
147
175
|
if count:
|
|
148
176
|
rejected.append({
|
|
@@ -283,7 +311,9 @@ def _slug(annotation: str) -> str:
|
|
|
283
311
|
return annotation.lstrip("@").replace(".", "-").lower()
|
|
284
312
|
|
|
285
313
|
|
|
286
|
-
def execute_candidates(
|
|
314
|
+
def execute_candidates(
|
|
315
|
+
candidates: list[dict], cir: Any, root: "Optional[Path]" = None
|
|
316
|
+
) -> tuple[list[dict], list[dict]]:
|
|
287
317
|
"""``(kept, dropped)`` — run every candidate before publishing it.
|
|
288
318
|
|
|
289
319
|
The check that makes this a baseline rather than a wish list, and the one
|
|
@@ -292,6 +322,12 @@ def execute_candidates(candidates: list[dict], cir: Any) -> tuple[list[dict], li
|
|
|
292
322
|
the engine `verify` runs and drops anything that disagrees, so a derivation
|
|
293
323
|
that grows a blind spot cannot ship a red gate on the commit it was
|
|
294
324
|
generated from.
|
|
325
|
+
|
|
326
|
+
C1-24: this net had the hole it was meant to catch. It evaluated without a
|
|
327
|
+
`root`, so the security *configuration* rules could not fire here either, and
|
|
328
|
+
`no-sec-004` passed the check that exists to stop exactly that rule from
|
|
329
|
+
being published. A safety net blind in the same way as the thing it guards
|
|
330
|
+
is not a second opinion.
|
|
295
331
|
"""
|
|
296
332
|
from sourcecode.verify_rules import evaluate_all, parse_rules
|
|
297
333
|
|
|
@@ -300,7 +336,7 @@ def execute_candidates(candidates: list[dict], cir: Any) -> tuple[list[dict], li
|
|
|
300
336
|
for rule in candidates:
|
|
301
337
|
payload = {k: v for k, v in rule.items() if not k.startswith("_")}
|
|
302
338
|
try:
|
|
303
|
-
violations = evaluate_all(parse_rules({"contracts": [payload]}), cir)
|
|
339
|
+
violations = evaluate_all(parse_rules({"contracts": [payload]}), cir, root)
|
|
304
340
|
except Exception as exc: # a rule this module built must never break the run
|
|
305
341
|
dropped.append({"id": rule.get("id", "?"), "reason": f"did not parse: {exc}"})
|
|
306
342
|
continue
|
|
@@ -333,12 +369,17 @@ def derive_contracts(root: Path, cir: Optional[Any] = None) -> dict:
|
|
|
333
369
|
|
|
334
370
|
proposed: list[dict] = []
|
|
335
371
|
rejected: list[dict] = []
|
|
372
|
+
# `_finding_rules` needs the root; the graph-only derivations do not. Passed
|
|
373
|
+
# by name rather than positionally so adding a third file-reading derivation
|
|
374
|
+
# is a one-word change instead of a silent blind spot (C1-24).
|
|
336
375
|
for derive in (_security_rules, _finding_rules, _edge_rules):
|
|
337
|
-
rules, skipped =
|
|
376
|
+
rules, skipped = (
|
|
377
|
+
derive(cir, root) if derive is _finding_rules else derive(cir)
|
|
378
|
+
)
|
|
338
379
|
proposed.extend(rules)
|
|
339
380
|
rejected.extend(skipped)
|
|
340
381
|
|
|
341
|
-
kept, dropped = execute_candidates(proposed, cir)
|
|
382
|
+
kept, dropped = execute_candidates(proposed, cir, root)
|
|
342
383
|
|
|
343
384
|
return {
|
|
344
385
|
"schema_version": SCHEMA_VERSION,
|
sourcecode/data_exposure.py
CHANGED
|
@@ -146,15 +146,44 @@ def build_data_exposure(
|
|
|
146
146
|
if not decl.labels:
|
|
147
147
|
# No declaration is not "nothing is exposed" — it is a question nobody asked
|
|
148
148
|
# yet, and the counts stay null rather than reading as a clean bill (I-3/R9).
|
|
149
|
+
#
|
|
150
|
+
# C4-14: the statement said what to do and the payload gave the reader no
|
|
151
|
+
# way to do it — "cero valor out-of-the-box, cero guía de onboarding en la
|
|
152
|
+
# propia salida", on the payload aimed at a regulated buyer. The file name,
|
|
153
|
+
# the key and the shape are read from `data_labels`, the module that parses
|
|
154
|
+
# them, so an example that stops being valid fails the build instead of
|
|
155
|
+
# teaching a buyer the wrong schema.
|
|
156
|
+
from sourcecode.data_labels import CONFIG_FILENAME, CONFIG_KEY
|
|
157
|
+
from sourcecode.remedies import remedy as _remedy
|
|
158
|
+
|
|
149
159
|
payload.update(
|
|
150
160
|
{
|
|
151
161
|
"answered": False,
|
|
152
162
|
"statement": (
|
|
153
163
|
"No data labels are declared, so this run measured nothing. A "
|
|
154
164
|
"label is a judgement about a domain and is never inferred from a "
|
|
155
|
-
"field name: declare one under `
|
|
156
|
-
"
|
|
165
|
+
f"field name: declare one under `{CONFIG_KEY}` in "
|
|
166
|
+
f"{CONFIG_FILENAME} and re-run."
|
|
157
167
|
),
|
|
168
|
+
"remedy": _remedy("no_data_labels_declared").to_dict(),
|
|
169
|
+
"declare": {
|
|
170
|
+
"file": CONFIG_FILENAME,
|
|
171
|
+
"key": CONFIG_KEY,
|
|
172
|
+
"example": {
|
|
173
|
+
CONFIG_KEY: [
|
|
174
|
+
{
|
|
175
|
+
"label": "pii",
|
|
176
|
+
"types": ["com.example.Person"],
|
|
177
|
+
"fields": ["com.example.Account#iban"],
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
"note": (
|
|
182
|
+
"`types` labels every route whose signature names the type; "
|
|
183
|
+
"`fields` labels the declaring type through the member. Both "
|
|
184
|
+
"are fully-qualified, and neither is matched by name shape."
|
|
185
|
+
),
|
|
186
|
+
},
|
|
158
187
|
"summary": {
|
|
159
188
|
"labels": 0,
|
|
160
189
|
"seeds": 0,
|
sourcecode/remedies.py
CHANGED
|
@@ -75,6 +75,23 @@ REMEDIES: "dict[str, Remedy]" = {
|
|
|
75
75
|
),
|
|
76
76
|
headline="which profile set actually runs",
|
|
77
77
|
),
|
|
78
|
+
Remedy(
|
|
79
|
+
key="no_data_labels_declared",
|
|
80
|
+
command="data-exposure",
|
|
81
|
+
# The next move is a declaration, not a flag: nothing this command
|
|
82
|
+
# could be told on the command line would make it *infer* that a
|
|
83
|
+
# field is sensitive, because that is a judgement about a domain.
|
|
84
|
+
# The invocation is the re-run, and `declare` beside it in the
|
|
85
|
+
# payload carries the file, the key and a copy-pasteable example.
|
|
86
|
+
option="",
|
|
87
|
+
answers=(
|
|
88
|
+
"no data labels are declared, so nothing was measured — declare which "
|
|
89
|
+
"types and fields are sensitive under `dataLabels` in "
|
|
90
|
+
"sourcecode.config.json (a label is a judgement about a domain and is "
|
|
91
|
+
"never inferred from a field name), then re-run"
|
|
92
|
+
),
|
|
93
|
+
headline="declare the labels, then re-run",
|
|
94
|
+
),
|
|
78
95
|
Remedy(
|
|
79
96
|
key="no_contracts_declared",
|
|
80
97
|
command="verify",
|
sourcecode/ris.py
CHANGED
|
@@ -451,24 +451,34 @@ def _tree_signature(repo_root: Path) -> str:
|
|
|
451
451
|
|
|
452
452
|
|
|
453
453
|
def _has_uncommitted_changes(repo_root: Path) -> bool:
|
|
454
|
-
"""
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
454
|
+
"""True when the working tree differs from HEAD in a way the analysis sees.
|
|
455
|
+
|
|
456
|
+
C3-40: this used to pass ``--untracked-files=no``, to keep legacy
|
|
457
|
+
`.sourcecode-cache/` directories from reading as modifications. That excused
|
|
458
|
+
far more than the noise it was aimed at — **an untracked `.java` file is in
|
|
459
|
+
the IR**, so a repository with a new source file reported
|
|
460
|
+
`has_uncommitted_changes: False` and, through it, `fresh: true` for a snapshot
|
|
461
|
+
that did not contain that file. A false *fresh* is the confident-falsehood
|
|
462
|
+
direction. It also contradicted the root scan's own `--changed-only`, whose
|
|
463
|
+
help promises "staged, unstaged, **untracked**" in the same CLI.
|
|
464
|
+
|
|
465
|
+
One authority now answers it: `baseline_autocapture.worktree_dirty`, which
|
|
466
|
+
already had to get this exactly right for the architectural history. It
|
|
467
|
+
honours `.gitignore` (so build output and any gitignored cache directory are
|
|
468
|
+
not modifications — the original concern, handled properly) and excuses only
|
|
469
|
+
this tool's own footprint under `.ask/`, for the reason recorded there: the
|
|
470
|
+
first capture leaves an untracked file behind, and counting it would leave
|
|
471
|
+
every history one entry long.
|
|
472
|
+
|
|
473
|
+
Returns False when the state cannot be determined (non-git directories), which
|
|
474
|
+
is the pre-existing contract of this helper — `worktree_dirty` answers `None`
|
|
475
|
+
there, and the RIS freshness fields have no way to carry it.
|
|
459
476
|
"""
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
timeout=2,
|
|
466
|
-
)
|
|
467
|
-
if result.returncode == 0:
|
|
468
|
-
return bool(result.stdout.strip())
|
|
469
|
-
except Exception:
|
|
470
|
-
pass
|
|
471
|
-
return False
|
|
477
|
+
from sourcecode.baseline_autocapture import HISTORY_DIRNAME, worktree_dirty
|
|
478
|
+
|
|
479
|
+
root = Path(repo_root)
|
|
480
|
+
own = (root / HISTORY_DIRNAME, root / HISTORY_DIRNAME.parts[0])
|
|
481
|
+
return bool(worktree_dirty(root, ignore=own))
|
|
472
482
|
|
|
473
483
|
|
|
474
484
|
def get_cold_start_context(repo_root: Path) -> dict:
|
|
@@ -338,7 +338,22 @@ def scan_security_configuration(
|
|
|
338
338
|
java_files: "Optional[list[str]]" = None,
|
|
339
339
|
config_files: "Optional[list[str]]" = None,
|
|
340
340
|
) -> "list[SecurityConfigObservation]":
|
|
341
|
-
"""Every textbook weakness under ``root``. Best-effort per file, never fatal.
|
|
341
|
+
"""Every textbook weakness under ``root``. Best-effort per file, never fatal.
|
|
342
|
+
|
|
343
|
+
E-2: every rule here matches on raw text, and until 4.5.3 none of them could
|
|
344
|
+
tell code from a comment — `SEC-004` and `SEC-007` fired identically on a live
|
|
345
|
+
statement and on a commented-out block, which cost 31 % of the `high` findings
|
|
346
|
+
in a field evaluation. Comments are blanked **here**, at the three points a
|
|
347
|
+
file is read, rather than inside each rule: a rule added later is covered by
|
|
348
|
+
construction instead of by whoever remembers. `source_text` preserves offsets,
|
|
349
|
+
so the line each observation reports is still the line in the real file.
|
|
350
|
+
"""
|
|
351
|
+
from sourcecode.source_text import (
|
|
352
|
+
blank_hash_comments,
|
|
353
|
+
blank_java_comments,
|
|
354
|
+
blank_xml_comments,
|
|
355
|
+
)
|
|
356
|
+
|
|
342
357
|
root = Path(root)
|
|
343
358
|
if java_files is None:
|
|
344
359
|
try:
|
|
@@ -353,7 +368,7 @@ def scan_security_configuration(
|
|
|
353
368
|
source = (root / rel).read_text(encoding="utf-8", errors="replace")
|
|
354
369
|
except OSError:
|
|
355
370
|
continue
|
|
356
|
-
out.extend(_scan_java(source, rel))
|
|
371
|
+
out.extend(_scan_java(blank_java_comments(source), rel))
|
|
357
372
|
|
|
358
373
|
if config_files is None:
|
|
359
374
|
config_files = []
|
|
@@ -367,7 +382,7 @@ def scan_security_configuration(
|
|
|
367
382
|
text = (root / rel).read_text(encoding="utf-8", errors="replace")
|
|
368
383
|
except OSError:
|
|
369
384
|
continue
|
|
370
|
-
out.extend(_scan_config(text, rel))
|
|
385
|
+
out.extend(_scan_config(blank_hash_comments(text), rel))
|
|
371
386
|
|
|
372
387
|
# CL-10 — the descriptors. Discovered the same way `environment_resolution`
|
|
373
388
|
# classifies them, so the two surfaces cannot disagree about which files are
|
|
@@ -386,7 +401,7 @@ def scan_security_configuration(
|
|
|
386
401
|
text = (root / rel).read_text(encoding="utf-8", errors="replace")
|
|
387
402
|
except OSError:
|
|
388
403
|
continue
|
|
389
|
-
out.extend(_scan_descriptor(text, rel))
|
|
404
|
+
out.extend(_scan_descriptor(blank_xml_comments(text), rel))
|
|
390
405
|
|
|
391
406
|
return sorted(
|
|
392
407
|
_shared_across_environments(out), key=lambda o: (o.rule, o.file, o.line)
|
sourcecode/servlet_surface.py
CHANGED
|
@@ -144,9 +144,15 @@ def build_servlet_surface(root: Path, *, limit: Optional[int] = None) -> dict:
|
|
|
144
144
|
if any(part in _SKIP_DIRS for part in path.parts) or not path.is_file():
|
|
145
145
|
continue
|
|
146
146
|
try:
|
|
147
|
-
|
|
147
|
+
# POSIX, like every other path this CLI publishes. C2-18 fixed this
|
|
148
|
+
# for `spring_profiles.conditional_beans[].source_file`; this module
|
|
149
|
+
# shipped two releases later without the convention, so one response
|
|
150
|
+
# could carry `src/main/java/...` beside `src\main\options\...`
|
|
151
|
+
# and defeat a consumer joining on path — which is what a servlet
|
|
152
|
+
# population is for (C2-24).
|
|
153
|
+
relative = path.relative_to(root).as_posix()
|
|
148
154
|
except ValueError:
|
|
149
|
-
relative =
|
|
155
|
+
relative = Path(path).as_posix()
|
|
150
156
|
if path.name == "web.xml":
|
|
151
157
|
mappings.extend(_from_web_xml(path, relative))
|
|
152
158
|
elif path.suffix == ".java":
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""source_text.py — the one authority for "this text, with comments neutralised".
|
|
2
|
+
|
|
3
|
+
A static analyzer that matches raw text cannot tell code from a note about code.
|
|
4
|
+
This project has now paid for that three times:
|
|
5
|
+
|
|
6
|
+
* **E-1 (3.7.0)** — a comment containing `class` before an annotated declaration
|
|
7
|
+
swallowed the real declaration, and the file left the graph entirely.
|
|
8
|
+
* **E-2 (4.5.3)** — the security configuration scan reported `SEC-004` and
|
|
9
|
+
`SEC-007` on commented-out code, which cost **31 % of the `high` findings** in a
|
|
10
|
+
field evaluation. Worse than the count: the correlation engine wrote a
|
|
11
|
+
persuasive paragraph about credentials in a block that was switched off.
|
|
12
|
+
Confident prose about dead code is worse than a terse warning, because it
|
|
13
|
+
invites trust.
|
|
14
|
+
|
|
15
|
+
**Offsets are preserved exactly.** Comment bodies are overwritten with spaces and
|
|
16
|
+
every newline is kept, so the result has the same length and the same line
|
|
17
|
+
structure as the input. A match found in the blanked text has the same
|
|
18
|
+
`start()`, and therefore the same reported line, as it would in the original —
|
|
19
|
+
which is what lets a scanner keep quoting the source file it read.
|
|
20
|
+
|
|
21
|
+
**String literals are deliberately kept.** This is the difference between these
|
|
22
|
+
functions and `hibernate_strat._strip_comments_strings`, which blanks literal
|
|
23
|
+
*content* on purpose so that a pattern cannot match a substring living inside a
|
|
24
|
+
string. The security rules need the opposite: `new MessageDigestPasswordEncoder(
|
|
25
|
+
"SHA-1")` and `<param-value>admin:{SHA-256}…</param-value>` carry their evidence
|
|
26
|
+
*in* the literal. Two different questions, two functions, neither a copy of the
|
|
27
|
+
other.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import re
|
|
33
|
+
|
|
34
|
+
#: Java/C-family lexical tokens, in precedence order. String and character
|
|
35
|
+
#: literals are matched **first** so that a `//` or `/*` living inside one — as in
|
|
36
|
+
#: `String url = "http://example.com";` — is never mistaken for a comment.
|
|
37
|
+
_JAVA_TOKENS = re.compile(
|
|
38
|
+
r'"""(?:\\.|[^\\])*?"""' # text block (Java 15+), before the plain string
|
|
39
|
+
r'|"(?:\\.|[^"\\\n])*"' # string literal
|
|
40
|
+
r"|'(?:\\.|[^'\\\n])*'" # char literal
|
|
41
|
+
r"|(?P<line>//[^\n]*)" # line comment
|
|
42
|
+
r"|(?P<block>/\*.*?\*/)", # block comment
|
|
43
|
+
re.DOTALL,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
#: XML/HTML comments. Unterminated ones run to end of input on purpose: an
|
|
47
|
+
#: unclosed `<!--` comments out the rest of the document for a parser too.
|
|
48
|
+
_XML_COMMENT = re.compile(r"<!--.*?(?:-->|\Z)", re.DOTALL)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _blank(text: str) -> str:
|
|
52
|
+
"""Same length, same newlines, no content."""
|
|
53
|
+
return "".join("\n" if ch == "\n" else " " for ch in text)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def blank_java_comments(source: str) -> str:
|
|
57
|
+
"""Java source with comment bodies replaced by spaces, offsets preserved.
|
|
58
|
+
|
|
59
|
+
String and character literals are left intact — see the module docstring.
|
|
60
|
+
"""
|
|
61
|
+
def _replace(match: "re.Match[str]") -> str:
|
|
62
|
+
if match.lastgroup in ("line", "block"):
|
|
63
|
+
return _blank(match.group(0))
|
|
64
|
+
return match.group(0) # a literal: untouched
|
|
65
|
+
|
|
66
|
+
return _JAVA_TOKENS.sub(_replace, source)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def blank_xml_comments(source: str) -> str:
|
|
70
|
+
"""XML/HTML with comment bodies replaced by spaces, offsets preserved."""
|
|
71
|
+
return _XML_COMMENT.sub(lambda m: _blank(m.group(0)), source)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
#: A `#` comment in `.properties`/YAML, but only where it opens the line. A `#`
|
|
75
|
+
#: further along may be a comment in YAML and is part of the value in
|
|
76
|
+
#: `.properties` (`password=abc#123`), and blanking that would corrupt evidence to
|
|
77
|
+
#: avoid a false positive — the wrong trade for a scan that quotes what it found.
|
|
78
|
+
#: The fully-commented-out block E-2 is about is covered by the line-start form.
|
|
79
|
+
_HASH_COMMENT_LINE = re.compile(r"^[ \t]*#[^\n]*", re.MULTILINE)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def blank_hash_comments(source: str) -> str:
|
|
83
|
+
"""`.properties`/YAML with whole-line `#` comments blanked, offsets preserved."""
|
|
84
|
+
return _HASH_COMMENT_LINE.sub(lambda m: _blank(m.group(0)), source)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def commented_spans(source: str, *, xml: bool) -> "list[tuple[int, int]]":
|
|
88
|
+
"""`(start, end)` offsets of every comment — what was switched off, not what runs.
|
|
89
|
+
|
|
90
|
+
The inverse of the functions above, and the reason they blank rather than
|
|
91
|
+
delete. A control that is present but commented out is not noise: somebody
|
|
92
|
+
decided it was needed and then turned it off, which is the most actionable
|
|
93
|
+
thing a reader can be told about a security surface. E-2's fix removes those
|
|
94
|
+
matches from the findings; this is what a future rule reports them *as*
|
|
95
|
+
(DEAD-001, queued as F-A). Nothing consumes it yet — it exists so the two
|
|
96
|
+
halves cannot drift apart when it does.
|
|
97
|
+
"""
|
|
98
|
+
pattern = _XML_COMMENT if xml else _JAVA_TOKENS
|
|
99
|
+
spans: "list[tuple[int, int]]" = []
|
|
100
|
+
for match in pattern.finditer(source):
|
|
101
|
+
if xml or match.lastgroup in ("line", "block"):
|
|
102
|
+
spans.append((match.start(), match.end()))
|
|
103
|
+
return spans
|
sourcecode/token_estimate.py
CHANGED
|
@@ -134,7 +134,7 @@ def collect_anchor_files(root: Path) -> list[str]:
|
|
|
134
134
|
out: set[str] = set()
|
|
135
135
|
for p in paths:
|
|
136
136
|
try:
|
|
137
|
-
out.add(
|
|
137
|
+
out.add(Path(p).resolve().relative_to(root).as_posix())
|
|
138
138
|
except (OSError, ValueError):
|
|
139
139
|
continue
|
|
140
140
|
return sorted(out)
|
sourcecode/verify_edit.py
CHANGED
|
@@ -519,8 +519,8 @@ def _custom_rules_axis(cir_head: Any, cir_working: Any, root: Path) -> AxisResul
|
|
|
519
519
|
return AxisResult(False, "no custom rules")
|
|
520
520
|
|
|
521
521
|
try:
|
|
522
|
-
head_ids = {v.id for v in vr.evaluate_all(rules, cir_head)}
|
|
523
|
-
work = vr.evaluate_all(rules, cir_working)
|
|
522
|
+
head_ids = {v.id for v in vr.evaluate_all(rules, cir_head, root)}
|
|
523
|
+
work = vr.evaluate_all(rules, cir_working, root)
|
|
524
524
|
except Exception as exc: # evaluator fault → honest degrade, never false-clean
|
|
525
525
|
return AxisResult(None, f"rule evaluation error: {exc}")
|
|
526
526
|
|
sourcecode/verify_repo.py
CHANGED
|
@@ -179,7 +179,8 @@ def evaluate_repo(
|
|
|
179
179
|
on_cir(cir)
|
|
180
180
|
except Exception: # noqa: BLE001 — a passenger never crashes the gate
|
|
181
181
|
pass
|
|
182
|
-
|
|
182
|
+
# `root` is what lets the security *configuration* rules fire at all (C1-24).
|
|
183
|
+
return evaluate_all(rules, cir, root), path, len(rules)
|
|
183
184
|
|
|
184
185
|
|
|
185
186
|
def verify_repo(
|
sourcecode/verify_rules.py
CHANGED
|
@@ -157,7 +157,7 @@ class ForbiddenEdgeRule:
|
|
|
157
157
|
from_sel: Selector
|
|
158
158
|
to_sel: Selector
|
|
159
159
|
|
|
160
|
-
def evaluate(self, cir: Any) -> list[Violation]:
|
|
160
|
+
def evaluate(self, cir: Any, root: "Optional[Path]" = None) -> list[Violation]:
|
|
161
161
|
nodes = {n.get("fqn"): n for n in _nodes(cir) if n.get("fqn")}
|
|
162
162
|
owner = _owner_type_map(cir) # member fqn → declaring type fqn
|
|
163
163
|
out: list[Violation] = []
|
|
@@ -201,7 +201,7 @@ class RequireEndpointSecurityRule:
|
|
|
201
201
|
require_policy: Optional[str]
|
|
202
202
|
require_role: Optional[str]
|
|
203
203
|
|
|
204
|
-
def evaluate(self, cir: Any) -> list[Violation]:
|
|
204
|
+
def evaluate(self, cir: Any, root: "Optional[Path]" = None) -> list[Violation]:
|
|
205
205
|
out: list[Violation] = []
|
|
206
206
|
for ep in cir.endpoints:
|
|
207
207
|
if not self.path_re.search(ep.path or ""):
|
|
@@ -224,32 +224,48 @@ class RequireEndpointSecurityRule:
|
|
|
224
224
|
_SEVERITY_ORDER = ("low", "medium", "high", "critical")
|
|
225
225
|
|
|
226
226
|
|
|
227
|
-
def _audit_findings(cir: Any) -> list[Any]:
|
|
227
|
+
def _audit_findings(cir: Any, root: "Optional[Path]" = None) -> list[Any]:
|
|
228
228
|
"""Findings of the Spring audit for this CIR, computed once per CIR.
|
|
229
229
|
|
|
230
230
|
A contract that forbids a finding class does not need a second detector —
|
|
231
231
|
the transaction and security patterns already ship. Memoised on the CIR so
|
|
232
232
|
ten contracts over the same repository run the audit once.
|
|
233
|
+
|
|
234
|
+
**`root` is not optional in practice, and C1-24 is why.** Half of the shipped
|
|
235
|
+
security rules — SEC-004…SEC-007 — are produced by `security_config_scan`,
|
|
236
|
+
which reads source and descriptor *text*; `SecurityScanner.analyze` returns
|
|
237
|
+
an empty list when it has no root to read from. This function used to call
|
|
238
|
+
`run_security_audit(cir, model=model)` with no root, so it saw the CIR
|
|
239
|
+
pattern rules and none of the configuration rules — and `verify --init`, its
|
|
240
|
+
only derivation consumer, concluded that SEC-004 "fires nowhere in this
|
|
241
|
+
repository" on a tree where `spring-audit` reported it `high`. One question
|
|
242
|
+
("what does the audit find?") answered by two authorities, which is the C1
|
|
243
|
+
class; the first time it produced a *fabricable contract*.
|
|
244
|
+
|
|
245
|
+
The memo key carries the root for the same reason: a cached answer computed
|
|
246
|
+
without one must not be served to a caller that supplied one.
|
|
233
247
|
"""
|
|
234
|
-
|
|
248
|
+
key = f"_verify_audit_findings::{root}"
|
|
249
|
+
cached = getattr(cir, key, None)
|
|
235
250
|
if cached is not None:
|
|
236
251
|
return cached
|
|
237
252
|
from sourcecode.spring_model import SpringSemanticModel
|
|
238
253
|
from sourcecode.spring_security_audit import run_security_audit
|
|
239
254
|
from sourcecode.spring_tx_analyzer import run_tx_audit
|
|
240
255
|
|
|
241
|
-
# Same two audits `spring-audit` runs, over one shared semantic model
|
|
256
|
+
# Same two audits `spring-audit` runs, over one shared semantic model, and
|
|
257
|
+
# with the same `root` — the argument is what makes them the same audit.
|
|
242
258
|
model = SpringSemanticModel.build(cir)
|
|
243
259
|
findings = [
|
|
244
260
|
f
|
|
245
261
|
for result in (
|
|
246
|
-
run_tx_audit(cir, model=model),
|
|
247
|
-
run_security_audit(cir, model=model),
|
|
262
|
+
run_tx_audit(cir, root=root, model=model),
|
|
263
|
+
run_security_audit(cir, root=root, model=model),
|
|
248
264
|
)
|
|
249
265
|
for f in result.findings
|
|
250
266
|
]
|
|
251
267
|
try:
|
|
252
|
-
object.__setattr__(cir,
|
|
268
|
+
object.__setattr__(cir, key, findings)
|
|
253
269
|
except Exception:
|
|
254
270
|
pass # frozen or slotted CIR — recompute rather than fail
|
|
255
271
|
return findings
|
|
@@ -270,10 +286,10 @@ class ForbidFindingRule:
|
|
|
270
286
|
category: Optional[str]
|
|
271
287
|
min_severity: str
|
|
272
288
|
|
|
273
|
-
def evaluate(self, cir: Any) -> list[Violation]:
|
|
289
|
+
def evaluate(self, cir: Any, root: "Optional[Path]" = None) -> list[Violation]:
|
|
274
290
|
floor = _SEVERITY_ORDER.index(self.min_severity)
|
|
275
291
|
out: list[Violation] = []
|
|
276
|
-
for f in _audit_findings(cir):
|
|
292
|
+
for f in _audit_findings(cir, root):
|
|
277
293
|
if self.pattern_id is not None and f.pattern_id != self.pattern_id:
|
|
278
294
|
continue
|
|
279
295
|
if self.category is not None and f.category != self.category:
|
|
@@ -420,8 +436,12 @@ def load_rules(root: Path) -> list[Rule]:
|
|
|
420
436
|
return parse_rules(raw)
|
|
421
437
|
|
|
422
438
|
|
|
423
|
-
def evaluate_all(
|
|
439
|
+
def evaluate_all(
|
|
440
|
+
rules: list[Rule], cir: Any, root: "Optional[Path]" = None
|
|
441
|
+
) -> list[Violation]:
|
|
442
|
+
"""Evaluate every rule. `root` reaches the rules that read files rather than
|
|
443
|
+
the graph — without it the security configuration rules cannot fire (C1-24)."""
|
|
424
444
|
out: list[Violation] = []
|
|
425
445
|
for r in rules:
|
|
426
|
-
out.extend(r.evaluate(cir))
|
|
446
|
+
out.extend(r.evaluate(cir, root))
|
|
427
447
|
return out
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 4.5.
|
|
3
|
+
Version: 4.5.3
|
|
4
4
|
Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Keywords: agents,ai,codebase,context,developer-tools,llm
|
|
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
|
|
|
42
42
|
|
|
43
43
|
**Context · Impact · Migration · Architecture · Review — everything from one structural model.**
|
|
44
44
|
|
|
45
|
-

|
|
46
46
|

|
|
47
47
|
|
|
48
48
|
> **ASK Engine** is the product. The CLI command is **`ask`**. The legacy **`sourcecode`**
|
|
@@ -97,7 +97,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
|
|
|
97
97
|
# pip / pipx
|
|
98
98
|
pipx install sourcecode # or: pip install sourcecode
|
|
99
99
|
|
|
100
|
-
ask version # ask 4.5.
|
|
100
|
+
ask version # ask 4.5.3 — and, on a build that has aged,
|
|
101
101
|
# how many releases have probably shipped since
|
|
102
102
|
```
|
|
103
103
|
|
|
@@ -309,7 +309,7 @@ from it.
|
|
|
309
309
|
| `risk` | experimental | what each defect actually costs, once reach, access, write effect and the shape of the input path are in it | `defect_severity × reachability × auth_verdict × write_effect × query_construction × input_constraints`; every factor names its authority |
|
|
310
310
|
| `enrich` | experimental | another scanner's SARIF findings, ranked by what this repository does with them | `--sarif <log>`; the same composition as `risk`, with the scanner as the severity authority |
|
|
311
311
|
| `migrate-recipe` | experimental | the migration report as the OpenRewrite recipe that applies it | only recipes a finding named; the manual remainder published beside them; writes nothing without `--write`, and never a runnable command for an empty recipe list |
|
|
312
|
-
| `data-exposure` | experimental | which routes can carry the data you labelled, and who reaches them | labels declared in `sourcecode.config.json` — never inferred from a name; `signature` and `call_reach` evidence published apart, field-level flow out of scope (NC-008) |
|
|
312
|
+
| `data-exposure` | experimental | which routes can carry the data you labelled, and who reaches them | labels declared in `sourcecode.config.json` — never inferred from a name; `signature` and `call_reach` evidence published apart, field-level flow out of scope (NC-008). With nothing declared, `ask data-exposure /path/to/repo` answers `answered: false` and hands back the file, the key and an example to declare — it never reports zero exposed routes |
|
|
313
313
|
| `endpoints` | core | every REST endpoint, effective path, security policy, confidence | Spring MVC + JAX-RS (~65 % recall on JAX-RS sub-resource locators). `--compact` answers the exposure census without the rows; `--servlets` lists the servlet-mounted surface as its own population; `--client-usage` says which routes the TS/JS client in this repository actually calls |
|
|
314
314
|
| `spring-audit` | core | transactional anomalies + security surface + validation gaps | `--ci`, `-f github-comment` |
|
|
315
315
|
| `migrate-check` | core | Boot 2→3 readiness: located blockers, per-dimension score, effort | `--blast-radius` orders the re-test plan |
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=TB3NC3ZIhSq8CPZFlNi9Y6qRAqfWAwRIj4a5XRpDjx0,308
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=yJBKjNpkY6bpueYJ2YnRezen3sYZDecEt7WaaNWdqug,9466
|
|
3
3
|
sourcecode/archetype.py,sha256=HBGTTaS-bVHS6pdacPUMKcMxklkDEKnZMgz48Kc3yec,37630
|
|
4
4
|
sourcecode/architectural_baseline.py,sha256=agDSwGEakdkgLLh5xnd3hiD_TC6pRujWt4klnuYF1wo,18689
|
|
5
5
|
sourcecode/architectural_delta.py,sha256=E6MkyjWl-1ZgR4KSKnI785gUAfc8nq-lK-jd_rupQBU,12496
|
|
6
6
|
sourcecode/architecture_analyzer.py,sha256=GFc4ek-s1IHWM7pl-0L32WahZ93AmDgrAMcOuBKA5Dk,61463
|
|
7
7
|
sourcecode/architecture_summary.py,sha256=BVVRHd952cjRhjHnR6CPrvKgaa-tdM16l-pBi1yDCPs,32395
|
|
8
|
-
sourcecode/ast_extractor.py,sha256=
|
|
8
|
+
sourcecode/ast_extractor.py,sha256=aXJjZ7XjAdxa99hWNm4SyzPqx4gNWiTbUYjNWGNl-Vc,51213
|
|
9
9
|
sourcecode/baseline_autocapture.py,sha256=xxfXT9Nu4uiDbWMZRawcCXsA4fjgEjZW2qI173dq3GE,14962
|
|
10
10
|
sourcecode/cache.py,sha256=Es9ZLqGRoheMv2bOD1azaFKqU2zJKJVudHhjQKcIxy8,40128
|
|
11
11
|
sourcecode/cache_model.py,sha256=hUGWNKBKLV76mGveFffOzr9fqFD1ZydJVakseRsg14s,17401
|
|
@@ -17,8 +17,8 @@ sourcecode/chain_rules.py,sha256=Bi6UHfgd-GxWswmnHRcPz5jdbAuqka3Zkz_P-MTvqhw,127
|
|
|
17
17
|
sourcecode/change_plan.py,sha256=kFjjp16XYbupgkv1CPkfqo39_SiZPMRQ8OOfC-Vy9eg,7929
|
|
18
18
|
sourcecode/cir_graphs.py,sha256=9G0HHj1kw2325IDyzo2OpX73BNswEckecf4MZUXB4JM,12078
|
|
19
19
|
sourcecode/classifier.py,sha256=JBzPwSSrDG-tUHAbcKB678HRbjLpD-ohzbzzO62mgpo,20114
|
|
20
|
-
sourcecode/cli.py,sha256=
|
|
21
|
-
sourcecode/client_calls.py,sha256=
|
|
20
|
+
sourcecode/cli.py,sha256=J9pEXkcMWFHg0FXW6CxWa69zvP2DHHOXlgliKXNc4Dc,492534
|
|
21
|
+
sourcecode/client_calls.py,sha256=daRTgbXNUOfkzGXJpVb6A737R_Thhka8vw1_YgxCaLg,13548
|
|
22
22
|
sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
|
|
23
23
|
sourcecode/compare.py,sha256=xq3zsqAOAw4AWkoD9khb9xDP_O3KvwqO9k-pf8sbi3g,10951
|
|
24
24
|
sourcecode/confidence_analyzer.py,sha256=5li0NyOdS3Ie-f2ZY1gBmnjtkuRvI50l4OnGaKkXAOU,22358
|
|
@@ -28,11 +28,11 @@ sourcecode/context_graph.py,sha256=UBUW97DnJe5hGgCI-JohgKWDfKTszBuL3DIxQylVZQE,4
|
|
|
28
28
|
sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
|
|
29
29
|
sourcecode/context_summarizer.py,sha256=cI2TZMvEhl0BEma12VtPaX6z03ZVBAetVzuK5GaCOvg,6852
|
|
30
30
|
sourcecode/contract_diff.py,sha256=ivgHL1qRKFhriwQ52KWj4oJselqd5icSJW7Z2Ke04po,9429
|
|
31
|
-
sourcecode/contract_init.py,sha256=
|
|
31
|
+
sourcecode/contract_init.py,sha256=bXRCqWM9tvrDLeT8j7ea6IWf87jB4VJxnlSzGBsRRXo,19582
|
|
32
32
|
sourcecode/contract_model.py,sha256=nRxJKPMs1VHwFTa8AVXhGmaLjti3Lr2sjHDpWgv1bfE,3917
|
|
33
33
|
sourcecode/contract_pipeline.py,sha256=4XE-xhab7ysdExXyUOBl2hAH4MABo7moKnT9V4XcEwQ,30243
|
|
34
34
|
sourcecode/coverage_parser.py,sha256=X9scuUWxacahYTrTBGea2Ku-sf-WCVcnc9OEB35_3IU,19340
|
|
35
|
-
sourcecode/data_exposure.py,sha256=
|
|
35
|
+
sourcecode/data_exposure.py,sha256=pp2WVTjWZGHuXh-1m3mfCEb5awzKQbUjs61t3Sy2En4,15248
|
|
36
36
|
sourcecode/data_labels.py,sha256=IF8xedY0uPFefpDpOSYrb1ita_XoyjPctlXTXAMe4pc,7230
|
|
37
37
|
sourcecode/defect_identity.py,sha256=XS2VFDImg9NLaRImybKTeyk-CoDSsXmchldg0dwvM7c,7028
|
|
38
38
|
sourcecode/degradation.py,sha256=BtuGUKFgkzI3Z6fwmUdxEUEaLFAcPQ_bHhU2w9ItDxw,18376
|
|
@@ -88,11 +88,11 @@ sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
|
|
|
88
88
|
sourcecode/reference_facts.py,sha256=Ns495c6eTmq2SrqPkcUrJUJru4_wj_yRWm4YDeJwves,13440
|
|
89
89
|
sourcecode/release_info.py,sha256=yxvjQM2HNzt84tGEOeTwwU8ZBcuupzjGLiwLFziMJL4,5378
|
|
90
90
|
sourcecode/relevance_scorer.py,sha256=0AgEt4KrV73nioMqBgjhGjtY7L2C7L7cSyKtj3IKcrw,9408
|
|
91
|
-
sourcecode/remedies.py,sha256=
|
|
91
|
+
sourcecode/remedies.py,sha256=8lgvKkW1WuBG-QfTnA0uBhEKnwWGSvgSMWCfTjzYhEY,6516
|
|
92
92
|
sourcecode/rename_refactor.py,sha256=h6dNFlB9aZ_3q6heeHBkgXQeXaT03nvPSsYH6P8qxFg,12965
|
|
93
93
|
sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
|
|
94
94
|
sourcecode/repository_ir.py,sha256=uuJsPmtgSEi4MKVKJEiZl8bpvU1QFU2Oslxn96p5xi8,356849
|
|
95
|
-
sourcecode/ris.py,sha256=
|
|
95
|
+
sourcecode/ris.py,sha256=SjjDNMcc9Zr9us9G8ikBVkoPXKK3BpM-dBodSGMh_g4,24783
|
|
96
96
|
sourcecode/risk.py,sha256=cgkHu4fjAyLs9xSytegdv4b8ua8RqRJEQ5rHbQKgzfo,34851
|
|
97
97
|
sourcecode/rule_catalog.py,sha256=buU6qZI1j1EjqAWOIzeDhZ9kO_6Tp_Ma-dmexNWkMe0,4324
|
|
98
98
|
sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
|
|
@@ -100,14 +100,15 @@ sourcecode/sarif.py,sha256=_3ggbtV0O2iK5zeUiJSS36KxpPRVAtGy0hzXAFBmww8,25590
|
|
|
100
100
|
sourcecode/scanner.py,sha256=z3CV0rcGunu0Y8mpNgp07wI7nxT0pxw1BkXRRtI0Rpo,9609
|
|
101
101
|
sourcecode/schema.py,sha256=aHNXDf8LGyUC8ZDE_VS9kiskC2-Oswhi_WnpdGy6HDw,24897
|
|
102
102
|
sourcecode/security_config.py,sha256=KblMEoRiEjrIE68YsPaUAFebxFp8UM7MS7lAk5CGD8U,3531
|
|
103
|
-
sourcecode/security_config_scan.py,sha256=
|
|
103
|
+
sourcecode/security_config_scan.py,sha256=9MC3N9G3627jOsP9SI2et_TQEUbdlmxuHq1Ycm-msBg,16920
|
|
104
104
|
sourcecode/security_posture.py,sha256=CjJ2Qm87HhKXepnaWx1ncJLW-SxI18udCmUDIkCyAsY,50270
|
|
105
105
|
sourcecode/semantic_analyzer.py,sha256=bpgdC6m0_ftVtRf3rSdwhbhWjnZnGxRXaZVcfe4BbcQ,95414
|
|
106
106
|
sourcecode/semantic_impact_engine.py,sha256=t09IirGC3JjQDy33JZd1_WKzQVKXkoNl3-XEUr5kjis,20563
|
|
107
107
|
sourcecode/semantic_integration_engine.py,sha256=7a0WqAInOv39f0Yr_94TYo_JP_8QpeI9KGaknpzAyQU,18899
|
|
108
108
|
sourcecode/semantic_services.py,sha256=nbUuPv-F01USTt_9CHT8iy_ucCIw3fz4W3Aquea_pd4,10782
|
|
109
109
|
sourcecode/serializer.py,sha256=T1-Ybi6aPczxiz11N_nfuZ_jUMQVBMExeWuYHCUjKPc,139222
|
|
110
|
-
sourcecode/servlet_surface.py,sha256=
|
|
110
|
+
sourcecode/servlet_surface.py,sha256=kwIBS6aT-DJHu8mqzvhlYrhaqAq_005SDAeVZ7yx8Y0,8165
|
|
111
|
+
sourcecode/source_text.py,sha256=Q5v5kAq2QFZ-HcaK5UXp_xZTbiLiFW5p09v7r_hQHnU,4878
|
|
111
112
|
sourcecode/spring_event_topology.py,sha256=5_ON_21Le5zbG-1GRc5GLIi5HJfy_QjcXLVPC5WeUGQ,18055
|
|
112
113
|
sourcecode/spring_findings.py,sha256=qgWz4LLL5TFe3eG-mTttlrDTx-6ps3EOuLA-S2HvQCM,16945
|
|
113
114
|
sourcecode/spring_impact.py,sha256=KkThpPmBbBJk_1H8f0cVWOuz6vBN-yX5qcTQ3Sjmjnk,74797
|
|
@@ -121,14 +122,14 @@ sourcecode/summarizer.py,sha256=0aD4x3vgPngqBCEBKGuES1J2Vk5f7mqCm_ZWErwm3js,2702
|
|
|
121
122
|
sourcecode/target_admission.py,sha256=wFZ4pzlxhiF6Q6s2lEAZEzcCfj1Y6xNvujjt8MdO0Qo,7154
|
|
122
123
|
sourcecode/test_gap_ranking.py,sha256=hl-tTyQUXZGJycG1b6npbLeE_DaVHaOsvPwG9qqC5y8,15711
|
|
123
124
|
sourcecode/test_sources.py,sha256=cMGVNLbYZZ2yt2PVcySK4scbjKpCLDAKhIViyWN_pRM,6986
|
|
124
|
-
sourcecode/token_estimate.py,sha256=
|
|
125
|
+
sourcecode/token_estimate.py,sha256=G-ivzIJfVq5TLQN04dC0WK4LZJjWVYL54Ebcxe4fJ7M,9268
|
|
125
126
|
sourcecode/tree_utils.py,sha256=8GAkIfQAsvtEudIeW1l4ooH_oRtrWR8cpJQJsEa_Pfw,2093
|
|
126
127
|
sourcecode/type_usage_surface.py,sha256=51IrKRQoIoRnlsiDjHnqpJBn2rc6E59aRhgS0HTzAF0,4428
|
|
127
128
|
sourcecode/validation_inference.py,sha256=-oWJqE6PqkqcZbCFDJeWCQHgI9Dbuv5ggJWD3LE_2IU,20817
|
|
128
129
|
sourcecode/validation_surface.py,sha256=jYL-hkDjaaRKkAt7ZUcbxm3Dydl8o6KVRhTkuPXKPVc,31460
|
|
129
|
-
sourcecode/verify_edit.py,sha256=
|
|
130
|
-
sourcecode/verify_repo.py,sha256=
|
|
131
|
-
sourcecode/verify_rules.py,sha256=
|
|
130
|
+
sourcecode/verify_edit.py,sha256=Toqx4z47gLUmcj5F673eTFq1dE_6e4T3mXVesjkPx6g,35955
|
|
131
|
+
sourcecode/verify_repo.py,sha256=ltSP1nFc726k2aTHCu5dfhxQgxQwz3nrUNDMDN-aDa8,12158
|
|
132
|
+
sourcecode/verify_rules.py,sha256=kqo-tuqOfxSti3jIVS-bJtbSV5b1KxdigOR0j5Wr_XM,19388
|
|
132
133
|
sourcecode/version_check.py,sha256=CHp6ZxTIfo8kyHPCBgJA1uFC0xQCoXMuuOfrW8QTL8o,4942
|
|
133
134
|
sourcecode/workspace.py,sha256=X_6NmNnitvT3_38V-JDChydo_sR68s249hLFlrQskU0,8271
|
|
134
135
|
sourcecode/detectors/__init__.py,sha256=A0AACJFF6HWf_RgatNtWu3PUzstcKtIGM9f1PoFcJug,1987
|
|
@@ -189,8 +190,8 @@ sourcecode/telemetry/consent.py,sha256=pQdl-QeLl6Gcibn0eWHSKZrm-HYSsjpVqOnjrgFp8
|
|
|
189
190
|
sourcecode/telemetry/events.py,sha256=4_yeO58U-Cwc1Qb27VB0_EjhmroY0k91n3_VGxeALB8,2776
|
|
190
191
|
sourcecode/telemetry/filters.py,sha256=RzxauTz8HliO4BllQnXEXc7zTeqdCZi5MgqGEDuW7OQ,6570
|
|
191
192
|
sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
|
|
192
|
-
sourcecode-4.5.
|
|
193
|
-
sourcecode-4.5.
|
|
194
|
-
sourcecode-4.5.
|
|
195
|
-
sourcecode-4.5.
|
|
196
|
-
sourcecode-4.5.
|
|
193
|
+
sourcecode-4.5.3.dist-info/METADATA,sha256=md2ShdxOBK6RppqSdpqL8YjqIDoo3PRAHMZxb96YT64,34161
|
|
194
|
+
sourcecode-4.5.3.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
195
|
+
sourcecode-4.5.3.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
|
|
196
|
+
sourcecode-4.5.3.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
197
|
+
sourcecode-4.5.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|