sourcecode 3.7.0__py3-none-any.whl → 4.0.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.
- sourcecode/__init__.py +1 -1
- sourcecode/archetype.py +43 -16
- sourcecode/cache.py +48 -0
- sourcecode/cache_model.py +17 -7
- sourcecode/cli.py +562 -127
- sourcecode/confidence_analyzer.py +7 -2
- sourcecode/contract_init.py +419 -0
- sourcecode/defect_identity.py +166 -0
- sourcecode/degradation.py +52 -0
- sourcecode/detectors/java.py +15 -31
- sourcecode/environment_resolution.py +361 -0
- sourcecode/filter_surface.py +243 -12
- sourcecode/git_checkout.py +149 -0
- sourcecode/identity_fallback.py +244 -0
- sourcecode/migrate_check.py +79 -7
- sourcecode/non_coverage.py +198 -0
- sourcecode/parse_cache.py +23 -0
- sourcecode/posture.py +201 -6
- sourcecode/pr_comment_renderer.py +13 -0
- sourcecode/prepare_context.py +110 -3
- sourcecode/remedies.py +87 -0
- sourcecode/repository_ir.py +43 -4
- sourcecode/retrieval/result.py +24 -9
- sourcecode/retrieval/steps.py +3 -0
- sourcecode/retrieval/steps_endpoint.py +5 -5
- sourcecode/retrieval/steps_graph.py +5 -5
- sourcecode/retrieval/steps_impact.py +19 -7
- sourcecode/retrieval/steps_intf.py +4 -4
- sourcecode/retrieval/steps_struct.py +5 -5
- sourcecode/retrieval/steps_txsec.py +5 -5
- sourcecode/risk.py +345 -0
- sourcecode/security_config_scan.py +262 -0
- sourcecode/security_posture.py +216 -23
- sourcecode/serializer.py +39 -5
- sourcecode/spring_findings.py +67 -4
- sourcecode/spring_profiles.py +199 -3
- sourcecode/spring_properties.py +1 -1
- sourcecode/spring_security_audit.py +88 -0
- sourcecode/spring_tx_analyzer.py +52 -0
- sourcecode/summarizer.py +7 -2
- sourcecode/test_gap_ranking.py +363 -0
- sourcecode/token_estimate.py +36 -0
- sourcecode/verify_repo.py +8 -0
- {sourcecode-3.7.0.dist-info → sourcecode-4.0.0.dist-info}/METADATA +34 -4
- {sourcecode-3.7.0.dist-info → sourcecode-4.0.0.dist-info}/RECORD +48 -38
- {sourcecode-3.7.0.dist-info → sourcecode-4.0.0.dist-info}/WHEEL +0 -0
- {sourcecode-3.7.0.dist-info → sourcecode-4.0.0.dist-info}/entry_points.txt +0 -0
- {sourcecode-3.7.0.dist-info → sourcecode-4.0.0.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/archetype.py
CHANGED
|
@@ -175,8 +175,13 @@ class _Features:
|
|
|
175
175
|
module_count: int
|
|
176
176
|
concept_mass: dict[str, float] # concept → fraction of source files
|
|
177
177
|
entry_kinds: dict[str, int]
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
# `None` when the endpoint surface was NOT measured (no root to read, or the
|
|
179
|
+
# extraction failed). It used to be 0, which is a different claim: a score
|
|
180
|
+
# that reads "no HTTP surface" off an unknown one refutes `api-first` and
|
|
181
|
+
# actively supports `worker`, so an unmeasured repository was classified as
|
|
182
|
+
# if it exposed nothing (I-3/R9, inside a score).
|
|
183
|
+
endpoint_total: "Optional[int]"
|
|
184
|
+
endpoint_share: "Optional[float]" # endpoints per source file
|
|
180
185
|
packaging: Optional[str]
|
|
181
186
|
frameworks: set[str]
|
|
182
187
|
graph_hubs: list[str]
|
|
@@ -250,7 +255,11 @@ class ArchetypeClassifier:
|
|
|
250
255
|
signals_missing=f.signals_missing,
|
|
251
256
|
generated_from=(
|
|
252
257
|
f"{f.total_files} source files, {f.module_count} modules, "
|
|
253
|
-
|
|
258
|
+
+ (
|
|
259
|
+
f"{f.endpoint_total} endpoints"
|
|
260
|
+
if f.endpoint_total is not None
|
|
261
|
+
else "endpoint surface not measured"
|
|
262
|
+
)
|
|
254
263
|
),
|
|
255
264
|
graph_metrics=f.graph_metrics,
|
|
256
265
|
)
|
|
@@ -299,8 +308,10 @@ class ArchetypeClassifier:
|
|
|
299
308
|
missing.append("entry_points")
|
|
300
309
|
|
|
301
310
|
endpoint_total = self._endpoint_count(root)
|
|
302
|
-
endpoint_share = (
|
|
303
|
-
|
|
311
|
+
endpoint_share = (
|
|
312
|
+
(endpoint_total / total) if (endpoint_total is not None and total) else None
|
|
313
|
+
)
|
|
314
|
+
if endpoint_total is not None:
|
|
304
315
|
used.append("endpoints")
|
|
305
316
|
else:
|
|
306
317
|
missing.append("endpoints")
|
|
@@ -519,21 +530,31 @@ class ArchetypeClassifier:
|
|
|
519
530
|
# api-first: HTTP surface must be significant relative to size. Ramp so a
|
|
520
531
|
# rounding-error surface cannot win: 0 below 0.5 endpoints/100 files, linear
|
|
521
532
|
# to full strength at >=2/100 files. (neo4j: 18/5600 = 0.32/100 -> 0.)
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
533
|
+
if f.endpoint_share is None:
|
|
534
|
+
# Unmeasured, not zero: contribute nothing and say which axis is blind.
|
|
535
|
+
add("api-first", "endpoint_density",
|
|
536
|
+
"endpoint surface not measured — this dimension is scored without it",
|
|
537
|
+
3.0, 0.0, 0.0)
|
|
538
|
+
else:
|
|
539
|
+
density = f.endpoint_share * 100
|
|
540
|
+
api_strength = max(0.0, min(1.0, (density - 0.5) / 1.5)) if f.endpoint_total else 0.0
|
|
541
|
+
add("api-first", "endpoint_density",
|
|
542
|
+
f"{f.endpoint_total} endpoints over {f.total_files} files "
|
|
543
|
+
f"({density:.2f}/100 files)",
|
|
544
|
+
3.0, api_strength, 1.0)
|
|
528
545
|
cli_entries = f.entry_kinds.get("cli", 0)
|
|
529
546
|
add("cli", "cli_entry_points", f"{cli_entries} CLI entry point(s); cli mass ≈ {cm['cli']:.0%}",
|
|
530
547
|
2.5, 1.0 if cli_entries else (1.0 if cm["cli"] > 0.05 else 0.0), max(cm["cli"], 0.2))
|
|
531
548
|
add("daemon", "server_bootstrap",
|
|
532
549
|
f"server/bootstrap entries: {f.entry_kinds.get('server',0)+f.entry_kinds.get('bootstrap',0)}",
|
|
533
550
|
2.0, 1.0 if ("server" in f.entry_kinds or "bootstrap" in f.entry_kinds) else 0.0, 0.5)
|
|
551
|
+
# `event_no_http` needs the absence of HTTP as a MEASURED fact: firing it
|
|
552
|
+
# on an unmeasured surface is how an unknown became evidence for `worker`.
|
|
553
|
+
_no_http_known = f.endpoint_total == 0
|
|
534
554
|
add("worker", "event_no_http",
|
|
535
|
-
f"event mass ≈ {cm['event']:.0%}, endpoints=
|
|
536
|
-
|
|
555
|
+
f"event mass ≈ {cm['event']:.0%}, endpoints="
|
|
556
|
+
+ (str(f.endpoint_total) if f.endpoint_total is not None else "not measured"),
|
|
557
|
+
1.5, 1.0 if (cm["event"] > 0.05 and _no_http_known) else 0.0, max(cm["event"], 0.1))
|
|
537
558
|
lib_strength = 1.0 if (f.packaging == "jar" and not f.entry_kinds) else 0.3
|
|
538
559
|
add("library", "consumed_as_dependency",
|
|
539
560
|
f"packaging={f.packaging}, entry kinds={list(f.entry_kinds) or 'none'}",
|
|
@@ -666,12 +687,18 @@ class ArchetypeClassifier:
|
|
|
666
687
|
return head if tail else ""
|
|
667
688
|
|
|
668
689
|
@staticmethod
|
|
669
|
-
def _endpoint_count(root: Optional[Path]) -> int:
|
|
690
|
+
def _endpoint_count(root: Optional[Path]) -> "Optional[int]":
|
|
691
|
+
"""The measured endpoint count, or None when it was not measured.
|
|
692
|
+
|
|
693
|
+
Both failure modes — no root to read, and an extraction that raised —
|
|
694
|
+
return None rather than 0, because every consumer below treats 0 as the
|
|
695
|
+
measured statement "this repository exposes no HTTP surface".
|
|
696
|
+
"""
|
|
670
697
|
if root is None:
|
|
671
|
-
return
|
|
698
|
+
return None
|
|
672
699
|
try:
|
|
673
700
|
from sourcecode.repository_ir import extract_java_endpoints
|
|
674
701
|
data = extract_java_endpoints(root)
|
|
675
702
|
return int(data.get("total", len(data.get("endpoints", []))))
|
|
676
703
|
except Exception:
|
|
677
|
-
return
|
|
704
|
+
return None
|
sourcecode/cache.py
CHANGED
|
@@ -354,6 +354,7 @@ def status(repo_root: Path) -> dict[str, Any]:
|
|
|
354
354
|
"cores": 0, "snapshots": 0, "views": 0, "cas_blobs": 0,
|
|
355
355
|
"total_size_bytes": 0, "total_size_mb": 0.0,
|
|
356
356
|
"current_git_head": current_head,
|
|
357
|
+
"stores": _store_breakdown(repo_root, 0),
|
|
357
358
|
**ris_fields,
|
|
358
359
|
}
|
|
359
360
|
cores = list(cache_d.glob("core-*.json.gz"))
|
|
@@ -371,10 +372,57 @@ def status(repo_root: Path) -> dict[str, Any]:
|
|
|
371
372
|
"total_size_bytes": total_bytes,
|
|
372
373
|
"total_size_mb": round(total_bytes / (1024 * 1024), 2),
|
|
373
374
|
"current_git_head": current_head,
|
|
375
|
+
# C3-36: `CAS blobs: 0`, `Total size: 0.1 MB` immediately after an 89-second
|
|
376
|
+
# warm of 3 342 files. Every figure above was true and described one store
|
|
377
|
+
# of three — the warm's output mostly lands in the shared CIR and the parse
|
|
378
|
+
# cache, which this command never counted. A status that reports a third of
|
|
379
|
+
# the state reads as a warm that did nothing.
|
|
380
|
+
"stores": _store_breakdown(repo_root, total_bytes),
|
|
374
381
|
**ris_fields,
|
|
375
382
|
}
|
|
376
383
|
|
|
377
384
|
|
|
385
|
+
def _store_breakdown(repo_root: Path, core_bytes: int) -> "dict[str, Any]":
|
|
386
|
+
"""What a warm actually populated, by store. Best-effort per store: a store
|
|
387
|
+
that cannot be inspected is reported as unavailable, never as empty."""
|
|
388
|
+
out: "dict[str, Any]" = {
|
|
389
|
+
"core": {
|
|
390
|
+
"cache_dir": str(cache_dir(repo_root)),
|
|
391
|
+
"bytes": core_bytes,
|
|
392
|
+
"scope": "this repository",
|
|
393
|
+
"holds": "core snapshots, rendered views and their CAS blobs",
|
|
394
|
+
},
|
|
395
|
+
}
|
|
396
|
+
try:
|
|
397
|
+
from sourcecode.context_cache import ContextCache # noqa: PLC0415
|
|
398
|
+
|
|
399
|
+
ctx = ContextCache.for_repo(repo_root).stats()
|
|
400
|
+
out["shared_cir"] = {
|
|
401
|
+
"cache_dir": ctx["cache_dir"],
|
|
402
|
+
"entries": ctx["contexts"],
|
|
403
|
+
"bytes": ctx["bytes_stored"],
|
|
404
|
+
"scope": "this repository",
|
|
405
|
+
"holds": "the shared Canonical IR that explain/impact/posture reuse",
|
|
406
|
+
}
|
|
407
|
+
except Exception:
|
|
408
|
+
out["shared_cir"] = {"available": False}
|
|
409
|
+
try:
|
|
410
|
+
from sourcecode import parse_cache as _pc # noqa: PLC0415
|
|
411
|
+
|
|
412
|
+
out["parse"] = {
|
|
413
|
+
**_pc.store_stats(),
|
|
414
|
+
"holds": "per-file parses, content-addressed across every repository",
|
|
415
|
+
}
|
|
416
|
+
except Exception:
|
|
417
|
+
out["parse"] = {"available": False}
|
|
418
|
+
out["note"] = (
|
|
419
|
+
"`cache warm` populates all of these; the core store alone is a third of "
|
|
420
|
+
"the answer, and reading it as the whole is how a completed warm looks like "
|
|
421
|
+
"an empty cache."
|
|
422
|
+
)
|
|
423
|
+
return out
|
|
424
|
+
|
|
425
|
+
|
|
378
426
|
def clear(repo_root: Path, *, clear_ris: bool = False) -> int:
|
|
379
427
|
"""Delete cache files for *repo_root*. Returns the number of files removed.
|
|
380
428
|
|
sourcecode/cache_model.py
CHANGED
|
@@ -124,16 +124,25 @@ COMMANDS: tuple[CommandCache, ...] = (
|
|
|
124
124
|
"`--compact` is what a warm stores by default; `--agent` needs `cache warm --agent`. "
|
|
125
125
|
"`--env-map`, `--depth N` and `--exclude` change the *analysis*, so they miss the "
|
|
126
126
|
"warmed core and rescan — this is the 171 s the field measured after a 103 s warm.",
|
|
127
|
-
"--compact
|
|
127
|
+
"--compact 13.3 s cold → 0.3 s warm (cold re-measured on 3.7.0: was 19.3 s, C3-6); "
|
|
128
|
+
"--agent --full --env-map --depth 20 34.7 s → 33.9 s (no gain)"),
|
|
128
129
|
CommandCache("posture", ("cir", "parse"), "shared", False,
|
|
129
130
|
"Resolves the conditional bean graph on every run, over the shared CIR a warm "
|
|
130
131
|
"builds — the parse it used to repeat for itself. `--diff` compares two profile "
|
|
131
132
|
"sets over that one IR, so the second side costs the resolution only.",
|
|
132
133
|
"10.1 s → 1.6 s"),
|
|
133
|
-
CommandCache("
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
134
|
+
CommandCache("risk", ("cir", "parse"), "shared", False,
|
|
135
|
+
"Composes what the audit, impact-chain and the posture already answer, so it "
|
|
136
|
+
"pays each of their costs once over the shared CIR a warm builds — one parse "
|
|
137
|
+
"for the whole composition, and the reachability query is cached per symbol "
|
|
138
|
+
"within the run.",
|
|
139
|
+
"not measured on the battery yet — the composition is bounded by the "
|
|
140
|
+
"`spring-audit` + `impact-chain` costs listed here, not by new analysis"),
|
|
141
|
+
CommandCache("endpoints", ("ris", "parse"), "shared", False,
|
|
142
|
+
"Recomputes the endpoint surface on every run, over a parse a warm has already "
|
|
143
|
+
"paid for. Until 3.7.0 the extractor parsed every file itself instead of reading "
|
|
144
|
+
"the shared parse cache, and a warm measurably bought it nothing (C3-6).",
|
|
145
|
+
"3.3 s → 1.4 s (re-measured on 3.7.0; was 2.8 s → 2.9 s)"),
|
|
137
146
|
CommandCache("spring-audit", ("ris", "parse"), "shared", False,
|
|
138
147
|
"Recomputes every run, but over a parse a warm has already paid for.",
|
|
139
148
|
"8.8 s → 3.7 s"),
|
|
@@ -158,8 +167,8 @@ COMMANDS: tuple[CommandCache, ...] = (
|
|
|
158
167
|
"1.1 s → 2.3 s (slower)"),
|
|
159
168
|
CommandCache("plan", ("parse",), "shared", False, "", "9.3 s → 3.8 s"),
|
|
160
169
|
CommandCache("compare", ("parse",), "shared", False, ""),
|
|
161
|
-
CommandCache("delta", (), "none", False, "Analyses two checkouts; neither is the tree the cache describes."),
|
|
162
|
-
CommandCache("contract-diff", (), "none", False, "Analyses two checkouts."),
|
|
170
|
+
CommandCache("delta", (), "none", False, "Analyses two states — two checkouts, or two refs materialised into temporary trees; neither is the tree the cache describes."),
|
|
171
|
+
CommandCache("contract-diff", (), "none", False, "Analyses two states (checkouts or refs); neither is the tree the cache describes."),
|
|
163
172
|
CommandCache("fix-bug", ("task",), "none", True,
|
|
164
173
|
"Shorthand for `prepare-context fix-bug`; caches its own answer, which a warm never runs."),
|
|
165
174
|
CommandCache("rename-class", (), "none", False, ""),
|
|
@@ -183,6 +192,7 @@ COMMANDS: tuple[CommandCache, ...] = (
|
|
|
183
192
|
"Reads the RIS a warm rebuilds — that is all it does. Without one it answers "
|
|
184
193
|
"`no_ris` instead of a snapshot.",
|
|
185
194
|
"0.2 s either way"),
|
|
195
|
+
CommandCache("trend", (), "none", False, "Reads stored baseline artifacts from disk; analyses no source, so no cache layer applies. Same command as `baseline trend`."),
|
|
186
196
|
CommandCache("baseline", ("parse",), "shared", False, "`capture`/`diff`/`trend` over architectural metrics.",
|
|
187
197
|
"capture 8.7 s → 3.6 s"),
|
|
188
198
|
CommandCache("retrieve", ("cir", "parse"), "shared", False,
|