sourcecode 2.0.0__py3-none-any.whl → 2.1.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/cli.py +28 -2
- sourcecode/migrate_check.py +64 -0
- sourcecode/security_posture.py +608 -0
- sourcecode/spring_findings.py +4 -0
- sourcecode/spring_security_audit.py +31 -0
- sourcecode/summarizer.py +33 -2
- sourcecode/validation_inference.py +249 -0
- sourcecode-2.1.0.dist-info/METADATA +241 -0
- {sourcecode-2.0.0.dist-info → sourcecode-2.1.0.dist-info}/RECORD +13 -11
- sourcecode-2.0.0.dist-info/METADATA +0 -925
- {sourcecode-2.0.0.dist-info → sourcecode-2.1.0.dist-info}/WHEEL +0 -0
- {sourcecode-2.0.0.dist-info → sourcecode-2.1.0.dist-info}/entry_points.txt +0 -0
- {sourcecode-2.0.0.dist-info → sourcecode-2.1.0.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cli.py
CHANGED
|
@@ -4496,7 +4496,18 @@ def validation_cmd(
|
|
|
4496
4496
|
from sourcecode.context_graph import ContextGraph
|
|
4497
4497
|
from sourcecode.validation_surface import build_validation_surface
|
|
4498
4498
|
# Structural facts come from the ContextGraph — the single access layer.
|
|
4499
|
-
|
|
4499
|
+
_graph = ContextGraph.build_from_root(target)
|
|
4500
|
+
data = build_validation_surface(target, graph=_graph)
|
|
4501
|
+
|
|
4502
|
+
# P1-C: classify the request-body validation *pattern* so a repo with a
|
|
4503
|
+
# validation framework on the classpath but no @Valid reads as
|
|
4504
|
+
# "present-but-unused" instead of a silent sea of zeros (DESIGN §2/§5).
|
|
4505
|
+
try:
|
|
4506
|
+
from sourcecode.validation_inference import infer_validation_pattern
|
|
4507
|
+
|
|
4508
|
+
data["validation_pattern"] = infer_validation_pattern(_graph.cir).to_dict()
|
|
4509
|
+
except Exception:
|
|
4510
|
+
pass
|
|
4500
4511
|
|
|
4501
4512
|
if path_prefix:
|
|
4502
4513
|
data["endpoints"] = [
|
|
@@ -4836,6 +4847,16 @@ def migrate_check_cmd(
|
|
|
4836
4847
|
help="Minimum severity to include: critical, high, medium, or low (default).",
|
|
4837
4848
|
show_default=True,
|
|
4838
4849
|
),
|
|
4850
|
+
compact: bool = typer.Option(
|
|
4851
|
+
False,
|
|
4852
|
+
"--compact",
|
|
4853
|
+
help=(
|
|
4854
|
+
"Bounded decision summary (JSON): readiness scores, headline blocker, "
|
|
4855
|
+
"effort, executive summary, Hibernate classification and aggregate "
|
|
4856
|
+
"counts, with large collections capped to their top items (+N more). "
|
|
4857
|
+
"No new analysis — same report, projected. Full detail is the default."
|
|
4858
|
+
),
|
|
4859
|
+
),
|
|
4839
4860
|
no_cache: bool = typer.Option(
|
|
4840
4861
|
False, "--no-cache",
|
|
4841
4862
|
help="Accepted for compatibility; this command always reads fresh source (no snapshot cache). No-op.",
|
|
@@ -4867,6 +4888,7 @@ def migrate_check_cmd(
|
|
|
4867
4888
|
\b
|
|
4868
4889
|
Examples:
|
|
4869
4890
|
ask migrate-check .
|
|
4891
|
+
ask migrate-check . --compact bounded decision summary
|
|
4870
4892
|
ask migrate-check /path/to/repo --format text
|
|
4871
4893
|
ask migrate-check . --min-severity high
|
|
4872
4894
|
ask migrate-check . --output migration.json
|
|
@@ -4905,7 +4927,11 @@ def migrate_check_cmd(
|
|
|
4905
4927
|
if format == "text":
|
|
4906
4928
|
output = report.to_text(min_severity=min_severity)
|
|
4907
4929
|
else:
|
|
4908
|
-
|
|
4930
|
+
# --compact projects the SAME report into a bounded, decision-grade view
|
|
4931
|
+
# (output-shaping only). Default (full) output is unchanged — it is the
|
|
4932
|
+
# baseline-oracle form and must stay byte-identical.
|
|
4933
|
+
payload = report.to_compact_dict() if compact else report.to_dict()
|
|
4934
|
+
output = _serialize_dict(payload, "json")
|
|
4909
4935
|
|
|
4910
4936
|
_total = report.summary.get("total_findings", 0)
|
|
4911
4937
|
_emit_command_output(
|
sourcecode/migrate_check.py
CHANGED
|
@@ -1308,6 +1308,33 @@ def _dimension_score(
|
|
|
1308
1308
|
return max(0, 100 - deduction)
|
|
1309
1309
|
|
|
1310
1310
|
|
|
1311
|
+
# --- Compact projection (output-shaping only) --------------------------------
|
|
1312
|
+
# `--compact` renders a bounded, decision-grade view of the SAME report: it caps
|
|
1313
|
+
# each large leaf collection to its top-N items and reports total/omitted counts.
|
|
1314
|
+
# It performs NO new analysis — it is a pure projection over the dict `to_dict()`
|
|
1315
|
+
# already produces (Semantic IR / ContextGraph / semantic engines untouched).
|
|
1316
|
+
# The default (full) output is unchanged and remains the baseline-oracle form.
|
|
1317
|
+
# FUTURE (not this increment): invert the default — emit compact by default and
|
|
1318
|
+
# add `--full` for the complete report — once the oracle baseline is recaptured.
|
|
1319
|
+
_COMPACT_TOP_N = 5
|
|
1320
|
+
|
|
1321
|
+
|
|
1322
|
+
def _cap_collection(items: list, top_n: int, key=None) -> dict:
|
|
1323
|
+
"""Project a list into a bounded `{total, shown, omitted, items}` view. Items
|
|
1324
|
+
are optionally reordered by `key` (stable) so the retained slice is the most
|
|
1325
|
+
important; ordering falls back to the engine's own emission order. No values
|
|
1326
|
+
are recomputed — only selected and counted."""
|
|
1327
|
+
total = len(items)
|
|
1328
|
+
ordered = sorted(items, key=key) if key is not None else items
|
|
1329
|
+
shown = ordered[:top_n]
|
|
1330
|
+
return {
|
|
1331
|
+
"total": total,
|
|
1332
|
+
"shown": len(shown),
|
|
1333
|
+
"omitted": max(0, total - len(shown)),
|
|
1334
|
+
"items": shown,
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
|
|
1311
1338
|
@dataclass
|
|
1312
1339
|
class MigrationReport:
|
|
1313
1340
|
schema_version: str = "1.4"
|
|
@@ -1679,6 +1706,43 @@ class MigrationReport:
|
|
|
1679
1706
|
"metadata": self.metadata,
|
|
1680
1707
|
}
|
|
1681
1708
|
|
|
1709
|
+
def to_compact_dict(self, top_n: int = _COMPACT_TOP_N) -> dict:
|
|
1710
|
+
"""Bounded, decision-grade projection of `to_dict()`.
|
|
1711
|
+
|
|
1712
|
+
Keeps every decision field verbatim (readiness_score, per-dimension
|
|
1713
|
+
readiness, headline_blocker, blocking_count, estimated_effort_days,
|
|
1714
|
+
effort_breakdown, summary, Hibernate classification, aggregate counts) and
|
|
1715
|
+
caps only the large leaf collections — top-level `findings` and the
|
|
1716
|
+
Hibernate `rewrite_targets` / `findings` / `critical_call_chains` /
|
|
1717
|
+
`golden_sql_hotspots` — to their top-`top_n` items, each wrapped as
|
|
1718
|
+
`{total, shown, omitted, items}`. It reuses the already-computed
|
|
1719
|
+
`to_dict()` output and recomputes nothing (output-shaping only)."""
|
|
1720
|
+
full = self.to_dict()
|
|
1721
|
+
compact = dict(full)
|
|
1722
|
+
compact["mode"] = "compact"
|
|
1723
|
+
compact["compact_note"] = (
|
|
1724
|
+
f"Bounded decision summary: large collections are capped to the top {top_n} "
|
|
1725
|
+
"items (see each collection's total/omitted). Run without --compact (JSON "
|
|
1726
|
+
"default) for the complete report. A future release will invert this — "
|
|
1727
|
+
"compact by default with --full for the full detail."
|
|
1728
|
+
)
|
|
1729
|
+
# Top-level findings: retain the most severe first, then engine order.
|
|
1730
|
+
compact["findings"] = _cap_collection(
|
|
1731
|
+
full["findings"], top_n,
|
|
1732
|
+
key=lambda f: SEVERITY_ORDER.get(f.get("severity"), 3),
|
|
1733
|
+
)
|
|
1734
|
+
hib = full.get("hibernate")
|
|
1735
|
+
if isinstance(hib, dict):
|
|
1736
|
+
h = dict(hib)
|
|
1737
|
+
for _col in (
|
|
1738
|
+
"rewrite_targets", "findings",
|
|
1739
|
+
"critical_call_chains", "golden_sql_hotspots",
|
|
1740
|
+
):
|
|
1741
|
+
if isinstance(h.get(_col), list):
|
|
1742
|
+
h[_col] = _cap_collection(h[_col], top_n)
|
|
1743
|
+
compact["hibernate"] = h
|
|
1744
|
+
return compact
|
|
1745
|
+
|
|
1682
1746
|
def to_text(self, min_severity: str = "low") -> str:
|
|
1683
1747
|
min_order = SEVERITY_ORDER.get(min_severity, 3)
|
|
1684
1748
|
visible = [f for f in self.findings if SEVERITY_ORDER.get(f.severity, 3) <= min_order]
|