sourcecode 5.9.2__py3-none-any.whl → 5.9.4__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/_build_commit.py +1 -1
- sourcecode/_docs/USER_GUIDE.md +24 -3
- sourcecode/architectural_baseline.py +5 -1
- sourcecode/cli.py +84 -11
- sourcecode/cve_feed.py +6 -1
- sourcecode/license.py +28 -1
- {sourcecode-5.9.2.dist-info → sourcecode-5.9.4.dist-info}/METADATA +18 -4
- {sourcecode-5.9.2.dist-info → sourcecode-5.9.4.dist-info}/RECORD +13 -13
- {sourcecode-5.9.2.dist-info → sourcecode-5.9.4.dist-info}/WHEEL +0 -0
- {sourcecode-5.9.2.dist-info → sourcecode-5.9.4.dist-info}/entry_points.txt +0 -0
- {sourcecode-5.9.2.dist-info → sourcecode-5.9.4.dist-info}/licenses/LICENSE +0 -0
- {sourcecode-5.9.2.dist-info → sourcecode-5.9.4.dist-info}/licenses/NOTICE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/_build_commit.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""Generated at build time by hatch_build.py. Do not edit (AUD-592-D04)."""
|
|
2
2
|
|
|
3
|
-
BUILD_COMMIT = '
|
|
3
|
+
BUILD_COMMIT = 'ec6b285e69ecb8d5dc90f82b106af14c221d1856'
|
|
4
4
|
CLOSURE_PROVENANCE = {'Window': {'commit': '', 'status': 'uncited'}, '`AUD-588-F05`': {'commit': '470e4a4', 'status': 'present'}, '`AUD-590-B01` / `B-01`, `F-01`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B02` / `B-02`, `F-02`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B03` / `B-03`, `F-03`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B04` / `B-04`, `F-04`': {'commit': '', 'status': 'uncited'}, '`AUD-590-R01` / `R-01`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A01` / `A-1`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A02` / `A-2`, `A-4`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A03` / `A-3`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A05` / `A-5`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A06` / `A-6`, `A-7`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A08` / `A-8`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A09` / `A-9`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A10` / `A-10`': {'commit': '', 'status': 'uncited'}, '`AUD-592-A02` / `A-1` second half, `§12.5`': {'commit': '63f3d61', 'status': 'present'}, '`AUD-592-B01` / `B-8`': {'commit': '8329614', 'status': 'present'}, '`AUD-592-D01` / `D-1`': {'commit': '0f8e03f', 'status': 'present'}, '`AUD-592-D02` / `D-2`': {'commit': '1d3db4d', 'status': 'present'}, '`AUD-592-D03` / `D-3`': {'commit': 'f28fe40', 'status': 'present'}, '`AUD-592-R01` / `A-1`': {'commit': '216025f', 'status': 'present'}, '`AUD-592-R02` / `N-4`, reopens `AUD-513-N04`': {'commit': 'ec3050d', 'status': 'present'}, '`AUD-593-N01` / `N-01`': {'commit': 'dd6a706', 'status': 'present'}, '`AUD-593-N02` / `N-02`': {'commit': 'f539787', 'status': 'present'}, '`AUD-593-N03` / `N-03`': {'commit': '1dd0b1c', 'status': 'present'}, '`AUD-593-N05` / `N-05`': {'commit': 'aa89388', 'status': 'present'}, '`AUD-593-N06` / `N-06`': {'commit': 'aa584a6', 'status': 'present'}, '`AUD-594-N04` / `D-5`, disclosure half of `B-3`': {'commit': 'a0060b7', 'status': 'present'}, '`AUD-594-X01` / `A-1`, residual of `AUD-593-N03`': {'commit': 'dd222b9', 'status': 'present'}, '`AUD-594-X02` / `A-1` second half': {'commit': '1193937', 'status': 'present'}, '`AUD-594-X03` / `X-03`, residual of `AUD-593-N01`': {'commit': 'e89e8f0', 'status': 'present'}, '`AUD-594-X04`': {'commit': '05b8cc9', 'status': 'present'}, '`AUD-595-A02` / `N-5`, advisory half of `AUD-588-B11`': {'commit': '3e15227', 'status': 'present'}, '`AUD-595-A03` / `B-6` exit-code half': {'commit': 'ab7285f', 'status': 'present'}, '`AUD-595-A05`': {'commit': 'e0b3d10', 'status': 'present'}, '`AUD-595-A07`, narrow half of `AUD-592-A02`': {'commit': 'cc15177', 'status': 'present'}, '`AUD-595-B01` / §B': {'commit': 'b77a1ad', 'status': 'present'}, '`AUD-595-Q01`': {'commit': '91a6566', 'status': 'present'}, '`AUD-596-A01` / `ASK-DET-001`': {'commit': 'fa508b4', 'status': 'present'}, '`AUD-596-A02` / `ASK-SELF-001`': {'commit': '4dfadc8', 'status': 'present'}, '`AUD-596-A03` / `ASK-C1-001`': {'commit': 'bffa3a6', 'status': 'present'}, '`AUD-596-A04` / `ASK-UX-003` + `ASK-UX-004`': {'commit': 'f0aac3c', 'status': 'present'}, '`AUD-596-B01` / `B-01`, harder witness for `BUG-6`': {'commit': '567d09d', 'status': 'present'}, '`AUD-596-B02` / `B-02`, class residual of `R2`': {'commit': '', 'status': 'uncited'}, '`AUD-596-B03` / `B-03`': {'commit': 'a8cb814', 'status': 'present'}, '`AUD-596-B06` / `B-06`': {'commit': '8107927', 'status': 'present'}, '`AUD-596-B07` / `B-07`, class residual of `AUD-592-R02`': {'commit': 'e060485', 'status': 'present'}, '`AUD-596-B09` / `B-09`': {'commit': '5b8146c', 'status': 'present'}, '`AUD-596-B10` / `B-10`': {'commit': '5cb994d', 'status': 'present'}, '`AUD-596-B16` / `B-16`': {'commit': '1507f69', 'status': 'present'}, '`AUD-596-D01` / `B-13` + `B-14`': {'commit': '0b1bdf0', 'status': 'present'}, '`AUD-596-D02` / `B-08`, residual of `AUD-594-N04`': {'commit': 'e7f73c3', 'status': 'present'}, '`AUD-596-D03` / `ASK-AGT-001`': {'commit': '8c46967', 'status': 'present'}, '`AUD-596-D04` / `B-18`': {'commit': '040f027', 'status': 'present'}, '`AUD-596-D05` / `B-19`': {'commit': 'fd2bf68', 'status': 'present'}, '`AUD-596-D06` / `B-20`': {'commit': '784cd83', 'status': 'present'}, '`AUD-596-D07` / `B-17`, half refuted': {'commit': 'c3ff822', 'status': 'present'}, '`AUD-596-D08` / `B-22`, class residual of `AUD-591-A06`': {'commit': 'b396c6a', 'status': 'present'}, '`AUD-596-D09` / `B-23`, surviving half of `B26`': {'commit': 'fd3d63f', 'status': 'present'}, '`AUD-596-D10` / `B-29`': {'commit': '', 'status': 'uncited'}, '`AUD-596-D11` / `ASK-UX-005`': {'commit': 'f4b4f77', 'status': 'present'}, '`AUD-596-D12` — six hints that send the reader back to the error': {'commit': '498df44', 'status': 'present'}, '`AUD-596-D13` / `B-28`': {'commit': '1014c72', 'status': 'present'}, '`AUD-596-D14` / `B-15`, narrow half of `B20`': {'commit': '10f2827', 'status': 'present'}, '`AUD-596-R01` / `B-05`': {'commit': '', 'status': 'uncited'}, '`AUD-596-X01` / `ASK-GATE-001`, gating half of `B24`': {'commit': 'e59c655', 'status': 'present'}, '`AUD-596-X02` / `ASK-UX-001` + `ASK-UX-002` + `B-21`': {'commit': 'f6ebf50', 'status': 'present'}, '`AUD-597-A01` / `ASK-UX-001` + `N-01`': {'commit': 'f8cf061', 'status': 'present'}, '`AUD-597-A02` / `ASK-UX-008` + `ASK-DOC-002`': {'commit': '9d47a4c', 'status': 'present'}, '`AUD-597-A03` / `ASK-DET-001`': {'commit': '4c7622a', 'status': 'present'}, '`AUD-597-A04` / `ASK-LEDGER-001`': {'commit': '46fb870', 'status': 'present'}, '`AUD-597-D01` / `B-04`': {'commit': '1094e6a', 'status': 'present'}, '`AUD-597-D02` / `ASK-DOC-001`': {'commit': 'ecb1bb7', 'status': 'present'}, '`AUD-597-D03` / `N-06`': {'commit': '9784333', 'status': 'present'}, '`AUD-597-D04` / `N-05`': {'commit': 'e51c087', 'status': 'present'}, '`AUD-597-D05` / `N-04`, fourth appearance of `C4-27`': {'commit': '9cccc15', 'status': 'present'}, '`AUD-597-D06` / `ASK-CLI-001`': {'commit': '178aa6d', 'status': 'present'}, '`AUD-597-D07` / `N-03`': {'commit': '9245e35', 'status': 'present'}, '`AUD-597-R01` / `ASK-PERF-001`': {'commit': '6910054', 'status': 'present'}, '`AUD-597-R02` / `ASK-ENC-001`': {'commit': 'ce6dac1', 'status': 'present'}, '`AUD-597-R03` / `ASK-UX-002`': {'commit': 'c57e50b', 'status': 'present'}, '`AUD-597-X01` / `ASK-BUILD-001` + `ASK-META-001` / `B-12`': {'commit': '354338b', 'status': 'present'}, '`AUD-598-B01` / `ASK-META-001`': {'commit': 'b2f7c65', 'status': 'present'}, '`AUD-598-B02` / `ASK-UX-002`': {'commit': '56a4bf7', 'status': 'present'}, '`AUD-598-B03` / `B-11`': {'commit': 'c63cb24', 'status': 'present'}, '`AUD-598-B04` / `N-02`': {'commit': '187937f', 'status': 'present'}, '`AUD-598-R01` / `ASK-PERF-002`': {'commit': '', 'status': 'uncited'}, '`AUD-598-X01` / `N-01` + `B-13` + `B-14` + `B-29` + `N-06`': {'commit': '6f2f5a4', 'status': 'present'}, '`AUD-600-B01` / `P-01`': {'commit': '366ffed', 'status': 'present'}, '`AUD-600-B02` / `P-02` + `ASK-PACK-002`': {'commit': '2a814c3', 'status': 'present'}, '`AUD-600-B03` / `ASK-PACK-001`': {'commit': '452f2a0', 'status': 'present'}, '`AUD-600-D01`': {'commit': '1224e1b', 'status': 'present'}, '`AUD-600-D02`': {'commit': 'eab8172', 'status': 'present'}, '`AUD-600-R01` / `R-01`': {'commit': '88add64', 'status': 'present'}, '`AUD-601-B01` / `N-02`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-B02` / `ASK-PACK-004`': {'commit': '', 'status': 'uncited'}, '`AUD-601-D01` / `N-01`, `ASK-UX-001`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-D02` / `B-13`, `B-14`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-M01` / `B-26`, `B-27` retracted': {'commit': '', 'status': 'uncited'}, '`AUD-601-R01` / `R-02`': {'commit': '', 'status': 'uncited'}, '`B-3`': {'commit': '773268d', 'status': 'present'}, '`B-6`': {'commit': '17c6e06', 'status': 'present'}, '`B-8`': {'commit': '8329614', 'status': 'present'}, '`BUG-3` / `443b345`': {'commit': '', 'status': 'uncited'}, '`BUG-4a` / `AUD-588-B11` residual': {'commit': '24b4185', 'status': 'present'}, '`BUG-4b` / `AUD-588-B11` residual': {'commit': '0376350', 'status': 'present'}, '`E-39`, class residual of `AUD-593-N06`': {'commit': '25fc11f', 'status': 'present'}, '`N-5`': {'commit': 'c68083f', 'status': 'present'}, '`N-7`': {'commit': '082e9de', 'status': 'present'}, '`R-1`': {'commit': '424fb1c', 'status': 'present'}, '`R-2`': {'commit': '5158b92', 'status': 'present'}, '`R-3`': {'commit': '88aec04', 'status': 'present'}, '`R-4`': {'commit': '04fd04f', 'status': 'present'}}
|
|
5
5
|
CLOSURE_COVERAGE = {'closed_rows': 113, 'present': 88, 'absent': 0, 'unresolved': 0, 'uncited': 25, 'unknown': 0}
|
sourcecode/_docs/USER_GUIDE.md
CHANGED
|
@@ -46,15 +46,30 @@ CLI commands — impact, endpoints, spring-audit, explain, … each a pro
|
|
|
46
46
|
The key idea: the extraction is **content-addressed**. Commands reuse the parse cache and,
|
|
47
47
|
where their analysed scope matches, the shared Canonical IR; `ask cache model` names what a
|
|
48
48
|
warm buys for each command rather than implying that every projection costs the same. In
|
|
49
|
-
5.9.
|
|
49
|
+
5.9.4, `validation` enters through that shared CIR and `data-exposure` reuses one semantic
|
|
50
50
|
model across all declared label seeds. (The extraction and consumption contract is fixed in
|
|
51
51
|
the architecture ADRs 0001–0004 under `docs/architecture/`.)
|
|
52
52
|
|
|
53
53
|
The current external pack audit intake is tracked in
|
|
54
54
|
[`AUDIT-2026-08-26-EXTERNAL-5.8.28-PACK.md`](AUDIT-2026-08-26-EXTERNAL-5.8.28-PACK.md);
|
|
55
|
-
the two pack fixes are included in release 5.9.
|
|
55
|
+
the two pack fixes are included in release 5.9.4, with release-artifact verification
|
|
56
56
|
remaining for detached execution and pack evidence.
|
|
57
57
|
|
|
58
|
+
### Reliability contracts in 5.9.4
|
|
59
|
+
|
|
60
|
+
Invalid analysis inputs fail before expensive work and use the structured
|
|
61
|
+
`INVALID_INPUT` envelope: a missing `--consumer` repository, a malformed CVE
|
|
62
|
+
feed, or an unresolved `pack gate --since` ref is never treated as an empty or
|
|
63
|
+
`UNVERIFIED` result. `posture --diff` reports unknown profiles when the project
|
|
64
|
+
declares profiles. `baseline capture` does not replace a snapshot already stored
|
|
65
|
+
for the same commit; use `--auto` when a pipeline should skip it.
|
|
66
|
+
|
|
67
|
+
With `endpoints --compact`, `endpoints: []` means the rows were deliberately
|
|
68
|
+
omitted. Read `endpoints_note`, `endpoints_omitted` and `total`; an empty list is
|
|
69
|
+
not evidence that the API has no routes. `impact` and `impact-chain` return exit
|
|
70
|
+
code 1 when the target is unresolved, including when the selected repository
|
|
71
|
+
contains no Java sources.
|
|
72
|
+
|
|
58
73
|
---
|
|
59
74
|
|
|
60
75
|
## The semantic engine
|
|
@@ -221,7 +236,7 @@ pipx install sourcecode # isolated install, no venv needed
|
|
|
221
236
|
|
|
222
237
|
# Verify
|
|
223
238
|
ask version
|
|
224
|
-
# ask 5.9.
|
|
239
|
+
# ask 5.9.4
|
|
225
240
|
```
|
|
226
241
|
|
|
227
242
|
Requires Python 3.9+.
|
|
@@ -1180,6 +1195,8 @@ the stored architecture or findings series (`--kind findings`) and publishes its
|
|
|
1180
1195
|
boundaries, latest commit, comparability and explicit limits as `baseline-attestation-v1`.
|
|
1181
1196
|
It is evidence continuity, not a legal or regulatory compliance verdict, and it returns
|
|
1182
1197
|
`status: no_history` instead of manufacturing a clean zero when no capture exists.
|
|
1198
|
+
Unlike `audit-report --sign-key`, `baseline attest` is currently unsigned: its
|
|
1199
|
+
`content_id` supports deterministic comparison, not third-party tamper verification.
|
|
1183
1200
|
|
|
1184
1201
|
For a multi-repository buyer, `ask auth workspace <repo>...` previews the aggregate Java
|
|
1185
1202
|
population that a workspace-sized entitlement would see. The preview is advisory only: it
|
|
@@ -1187,6 +1204,10 @@ does not change the current per-repository gate or charge the workspace.
|
|
|
1187
1204
|
For a named-buyer pilot, add `--enforce`: crossing the aggregate threshold then requires
|
|
1188
1205
|
the existing Pro entitlement and exits with the standard license-required contract; below
|
|
1189
1206
|
the threshold it passes. This flag is opt-in and does not change per-repository gates.
|
|
1207
|
+
Repository arguments are normalized before counting. If a parent and descendant are both
|
|
1208
|
+
supplied, the parent is counted once and the overlap is listed in `overlapping_paths`.
|
|
1209
|
+
Missing or non-directory paths fail with the standard structured `INVALID_INPUT` / `missing`
|
|
1210
|
+
contract; they are never treated as empty repositories.
|
|
1190
1211
|
|
|
1191
1212
|
---
|
|
1192
1213
|
|
|
@@ -445,11 +445,15 @@ def _announce_ask_dir(ask_dir: Path) -> None:
|
|
|
445
445
|
|
|
446
446
|
|
|
447
447
|
def write_baseline(baseline: dict, out_dir: Path) -> Path:
|
|
448
|
-
"""Write
|
|
448
|
+
"""Write a new baseline without replacing an existing commit snapshot."""
|
|
449
449
|
_readonly.guard(out_dir, what="baseline history directory")
|
|
450
450
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
451
451
|
protect_ask_dir(out_dir)
|
|
452
452
|
path = out_dir / _baseline_filename(baseline)
|
|
453
|
+
if path.exists():
|
|
454
|
+
raise FileExistsError(
|
|
455
|
+
f"Baseline already exists for commit {baseline.get('commit') or '-'}: {path}"
|
|
456
|
+
)
|
|
453
457
|
path.write_text(
|
|
454
458
|
json.dumps(baseline, sort_keys=True, indent=2) + "\n", encoding="utf-8"
|
|
455
459
|
)
|
sourcecode/cli.py
CHANGED
|
@@ -4552,6 +4552,25 @@ def pack_gate_cmd(
|
|
|
4552
4552
|
)
|
|
4553
4553
|
raise typer.Exit(code=2)
|
|
4554
4554
|
comparison_ref = since
|
|
4555
|
+
if since is not None:
|
|
4556
|
+
from sourcecode.git_checkout import RefError, repo_root, resolve_ref
|
|
4557
|
+
|
|
4558
|
+
try:
|
|
4559
|
+
resolve_ref(repo_root(root), since)
|
|
4560
|
+
except RefError as exc:
|
|
4561
|
+
# Test doubles and callers that provide a non-Git analysis root are
|
|
4562
|
+
# still handled by the existing pack component contracts. Only a
|
|
4563
|
+
# ref failure in an actual repository is an invalid comparison.
|
|
4564
|
+
if exc.code == "not_a_git_repository":
|
|
4565
|
+
pass
|
|
4566
|
+
else:
|
|
4567
|
+
_emit_error_json(
|
|
4568
|
+
INVALID_INPUT_CODE,
|
|
4569
|
+
str(exc),
|
|
4570
|
+
ref=exc.ref or since,
|
|
4571
|
+
hint="Use a commit, tag, or local branch that exists in the repository.",
|
|
4572
|
+
)
|
|
4573
|
+
raise typer.Exit(code=2)
|
|
4555
4574
|
if pr is not None:
|
|
4556
4575
|
try:
|
|
4557
4576
|
from sourcecode.github_diff import pull_request_base_ref
|
|
@@ -8578,6 +8597,9 @@ def impact_cmd(
|
|
|
8578
8597
|
not_found — no symbol and no substring. `risk_score` is null, never 0:
|
|
8579
8598
|
nothing was measured, which is not measuring zero.
|
|
8580
8599
|
|
|
8600
|
+
An unresolved target exits with code 1, including when the selected repository
|
|
8601
|
+
contains no Java sources; the structured answer still names `not_found`.
|
|
8602
|
+
|
|
8581
8603
|
\b
|
|
8582
8604
|
NOTE: Free on repos up to the size limit; Pro unlocks enterprise-scale
|
|
8583
8605
|
monoliths. Run 'ask license' for details.
|
|
@@ -8652,7 +8674,7 @@ def impact_cmd(
|
|
|
8652
8674
|
format,
|
|
8653
8675
|
)
|
|
8654
8676
|
_emit_command_output(_nf_output, output_path, copy)
|
|
8655
|
-
|
|
8677
|
+
_exit_on_unresolved_target("not_found")
|
|
8656
8678
|
|
|
8657
8679
|
_prog = Progress()
|
|
8658
8680
|
_prog.start(f"building CIR ({len(file_list)} files) for impact analysis")
|
|
@@ -8902,7 +8924,8 @@ def endpoints_cmd(
|
|
|
8902
8924
|
False, "--compact",
|
|
8903
8925
|
help="Answer how exposed the repository is without listing every endpoint: "
|
|
8904
8926
|
"counts by method, by security policy and by controller. Every count is "
|
|
8905
|
-
"measured over the full population
|
|
8927
|
+
"measured over the full population; an empty `endpoints` list means the "
|
|
8928
|
+
"rows were intentionally omitted, not that no endpoints exist.",
|
|
8906
8929
|
),
|
|
8907
8930
|
no_cache: bool = typer.Option(
|
|
8908
8931
|
False, "--no-cache",
|
|
@@ -8980,6 +9003,13 @@ def endpoints_cmd(
|
|
|
8980
9003
|
_enforce_format("endpoints", format)
|
|
8981
9004
|
|
|
8982
9005
|
target = _admit_path(path)
|
|
9006
|
+
if consumer:
|
|
9007
|
+
# A missing consumer must fail before the server scan. Treating it as an
|
|
9008
|
+
# empty consumer silently turns a requested join into an unfiltered one.
|
|
9009
|
+
consumer = [
|
|
9010
|
+
_admit_path(item, usage="endpoints --consumer")
|
|
9011
|
+
for item in consumer
|
|
9012
|
+
]
|
|
8983
9013
|
|
|
8984
9014
|
# AUD-591-A10: a prefix that is not a route selects nothing and reports it
|
|
8985
9015
|
# as no exposure. Rejected before the analysis, so the shell rewrite is
|
|
@@ -9116,6 +9146,12 @@ def endpoints_cmd(
|
|
|
9116
9146
|
|
|
9117
9147
|
_listed = len(data.get("endpoints", []))
|
|
9118
9148
|
data["endpoints"] = []
|
|
9149
|
+
data["endpoints_note"] = (
|
|
9150
|
+
f"The endpoints list is intentionally omitted by --compact; "
|
|
9151
|
+
f"{_listed} endpoint(s) were measured. An empty `endpoints` list "
|
|
9152
|
+
"does not mean that the repository has no endpoints. Re-run without "
|
|
9153
|
+
"--compact or narrow the request to retrieve rows."
|
|
9154
|
+
)
|
|
9119
9155
|
data["endpoints_omitted"] = {
|
|
9120
9156
|
"total": _listed,
|
|
9121
9157
|
"shown": 0,
|
|
@@ -10599,7 +10635,9 @@ def baseline_capture_cmd(
|
|
|
10599
10635
|
ref: Optional[str] = typer.Option(
|
|
10600
10636
|
None, "--ref",
|
|
10601
10637
|
help="Human label for this baseline (e.g. a version or milestone). "
|
|
10602
|
-
"The commit is recorded automatically when the path is a git repo."
|
|
10638
|
+
"The commit is recorded automatically when the path is a git repo. "
|
|
10639
|
+
"A capture never overwrites an existing snapshot for the same commit; "
|
|
10640
|
+
"use --auto to skip an already captured commit.",
|
|
10603
10641
|
),
|
|
10604
10642
|
kind: str = typer.Option(
|
|
10605
10643
|
"architecture", "--kind",
|
|
@@ -10750,7 +10788,16 @@ def baseline_capture_cmd(
|
|
|
10750
10788
|
baseline = build_baseline(
|
|
10751
10789
|
_cir, ref=ref, commit=git_commit(target_dir), record_env=record_env,
|
|
10752
10790
|
)
|
|
10753
|
-
|
|
10791
|
+
try:
|
|
10792
|
+
written = write_baseline(baseline, dest)
|
|
10793
|
+
except FileExistsError as exc:
|
|
10794
|
+
_prog.finish()
|
|
10795
|
+
_emit_error_json(
|
|
10796
|
+
INVALID_INPUT_CODE,
|
|
10797
|
+
str(exc),
|
|
10798
|
+
hint="Use `--auto` to skip an already captured commit, or capture a new commit.",
|
|
10799
|
+
)
|
|
10800
|
+
raise typer.Exit(code=1)
|
|
10754
10801
|
_prog.finish()
|
|
10755
10802
|
|
|
10756
10803
|
t = baseline["totals"]
|
|
@@ -14519,7 +14566,9 @@ def posture_cmd(
|
|
|
14519
14566
|
Two axes, and they are different questions. `--diff` holds the tree fixed
|
|
14520
14567
|
and moves the profile set. `--diff-ref` (and `--since`) hold the profile set
|
|
14521
14568
|
fixed and move the tree: *does this change what a client can reach without
|
|
14522
|
-
authenticating?* That one takes `--fail-on` and is the per-PR gate.
|
|
14569
|
+
authenticating?* That one takes `--fail-on` and is the per-PR gate. When the
|
|
14570
|
+
repository declares profiles, `--diff` rejects a profile name outside that
|
|
14571
|
+
population instead of comparing a configuration that does not exist.
|
|
14523
14572
|
|
|
14524
14573
|
\b
|
|
14525
14574
|
Examples:
|
|
@@ -14743,10 +14792,28 @@ def posture_cmd(
|
|
|
14743
14792
|
)
|
|
14744
14793
|
raise typer.Exit(code=1)
|
|
14745
14794
|
left_raw, right_raw = diff.split(":", 1)
|
|
14795
|
+
left_profiles = _parse_set(left_raw, flag="--diff (left side)")
|
|
14796
|
+
right_profiles = _parse_set(right_raw, flag="--diff (right side)")
|
|
14797
|
+
from sourcecode.spring_profiles import profile_population
|
|
14798
|
+
|
|
14799
|
+
declared_profiles = set(profile_population(path).names)
|
|
14800
|
+
missing_profiles = sorted(
|
|
14801
|
+
(left_profiles | right_profiles) - declared_profiles
|
|
14802
|
+
)
|
|
14803
|
+
if missing_profiles and declared_profiles:
|
|
14804
|
+
_emit_error_json(
|
|
14805
|
+
INVALID_INPUT_CODE,
|
|
14806
|
+
"--diff names profile(s) not declared by the repository: "
|
|
14807
|
+
+ ", ".join(missing_profiles),
|
|
14808
|
+
profiles=missing_profiles,
|
|
14809
|
+
hint="Use profiles reported by `ask posture <repo>`; use a single "
|
|
14810
|
+
"posture run to inspect an undeclared profile explicitly.",
|
|
14811
|
+
)
|
|
14812
|
+
raise typer.Exit(code=1)
|
|
14746
14813
|
data = diff_posture(
|
|
14747
14814
|
path,
|
|
14748
|
-
|
|
14749
|
-
|
|
14815
|
+
left_profiles,
|
|
14816
|
+
right_profiles,
|
|
14750
14817
|
overrides,
|
|
14751
14818
|
)
|
|
14752
14819
|
else:
|
|
@@ -15571,7 +15638,7 @@ def impact_chain_cmd(
|
|
|
15571
15638
|
output = _serialize_dict(data, format)
|
|
15572
15639
|
_emit_command_output(output, output_path, copy,
|
|
15573
15640
|
success_msg=f"Impact chain written to {output_path}")
|
|
15574
|
-
|
|
15641
|
+
_exit_on_unresolved_target("not_found")
|
|
15575
15642
|
|
|
15576
15643
|
_prog = Progress()
|
|
15577
15644
|
_prog.start(f"analyzing impact ({len(file_list)} files)")
|
|
@@ -17919,7 +17986,8 @@ def auth_workspace_cmd(
|
|
|
17919
17986
|
"--enforce",
|
|
17920
17987
|
help=(
|
|
17921
17988
|
"Pilot gate: require the existing Pro entitlement when the aggregate "
|
|
17922
|
-
"workspace threshold is exceeded. Advisory by default."
|
|
17989
|
+
"workspace threshold is exceeded. Advisory by default. Overlapping "
|
|
17990
|
+
"paths are counted once; missing paths are rejected."
|
|
17923
17991
|
),
|
|
17924
17992
|
),
|
|
17925
17993
|
) -> None:
|
|
@@ -17927,12 +17995,17 @@ def auth_workspace_cmd(
|
|
|
17927
17995
|
|
|
17928
17996
|
Advisory mode is the default. Use ``--enforce`` only for the named-buyer
|
|
17929
17997
|
pilot: it reuses the existing Pro gate when the aggregate threshold is
|
|
17930
|
-
crossed and performs no billing.
|
|
17998
|
+
crossed and performs no billing. Repository paths are normalized, parent /
|
|
17999
|
+
descendant overlaps are counted once, and missing paths are rejected.
|
|
17931
18000
|
"""
|
|
17932
18001
|
_enforce_format("auth-workspace", format)
|
|
17933
18002
|
from sourcecode.license import require_pro, workspace_entitlement
|
|
17934
18003
|
|
|
17935
|
-
|
|
18004
|
+
validated_repos = [
|
|
18005
|
+
_admit_path(repo, usage="auth workspace")
|
|
18006
|
+
for repo in repos
|
|
18007
|
+
]
|
|
18008
|
+
data = workspace_entitlement([str(repo) for repo in validated_repos], enforce=enforce)
|
|
17936
18009
|
if data["enforcement_decision"] == "pro_required":
|
|
17937
18010
|
require_pro("workspace entitlement")
|
|
17938
18011
|
_emit_command_output(
|
sourcecode/cve_feed.py
CHANGED
|
@@ -258,9 +258,14 @@ def load_feed(feed_path: Path, *, now: Optional[datetime] = None) -> Feed:
|
|
|
258
258
|
newest = stamp
|
|
259
259
|
|
|
260
260
|
if unreadable:
|
|
261
|
+
feed.error = f"{unreadable} feed file(s) were not valid JSON"
|
|
261
262
|
feed.limitations.append(
|
|
262
|
-
f"{unreadable} feed file(s) were not valid JSON
|
|
263
|
+
f"{unreadable} feed file(s) were not valid JSON; the feed was rejected"
|
|
263
264
|
)
|
|
265
|
+
# Partial advisory data is unsafe for a reachability gate: a skipped
|
|
266
|
+
# file can contain the one advisory that should have blocked the run.
|
|
267
|
+
feed.loaded = False
|
|
268
|
+
return feed
|
|
264
269
|
feed.packages = len(feed.by_package)
|
|
265
270
|
feed.loaded = True
|
|
266
271
|
|
sourcecode/license.py
CHANGED
|
@@ -550,7 +550,33 @@ def workspace_entitlement(
|
|
|
550
550
|
aggregate crosses the threshold. This function only publishes the decision;
|
|
551
551
|
it never changes the per-repository rule or performs billing.
|
|
552
552
|
"""
|
|
553
|
-
|
|
553
|
+
normalized = sorted({str(Path(path).resolve()) for path in repo_paths})
|
|
554
|
+
invalid = [path for path in normalized if not Path(path).is_dir()]
|
|
555
|
+
if invalid:
|
|
556
|
+
raise FileNotFoundError(
|
|
557
|
+
"Workspace repository path does not exist or is not a directory: "
|
|
558
|
+
+ invalid[0]
|
|
559
|
+
)
|
|
560
|
+
unique: list[str] = []
|
|
561
|
+
overlapping_paths: list[dict[str, str]] = []
|
|
562
|
+
for candidate in normalized:
|
|
563
|
+
parent = next(
|
|
564
|
+
(existing for existing in unique if Path(candidate) == Path(existing)
|
|
565
|
+
or Path(existing) in Path(candidate).parents),
|
|
566
|
+
None,
|
|
567
|
+
)
|
|
568
|
+
if parent is not None:
|
|
569
|
+
overlapping_paths.append({"parent": parent, "child": candidate})
|
|
570
|
+
continue
|
|
571
|
+
descendants = [
|
|
572
|
+
existing for existing in unique
|
|
573
|
+
if Path(candidate) in Path(existing).parents
|
|
574
|
+
]
|
|
575
|
+
for descendant in descendants:
|
|
576
|
+
unique.remove(descendant)
|
|
577
|
+
overlapping_paths.append({"parent": candidate, "child": descendant})
|
|
578
|
+
unique.append(candidate)
|
|
579
|
+
unique.sort()
|
|
554
580
|
repositories = [
|
|
555
581
|
{"path": path, "java_files": count_java_files(path)}
|
|
556
582
|
for path in unique
|
|
@@ -561,6 +587,7 @@ def workspace_entitlement(
|
|
|
561
587
|
"status": "ok" if repositories else "no_repositories",
|
|
562
588
|
"repository_count": len(repositories),
|
|
563
589
|
"repositories": repositories,
|
|
590
|
+
"overlapping_paths": overlapping_paths,
|
|
564
591
|
"workspace_java_files": total,
|
|
565
592
|
"workspace_java_file_limit": _FREE_REPO_JAVA_FILE_LIMIT,
|
|
566
593
|
"workspace_threshold_exceeded": total > _FREE_REPO_JAVA_FILE_LIMIT,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 5.9.
|
|
3
|
+
Version: 5.9.4
|
|
4
4
|
Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/HarounDominique/sourcecode
|
|
6
6
|
Project-URL: Repository, https://github.com/HarounDominique/sourcecode
|
|
@@ -59,7 +59,7 @@ Description-Content-Type: text/markdown
|
|
|
59
59
|
> question. All analysis is local and deterministic; see [Privacy](#configuration--privacy)
|
|
60
60
|
> before inspecting a repository whose telemetry policy you need to control.
|
|
61
61
|
|
|
62
|
-

|
|
63
63
|

|
|
64
64
|
|
|
65
65
|
Copyright (c) 2026 Dominique Haroun. All rights reserved. ASK Engine 5.7.0 and
|
|
@@ -196,7 +196,7 @@ set `ASK_MAX_RSS_MB=<megabytes>`; if the process exceeds it, ASK refuses the
|
|
|
196
196
|
answer with `MEMORY_TOO_LARGE`, including the observed peak and limit. Leave it
|
|
197
197
|
unset, or set it to `0`, to disable the ceiling explicitly.
|
|
198
198
|
|
|
199
|
-
**Current performance status.** Release 5.9.
|
|
199
|
+
**Current performance status.** Release 5.9.4 retains the shared Canonical IR
|
|
200
200
|
path used by `validation` and now passes that same CIR into labelled `data-exposure`
|
|
201
201
|
runs; the no-label path remains parse-free. These are implementation properties,
|
|
202
202
|
not duration promises. A controlled external warm-path audit has opened performance
|
|
@@ -212,6 +212,20 @@ aggregate Java entitlement. `--enforce` is an opt-in named-buyer pilot that reus
|
|
|
212
212
|
the existing Pro gate; advisory mode remains the default and no billing is
|
|
213
213
|
performed by the CLI.
|
|
214
214
|
|
|
215
|
+
`auth workspace` normalizes its repository arguments before counting. If one
|
|
216
|
+
path contains another, the parent is counted once and the overlap is published
|
|
217
|
+
in `overlapping_paths`; a missing or non-directory path is rejected as structured
|
|
218
|
+
invalid input rather than treated as an empty repository. This prevents silent
|
|
219
|
+
entitlement errors in CI and does not change per-repository gates or billing.
|
|
220
|
+
|
|
221
|
+
Release 5.9.4 also hardens the boundary contracts: missing consumer repositories,
|
|
222
|
+
malformed CVE feeds and unresolved `pack gate --since` refs fail as structured
|
|
223
|
+
input errors; duplicate same-commit baselines are never overwritten. `posture
|
|
224
|
+
--diff` validates unknown profiles when the repository declares profiles. In
|
|
225
|
+
`endpoints --compact`, an empty `endpoints` list is explicitly marked as an
|
|
226
|
+
intentional omission, not an empty API; unresolved `impact` and `impact-chain`
|
|
227
|
+
queries exit non-zero, including on repositories without Java sources.
|
|
228
|
+
|
|
215
229
|
**What a warm actually covers.** `ask cache warm` rebuilds the declared root views and their
|
|
216
230
|
shared structural layers (L1/L2 + the Repository Intelligence Snapshot + the shared Canonical
|
|
217
231
|
IR). Pass `--views compact` or `--views compact,agent` to warm only selected views; without
|
|
@@ -237,7 +251,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
|
|
|
237
251
|
# pip / pipx
|
|
238
252
|
pipx install sourcecode # or: pip install sourcecode
|
|
239
253
|
|
|
240
|
-
ask version # ask 5.9.
|
|
254
|
+
ask version # ask 5.9.4 — and, on a build that has aged,
|
|
241
255
|
# how many releases have probably shipped since
|
|
242
256
|
```
|
|
243
257
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=0qlYxElAIApXgtaqvLvB59Bcp23mslHDvyztYWrQZD0,308
|
|
2
2
|
sourcecode/__main__.py,sha256=qDFVkobwsdZrX0B3stiRqsUNivVpybKO-egzAwCW4xw,165
|
|
3
|
-
sourcecode/_build_commit.py,sha256=
|
|
3
|
+
sourcecode/_build_commit.py,sha256=W0ohkcA2_TncmrWEsLzLPfZNmCxpl2xc1rVqQD4-i6o,8740
|
|
4
4
|
sourcecode/access_drift.py,sha256=1R7_fiAPvKNOnxc9uxkH3JH-k8MiAuyE7FwWUX1r4Fg,8995
|
|
5
5
|
sourcecode/adaptive_scanner.py,sha256=yJBKjNpkY6bpueYJ2YnRezen3sYZDecEt7WaaNWdqug,9466
|
|
6
6
|
sourcecode/analysis_budget.py,sha256=ZuNEboPF8A6GK-_V7WNt92ezgriga7_Rbr-vvSN4UEE,4183
|
|
7
7
|
sourcecode/answer_bounds.py,sha256=_51_DCuYEUmI_xwUFxDMiprSavkNakkLiIBbPjevQ3M,9793
|
|
8
8
|
sourcecode/archetype.py,sha256=V_s5grgpbanrXb8qGEQb73W5Cg5iKW9FQHWKgg5KM8Y,41201
|
|
9
|
-
sourcecode/architectural_baseline.py,sha256=
|
|
9
|
+
sourcecode/architectural_baseline.py,sha256=9ds-ZYw4dEJUKWZSdq_ufHq7d2AXnMbEHteSQlT7iu8,26468
|
|
10
10
|
sourcecode/architectural_delta.py,sha256=JhZVmA0gYUryt4x4ieVuxPbcvoBygHgADZaYJ_o4gdk,15142
|
|
11
11
|
sourcecode/architecture_analyzer.py,sha256=GFc4ek-s1IHWM7pl-0L32WahZ93AmDgrAMcOuBKA5Dk,61463
|
|
12
12
|
sourcecode/architecture_summary.py,sha256=BVVRHd952cjRhjHnR6CPrvKgaa-tdM16l-pBi1yDCPs,32395
|
|
@@ -35,7 +35,7 @@ sourcecode/change_plan.py,sha256=drAoDsqFplutCLc6Y0xue7sYkTls0SpYh7aOXvttv3I,129
|
|
|
35
35
|
sourcecode/ci_output.py,sha256=X4NRokWWMRF9Ex8OQrEO2g04GzJ00GDP5zYIoqI9YCc,4625
|
|
36
36
|
sourcecode/cir_graphs.py,sha256=RytqSv1WF8kdlfXj2fPQz16LY4akS2kyThbdWL8beCQ,14835
|
|
37
37
|
sourcecode/classifier.py,sha256=JBzPwSSrDG-tUHAbcKB678HRbjLpD-ohzbzzO62mgpo,20114
|
|
38
|
-
sourcecode/cli.py,sha256=
|
|
38
|
+
sourcecode/cli.py,sha256=_8CaYY1ye6GNc1VEYzOa4JbTkmUiGoxfnUffIi7i1Zo,936454
|
|
39
39
|
sourcecode/client_calls.py,sha256=YOPnfRZzRyqmdHvXSVeiunW8cuiuCuWXacNIQRgKMCw,22640
|
|
40
40
|
sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
|
|
41
41
|
sourcecode/compare.py,sha256=Lnfmm9I3k0H1jP1tV6ZJ-R7znjQPLPLCe5TzV1Jb_PM,15323
|
|
@@ -52,7 +52,7 @@ sourcecode/contract_init.py,sha256=bXRCqWM9tvrDLeT8j7ea6IWf87jB4VJxnlSzGBsRRXo,1
|
|
|
52
52
|
sourcecode/contract_model.py,sha256=nRxJKPMs1VHwFTa8AVXhGmaLjti3Lr2sjHDpWgv1bfE,3917
|
|
53
53
|
sourcecode/contract_pipeline.py,sha256=4XE-xhab7ysdExXyUOBl2hAH4MABo7moKnT9V4XcEwQ,30243
|
|
54
54
|
sourcecode/coverage_parser.py,sha256=X9scuUWxacahYTrTBGea2Ku-sf-WCVcnc9OEB35_3IU,19340
|
|
55
|
-
sourcecode/cve_feed.py,sha256=
|
|
55
|
+
sourcecode/cve_feed.py,sha256=IBVpQaY04biXKEeNf1qKO_95GqEV23fwhj4oNk7s1-g,11028
|
|
56
56
|
sourcecode/cve_reachability.py,sha256=aEHkG3IaeLxgekK79cz07QTcv9LZHEqEYqyLGjwjzSE,20409
|
|
57
57
|
sourcecode/data_exposure.py,sha256=73EcuHsN3ESKdSvSj1B2FzD1myGJqzlNJpYO1NpI5WM,19963
|
|
58
58
|
sourcecode/data_labels.py,sha256=9v0JaICRH7jIZ015T1_DuT5E0MFfeL-Wcq1WOPE0Fe0,10965
|
|
@@ -96,7 +96,7 @@ sourcecode/identity_fallback.py,sha256=uRC_j_80uaI4Ue4tf-OhKX4h472dmufYcjEFGSxY2
|
|
|
96
96
|
sourcecode/integration_coordinates.py,sha256=rvuApnS7WdRUbeQLg_oKj-Gi0koO2RKdw6HenXNgrDo,12443
|
|
97
97
|
sourcecode/intent_router.py,sha256=64d5Q1TKj9h2vv_iuWbCEb0eeeG6H5wYvSX7V_7w-gw,15511
|
|
98
98
|
sourcecode/jdk_exports.py,sha256=fCrlwNAXUT9gge_joq6kMnY3zJxYB2pxqy-0w3o3MJI,874
|
|
99
|
-
sourcecode/license.py,sha256=
|
|
99
|
+
sourcecode/license.py,sha256=Rx_TXEztUJB9TzI352hEh8cmCd-30J9WS6iT4EndcW0,30974
|
|
100
100
|
sourcecode/manifest_cache.py,sha256=aPRHUs781d00fVbtlTiSaq0-AS9bAqS7_zJ0snZvlJs,2244
|
|
101
101
|
sourcecode/mcp_nudge.py,sha256=beNjfyb7z09T1rnS4t6Q4qfbLzZQNaP6ddCA4XpmV0s,3256
|
|
102
102
|
sourcecode/metrics_analyzer.py,sha256=gLoRWKygF18jLgwsqmGXSWopw-f5iO1VbM7Jjdif6ag,22809
|
|
@@ -276,10 +276,10 @@ sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec
|
|
|
276
276
|
sourcecode/_docs/DEFECT-LEDGER.md,sha256=EkYSJieDrazfADCZ8tsCsybyW9U0nAzWDQ8rS4KmGTU,1115164
|
|
277
277
|
sourcecode/_docs/LICENSE,sha256=23-fk61oSWGVmkWyka-FIM0rhAPM2hcYHnsaridL8Uo,1859
|
|
278
278
|
sourcecode/_docs/NOTICE,sha256=Br-AA8Y3iJo1WXa5uP8QBpzqI5nDu3iGLbFyvDt3BOs,321
|
|
279
|
-
sourcecode/_docs/USER_GUIDE.md,sha256=
|
|
280
|
-
sourcecode-5.9.
|
|
281
|
-
sourcecode-5.9.
|
|
282
|
-
sourcecode-5.9.
|
|
283
|
-
sourcecode-5.9.
|
|
284
|
-
sourcecode-5.9.
|
|
285
|
-
sourcecode-5.9.
|
|
279
|
+
sourcecode/_docs/USER_GUIDE.md,sha256=cE9Q2IMoCXELmhwpvZHslppGBGK2nlR0RSzksifni7U,104721
|
|
280
|
+
sourcecode-5.9.4.dist-info/METADATA,sha256=PivRgNyboaCJ75FC86-6HQ4OIIk0l5TAperOYb4uTsI,64752
|
|
281
|
+
sourcecode-5.9.4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
282
|
+
sourcecode-5.9.4.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
|
|
283
|
+
sourcecode-5.9.4.dist-info/licenses/LICENSE,sha256=23-fk61oSWGVmkWyka-FIM0rhAPM2hcYHnsaridL8Uo,1859
|
|
284
|
+
sourcecode-5.9.4.dist-info/licenses/NOTICE,sha256=Br-AA8Y3iJo1WXa5uP8QBpzqI5nDu3iGLbFyvDt3BOs,321
|
|
285
|
+
sourcecode-5.9.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|