sourcecode 5.8.8__py3-none-any.whl → 5.8.10__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/_docs/DEFECT-LEDGER.md +75 -2
- sourcecode/_docs/USER_GUIDE.md +1 -1
- sourcecode/change_plan.py +3 -0
- sourcecode/cli.py +28 -17
- sourcecode/compare.py +1 -0
- sourcecode/data_exposure.py +12 -4
- sourcecode/explain_endpoint.py +7 -1
- sourcecode/output_ceiling.py +8 -3
- sourcecode/pr_impact.py +17 -0
- sourcecode/rule_catalog.py +1 -0
- sourcecode/security_config_scan.py +22 -0
- sourcecode/security_posture.py +16 -2
- sourcecode/selftest.py +31 -6
- sourcecode/spring_impact.py +4 -0
- sourcecode/spring_security_audit.py +78 -1
- {sourcecode-5.8.8.dist-info → sourcecode-5.8.10.dist-info}/METADATA +3 -3
- {sourcecode-5.8.8.dist-info → sourcecode-5.8.10.dist-info}/RECORD +22 -22
- {sourcecode-5.8.8.dist-info → sourcecode-5.8.10.dist-info}/WHEEL +0 -0
- {sourcecode-5.8.8.dist-info → sourcecode-5.8.10.dist-info}/entry_points.txt +0 -0
- {sourcecode-5.8.8.dist-info → sourcecode-5.8.10.dist-info}/licenses/LICENSE +0 -0
- {sourcecode-5.8.8.dist-info → sourcecode-5.8.10.dist-info}/licenses/NOTICE +0 -0
sourcecode/__init__.py
CHANGED
|
@@ -16,8 +16,23 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
|
|
|
16
16
|
|
|
17
17
|
## Current Synchronization
|
|
18
18
|
|
|
19
|
-
**As of 2026-08-19,
|
|
20
|
-
|
|
19
|
+
**As of 2026-08-19, the `5.8.10` release commit:** this is the current status of
|
|
20
|
+
the 5.8.9 re-audit queue. The audit observations below remain historical evidence;
|
|
21
|
+
only the table here declares present state.
|
|
22
|
+
|
|
23
|
+
| Rows | Current status | Evidence |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `AUD-588-B01`, `AUD-589-B01`, `C3-122` | **closed** | `20596c8`; failed writes cannot suppress the global output-promise guard, covering both `risk` and `audit-report` emit paths. |
|
|
26
|
+
| `AUD-588-B03` | **closed** | `16a52cc`; `SEC-009`, static `@Profile` evidence and `permit_all_rules` are emitted with regressions. |
|
|
27
|
+
| `AUD-588-B04` | **closed** | `fd34eb1`; `data-exposure` preserves the chain decision for every route. |
|
|
28
|
+
| `AUD-588-B05` | **closed** | `c15f5b6`; changed container-wired components block a false `unaffected` disposition. |
|
|
29
|
+
| `AUD-588-B07` | **closed** | `5cc6ba7`; ASK-09 reads the envelope error contract and covers the wrapped shape. |
|
|
30
|
+
| `AUD-588-B08` | **closed** | `c084555`; `impact` publishes its independent stdout-byte budget. |
|
|
31
|
+
| `AUD-588-B11` (container/units subset) | **closed** | `ca72b61`, `a49cf6a`; plan/compare publish container wiring and impact-chain names security-surface units. |
|
|
32
|
+
| `AUD-588-B02`, `B06`, B11 residuals, `B12` | **open** | Require provider non-coverage verification, remaining contract parity and controlled cost calibration; no closure is inferred from the release. |
|
|
33
|
+
|
|
34
|
+
**As of 2026-08-19, the `5.8.9` release commit:** the latest audit queue was implemented in
|
|
35
|
+
committed post-5.8.7 fixes. This table is
|
|
21
36
|
the authoritative status for round 11; detailed rows retain their original findings and
|
|
22
37
|
acceptance criteria for auditability.
|
|
23
38
|
|
|
@@ -38,6 +53,64 @@ acceptance criteria for auditability.
|
|
|
38
53
|
| `C3-55` | **closed** | `63249df`; below-floor analysis budgets warn in noninteractive runs while execution continues |
|
|
39
54
|
| `C3-55` regression | **closed** | `f39432b`; above-floor provenance remains quiet off-TTY and is emitted interactively |
|
|
40
55
|
|
|
56
|
+
## Current Audit Intake: 5.8.8
|
|
57
|
+
|
|
58
|
+
The external audit of `ask 5.8.8` against `saint-server` (`HEAD 3dde0376`, 3,342 Java
|
|
59
|
+
files, Windows 11/Git Bash, `ASK_READONLY=1`) scored **75/100** and exercised **53/61
|
|
60
|
+
invocables (87%)**. The repository stayed byte-identical. These findings are new relative
|
|
61
|
+
to the closed round-11 queue and are not marked closed until reproduced and covered here.
|
|
62
|
+
|
|
63
|
+
| ID | Severity | Defect | Initial status |
|
|
64
|
+
|---|---|---|---|
|
|
65
|
+
| `AUD-588-B01` | Critical | `risk` can exit 0 with empty stdout and fail to create `-o`; long runs also vary widely. | **open — reproduce first; require parseable output or typed non-zero error** |
|
|
66
|
+
| `AUD-588-B02` | High | `migrate-check` includes an inapplicable Hibernate axis and publishes ghost effort for an EclipseLink repository. | **open — fix applicability/aggregate; add fixture** |
|
|
67
|
+
| `AUD-588-B03` | High | `spring-audit` lacks an active `permitAll` rule and does not expose security-bean profile activation state. | **open — reuse posture access/profile projection** |
|
|
68
|
+
| `AUD-588-B04` | High | `explain-endpoint` and `data-exposure` discard a derived `permit_all` chain decision and emit `coverage_unknown`. | **open — make verdict consume the chain authority** |
|
|
69
|
+
| `AUD-588-B05` | High | `pr-impact.endpoint_disposition` marks all endpoints unaffected despite container-wired security changes. | **open — propagate to `unknown` or publish a disposition caveat** |
|
|
70
|
+
| `AUD-588-B06` | Medium | `migrate-check` is the only audited command without `non_coverage`, hiding the EclipseLink gap. | **open — disclosure before product expansion** |
|
|
71
|
+
| `AUD-588-B07` | Medium | ASK-09 selftest failure is not reproducible and the row omits its probe command and observed excerpt. | **open — publish probe provenance and assert error code first** |
|
|
72
|
+
| `AUD-588-B08` | Medium | Output-too-large hints recommend a rejected subcommand flag and omit the independent byte budget. | **open — derive flags and disclose both budgets** |
|
|
73
|
+
| `AUD-588-B09` | Medium | `rename-class` reuses `-f` for `--from`, conflicting with the CLI-wide format alias and allowing silent overwrite. | **open — remove collision or reject duplicate destinations** |
|
|
74
|
+
| `AUD-588-B10` | Medium | `baseline diff` lacks `--dir`, breaking the external read-only capture/trend/diff flow. | **open — restore flag parity with regression coverage** |
|
|
75
|
+
| `AUD-588-B11` | Medium | Seven cross-command contract inconsistencies mix naming, units, container-wired warnings, build discovery, populations and tier claims. | **open — solve through shared fact/units authorities** |
|
|
76
|
+
| `AUD-588-B12` | Low/Medium | Cost model is stale by up to 1.74x at warm cache and long commands can be silent until timeout. | **open — remeasure 5.8.8 and add default foreground progress** |
|
|
77
|
+
|
|
78
|
+
### Re-audit Intake: 5.8.9
|
|
79
|
+
|
|
80
|
+
The same external auditor re-ran the same `saint-server` snapshot under the same
|
|
81
|
+
read-only protocol against 5.8.9. Score: **80/100** (75 previously), still **53/61
|
|
82
|
+
invocables (87%)**. The CIR remained byte-identical. This reopens false closures and
|
|
83
|
+
adds one newly observable zero-output path; it does not duplicate the rows above.
|
|
84
|
+
|
|
85
|
+
| ID / lineage | Status | Evidence and next action |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `AUD-588-B01` / `C3-122` | **reopened, critical** | `risk` exits 0 with empty stdout and can omit a promised `-o` file. Fix early/partial composition and enforce a typed failure or parseable answer through the real entry point. |
|
|
88
|
+
| `AUD-588-B02` + `B06` | **open, critical** | EclipseLink still receives a Hibernate axis and `non_coverage` is null. Make `not_applicable_reason` authoritative for applicability, effort and aggregate; publish provider non-coverage. |
|
|
89
|
+
| `AUD-588-B03` | **partial** | `permitAll` evidence and profiles are present, but no rule or per-finding activation state exists; rename `active_permit_all` to an honest per-entry contract. |
|
|
90
|
+
| `AUD-588-B04` | **partial** | `explain-endpoint` is fixed; `data-exposure` still reports `coverage_unknown` for chain-permitted routes. |
|
|
91
|
+
| `AUD-588-B05` | **regression** | `unaffected_basis` claims no blocker while container-wired changes are present. Demote routes or publish a blocker/caveat; preserve `affected + unknown + unaffected = total`. |
|
|
92
|
+
| `AUD-588-B07` | **partial** | Selftest publishes provenance but reads `error.code` at the wrong envelope level, producing a false `error` instead of `pass`. |
|
|
93
|
+
| `AUD-588-B08` | **partial** | `impact` still trims stdout at 50,000 bytes after the token ceiling is raised without naming that second budget. |
|
|
94
|
+
| `AUD-588-B11` | **open, six residuals** | `plan`/`compare` miss container reach; units, build discovery, populations and risk tier still diverge. |
|
|
95
|
+
| `AUD-588-B12` | **partial** | Timeline completes, but cost anchors are stale and long foreground commands remain silent by default. |
|
|
96
|
+
| `AUD-589-B01` | **new, high** | `audit-report` can exit 0 with empty stdout; its `-o` path diverges and leaves partial evidence when killed. Reuse the working `_partial` and output-promise contracts. |
|
|
97
|
+
| `AUD-589-B02` | **unconfirmed performance suspicion** | `data-exposure --config` was measured 2.76x slower with identical output; require convergent controlled measurements before code changes. |
|
|
98
|
+
|
|
99
|
+
### Recommended Features From This Audit
|
|
100
|
+
|
|
101
|
+
| ID | Recommendation | Classification |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| `AUD-588-F01` | Explicit EclipseLink/JPA-provider migration axis, including namespace and coordinate changes. | **Deferred**; its prerequisite provider `non_coverage` disclosure is still open. |
|
|
104
|
+
| `AUD-588-F02` | Shared answer-contract layer for units, populations, caveats and container-wired reach. | **Partially covered**; six consumers remain disconnected according to the re-audit. |
|
|
105
|
+
| `AUD-588-F03` | Versioned cost-model calibration by repository size and cache state. | **Partially covered**; schema and dimensions exist, but 5.1.0-era anchors are stale. |
|
|
106
|
+
| `AUD-588-F04` | Default progress/heartbeat for long foreground commands without contaminating machine-readable stdout. | **Explicit opt-in by decision**, not default coverage; update cost advice accordingly. |
|
|
107
|
+
| `AUD-588-F05` | Controlled audit-coverage expansion from 87% to 90% via safe `auth logout`, reversible `telemetry disable` and dirty-tree `verify-edit` checks. | **Locally verified only**; external audit remains at 87% with eight blocked invocables. |
|
|
108
|
+
|
|
109
|
+
**Recommended fix order:** `B01`, `B02`, `B04`, `B03`, `B05/B11`, `B08/B09`, `B07`,
|
|
110
|
+
then `B06/B10/B12`. The dirty-tree `verify-edit` path remains unverified, not confirmed
|
|
111
|
+
broken; the audit's positive results for inheritance, profile posture, caps, non-coverage,
|
|
112
|
+
read-only flows and typed errors are regression anchors for this queue.
|
|
113
|
+
|
|
41
114
|
The current full-suite exceptions are environmental or pre-existing measurement gaps,
|
|
42
115
|
not silently accepted fixes: RIS tests cannot write the default `~/.sourcecode/cache`
|
|
43
116
|
under the managed sandbox; `cold-start` therefore reports `no_ris`. The E-6 module-name
|
sourcecode/_docs/USER_GUIDE.md
CHANGED
sourcecode/change_plan.py
CHANGED
|
@@ -28,6 +28,7 @@ from typing import TYPE_CHECKING
|
|
|
28
28
|
|
|
29
29
|
from sourcecode import non_coverage as _non_coverage
|
|
30
30
|
from sourcecode.canonical_ir import project_blast_radius
|
|
31
|
+
from sourcecode.container_wiring import detect_classes
|
|
31
32
|
from sourcecode.repository_ir import _all_callers_from_rg
|
|
32
33
|
from sourcecode.security_posture import _graph_nodes
|
|
33
34
|
from sourcecode.path_filters import is_test_path
|
|
@@ -139,6 +140,7 @@ def build_change_plan(
|
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
seeds = list(blast["matched_fqns"])
|
|
143
|
+
container_wired = [w.to_dict() for w in detect_classes(cir, seeds)]
|
|
142
144
|
meta = _node_meta(cir)
|
|
143
145
|
affected = _dependents_closure(cir.reverse_graph or {}, seeds, max_depth, _NODE_CAP)
|
|
144
146
|
|
|
@@ -221,6 +223,7 @@ def build_change_plan(
|
|
|
221
223
|
"count": len(prod),
|
|
222
224
|
"components": prod[:_LIST_CAP],
|
|
223
225
|
},
|
|
226
|
+
"container_wired": container_wired,
|
|
224
227
|
"covering_tests": (
|
|
225
228
|
{
|
|
226
229
|
"count": len(tests),
|
sourcecode/cli.py
CHANGED
|
@@ -2112,12 +2112,14 @@ def _emit_command_output(
|
|
|
2112
2112
|
raise typer.Exit(1)
|
|
2113
2113
|
if output_path is not None:
|
|
2114
2114
|
_receipt = _safe_write_file(output_path, content)
|
|
2115
|
-
_mark_output_delivered()
|
|
2116
2115
|
if not _receipt.get("written"):
|
|
2117
2116
|
# B15: the answer was produced and thrown away, on request. Saying
|
|
2118
2117
|
# "written to nul" would be the one thing worse than the error this
|
|
2119
2118
|
# used to raise.
|
|
2120
2119
|
typer.echo(_receipt.get("note") or f"Discarded to {output_path}")
|
|
2120
|
+
# A failed write is never delivery. The guard separately exempts
|
|
2121
|
+
# discard sinks and existing files by path, so no state flag is
|
|
2122
|
+
# needed here.
|
|
2121
2123
|
elif success_msg:
|
|
2122
2124
|
# C3-26/C3-18: the payload went to the file, so stdout is free and
|
|
2123
2125
|
# this confirmation is the run's whole answer — `cache warm` and
|
|
@@ -2126,12 +2128,14 @@ def _emit_command_output(
|
|
|
2126
2128
|
# stderr battery listed its commands by hand. Measured before the
|
|
2127
2129
|
# fix on spring-petclinic: stdout 0 bytes, stderr 155.
|
|
2128
2130
|
typer.echo(success_msg)
|
|
2131
|
+
_mark_output_delivered()
|
|
2129
2132
|
else:
|
|
2130
2133
|
# C3-122: a command whose emit seam carries no sentence of its own
|
|
2131
2134
|
# confirmed a 444-second run with total silence. The receipt is a
|
|
2132
2135
|
# notice, so a pipeline and a PowerShell capture stay exactly as
|
|
2133
2136
|
# they were (C3-26) and a person at a terminal is told.
|
|
2134
2137
|
_notice(f"✓ {_receipt.get('bytes', 0)} bytes written to {output_path}")
|
|
2138
|
+
_mark_output_delivered()
|
|
2135
2139
|
else:
|
|
2136
2140
|
try:
|
|
2137
2141
|
sys.stdout.buffer.write(content.encode("utf-8"))
|
|
@@ -2154,7 +2158,8 @@ def _emit_command_output(
|
|
|
2154
2158
|
_notice("✓ copied to clipboard")
|
|
2155
2159
|
# The answer has been handed over. Any `done (Xs)` a command deferred is now
|
|
2156
2160
|
# a true claim and prints here — never before this line (C3-54).
|
|
2157
|
-
|
|
2161
|
+
if _OUTPUT_DELIVERED:
|
|
2162
|
+
_progress_output_emitted()
|
|
2158
2163
|
|
|
2159
2164
|
|
|
2160
2165
|
def _emit_text_output(
|
|
@@ -2860,15 +2865,10 @@ COMMANDS_WITHOUT_FORMAT: "frozenset[str]" = frozenset({
|
|
|
2860
2865
|
"version", # a version string
|
|
2861
2866
|
})
|
|
2862
2867
|
|
|
2863
|
-
#:
|
|
2864
|
-
#:
|
|
2865
|
-
#:
|
|
2866
|
-
|
|
2867
|
-
#: parser for a class name. The long `--format` works; the short form moves at
|
|
2868
|
-
#: the next major.
|
|
2869
|
-
COMMANDS_WITHOUT_FORMAT_SHORTHAND: "frozenset[str]" = frozenset({
|
|
2870
|
-
"rename-class",
|
|
2871
|
-
})
|
|
2868
|
+
#: Every payload command uses the same `--format/-f` spelling. A previous
|
|
2869
|
+
#: exception let `rename-class -f` overwrite `--from` silently; removing that
|
|
2870
|
+
#: alias is safer than making one short flag mean two unrelated facts.
|
|
2871
|
+
COMMANDS_WITHOUT_FORMAT_SHORTHAND: "frozenset[str]" = frozenset()
|
|
2872
2872
|
|
|
2873
2873
|
|
|
2874
2874
|
#: The one help string for `--progress`, for the same reason as COPY_OPTION_HELP.
|
|
@@ -6988,6 +6988,16 @@ def impact_cmd(
|
|
|
6988
6988
|
cir._raw_ir, target, max_depth=depth, list_limit=0 if _to_file else None,
|
|
6989
6989
|
root=root, covered_types=_covered_types,
|
|
6990
6990
|
)
|
|
6991
|
+
# Token ceilings and this command's byte budget are independent controls.
|
|
6992
|
+
# Publish both at the point where the impact answer is shaped so raising
|
|
6993
|
+
# ASK_MAX_OUTPUT_TOKENS cannot be mistaken for disabling stdout trimming.
|
|
6994
|
+
result["stdout_byte_budget"] = BUDGET_IMPACT if not _to_file else None
|
|
6995
|
+
result["stdout_byte_budget_basis"] = (
|
|
6996
|
+
"impact stdout rendering is capped independently at 50,000 UTF-8 bytes; "
|
|
6997
|
+
"ASK_MAX_OUTPUT_TOKENS does not disable this byte budget"
|
|
6998
|
+
if not _to_file else
|
|
6999
|
+
"No stdout byte budget applies when --output writes the complete answer."
|
|
7000
|
+
)
|
|
6991
7001
|
result = _apply_budget(result, BUDGET_IMPACT, label="impact", output_path=output_path)
|
|
6992
7002
|
if _misread and result.get("resolution") == "not_found":
|
|
6993
7003
|
result["analysis_warnings"] = [*(result.get("analysis_warnings") or []), _misread]
|
|
@@ -8722,6 +8732,10 @@ def baseline_diff_cmd(
|
|
|
8722
8732
|
Path("."),
|
|
8723
8733
|
help="Repository root to diff against the stored baseline (default: cwd).",
|
|
8724
8734
|
),
|
|
8735
|
+
history_dir: Optional[Path] = typer.Option(
|
|
8736
|
+
None, "--dir", "--history-dir",
|
|
8737
|
+
help="Directory containing stored architectural-baseline-v1 artifacts.",
|
|
8738
|
+
),
|
|
8725
8739
|
baseline_file: Optional[Path] = typer.Option(
|
|
8726
8740
|
None, "--baseline", "-b",
|
|
8727
8741
|
help="Path to a stored architectural-baseline-v1 JSON artifact. Omit it to "
|
|
@@ -8767,7 +8781,7 @@ def baseline_diff_cmd(
|
|
|
8767
8781
|
if baseline_file is None:
|
|
8768
8782
|
from sourcecode.architectural_baseline import latest_baseline_path
|
|
8769
8783
|
|
|
8770
|
-
history = _resolve_history_dir(target_dir)
|
|
8784
|
+
history = _resolve_history_dir((history_dir or target_dir).resolve())
|
|
8771
8785
|
latest = latest_baseline_path(history)
|
|
8772
8786
|
if latest is None:
|
|
8773
8787
|
_emit_error_json(
|
|
@@ -13330,7 +13344,7 @@ def rename_class_cmd(
|
|
|
13330
13344
|
help="Repository root to operate on (default: current directory)",
|
|
13331
13345
|
),
|
|
13332
13346
|
old_name: str = typer.Option(
|
|
13333
|
-
..., "--from",
|
|
13347
|
+
..., "--from",
|
|
13334
13348
|
help="Current class name (PascalCase, e.g. ServiceA)",
|
|
13335
13349
|
),
|
|
13336
13350
|
new_name: str = typer.Option(
|
|
@@ -13350,10 +13364,7 @@ def rename_class_cmd(
|
|
|
13350
13364
|
help="Write change audit JSON to a file instead of stdout.",
|
|
13351
13365
|
),
|
|
13352
13366
|
copy: bool = _copy_option(),
|
|
13353
|
-
|
|
13354
|
-
# existed: rebinding it would hand a class name to a format parser.
|
|
13355
|
-
# On the record in cli.COMMANDS_WITHOUT_FORMAT_SHORTHAND (C3-59).
|
|
13356
|
-
format: str = _format_option(help="Output format: json (default) or yaml.", shorthand=False),
|
|
13367
|
+
format: str = _format_option(help="Output format: json (default) or yaml."),
|
|
13357
13368
|
progress_mode: Optional[str] = _progress_option(),
|
|
13358
13369
|
) -> None:
|
|
13359
13370
|
"""Rename a Java class throughout the repository.
|
sourcecode/compare.py
CHANGED
|
@@ -262,6 +262,7 @@ def build_comparison(
|
|
|
262
262
|
"resolution": resolution,
|
|
263
263
|
"resolved": resolved,
|
|
264
264
|
"cost": cost,
|
|
265
|
+
"container_wired": plan.get("container_wired") if resolved else None,
|
|
265
266
|
"why": _evidence_trail(cost, blast, resolved, plan),
|
|
266
267
|
}
|
|
267
268
|
# C1-13/C2-1: when the walk was capped, some magnitudes are floors. The
|
sourcecode/data_exposure.py
CHANGED
|
@@ -83,6 +83,7 @@ class ExposedRoute:
|
|
|
83
83
|
verdict: str
|
|
84
84
|
access: Optional[str] = None
|
|
85
85
|
access_decision: Optional[str] = None
|
|
86
|
+
chain_decision: Optional[str] = None
|
|
86
87
|
|
|
87
88
|
def to_dict(self) -> dict:
|
|
88
89
|
out = {
|
|
@@ -100,6 +101,8 @@ class ExposedRoute:
|
|
|
100
101
|
if self.access is not None:
|
|
101
102
|
out["access_under_profile"] = self.access
|
|
102
103
|
out["access_decision"] = self.access_decision
|
|
104
|
+
if self.chain_decision is not None:
|
|
105
|
+
out["chain_decision"] = self.chain_decision
|
|
103
106
|
return out
|
|
104
107
|
|
|
105
108
|
|
|
@@ -232,12 +235,13 @@ def build_data_exposure(
|
|
|
232
235
|
from sourcecode.security_posture import endpoint_security_surface
|
|
233
236
|
from sourcecode.spring_impact import run_impact_chain
|
|
234
237
|
|
|
235
|
-
surface = endpoint_security_surface(cir, root=root)
|
|
236
238
|
profile_access: dict[str, str] = {}
|
|
237
|
-
|
|
238
|
-
from sourcecode.posture import endpoint_access_under
|
|
239
|
+
from sourcecode.posture import endpoint_access_under
|
|
239
240
|
|
|
240
|
-
|
|
241
|
+
profile_access = endpoint_access_under(root, set(profiles or ()), cir=cir)
|
|
242
|
+
surface = endpoint_security_surface(
|
|
243
|
+
cir, root=root, access_decisions=profile_access,
|
|
244
|
+
)
|
|
241
245
|
|
|
242
246
|
handlers = {
|
|
243
247
|
str(getattr(e, "handler_symbol", "") or (e.get("handler_symbol") if isinstance(e, dict) else "")): e
|
|
@@ -374,6 +378,10 @@ def _finish_route(
|
|
|
374
378
|
verdict=verdict,
|
|
375
379
|
access=access,
|
|
376
380
|
access_decision=decision,
|
|
381
|
+
chain_decision=(
|
|
382
|
+
str(bound["chain_decision"])
|
|
383
|
+
if bound.get("chain_decision") is not None else None
|
|
384
|
+
),
|
|
377
385
|
)
|
|
378
386
|
|
|
379
387
|
|
sourcecode/explain_endpoint.py
CHANGED
|
@@ -368,6 +368,10 @@ def _verdict_step(endpoint: Any, surface: "dict[str, dict]") -> "dict[str, Any]"
|
|
|
368
368
|
verdict=answer.get("verdict"),
|
|
369
369
|
confidence=answer.get("confidence"),
|
|
370
370
|
declared_policy=answer.get("declared_policy"),
|
|
371
|
+
**(
|
|
372
|
+
{"chain_decision": answer["chain_decision"]}
|
|
373
|
+
if answer.get("chain_decision") is not None else {}
|
|
374
|
+
),
|
|
371
375
|
note=(
|
|
372
376
|
"this is the coverage answer; `declared_policy` beside it is the "
|
|
373
377
|
"annotation vocabulary, which is evidence and not a verdict"
|
|
@@ -439,7 +443,9 @@ def explain_endpoint(
|
|
|
439
443
|
access_block, access = {"chains": {}, "_error": str(exc)}, {}
|
|
440
444
|
evidence = dict(access_block.get("evidence") or {})
|
|
441
445
|
try:
|
|
442
|
-
surface = endpoint_security_surface(
|
|
446
|
+
surface = endpoint_security_surface(
|
|
447
|
+
cir, root=root, access_decisions=access,
|
|
448
|
+
)
|
|
443
449
|
except Exception:
|
|
444
450
|
surface = {}
|
|
445
451
|
|
sourcecode/output_ceiling.py
CHANGED
|
@@ -408,9 +408,7 @@ def ways_out(
|
|
|
408
408
|
if destination != "file":
|
|
409
409
|
# The env var is the bypass for *this* ceiling; the MB guard on a file is
|
|
410
410
|
# a different one and is not moved by it, so it is not offered there.
|
|
411
|
-
out.append(
|
|
412
|
-
f"{ENV_CEILING}=0 (or --max-output-tokens 0) to print it anyway"
|
|
413
|
-
)
|
|
411
|
+
out.append(f"{ENV_CEILING}=0 to print it anyway")
|
|
414
412
|
return out
|
|
415
413
|
|
|
416
414
|
|
|
@@ -480,6 +478,7 @@ def exceeds(content: str, *, to_file: bool) -> "Optional[dict[str, Any]]":
|
|
|
480
478
|
if tokens <= limit:
|
|
481
479
|
return None
|
|
482
480
|
basis = resolve()
|
|
481
|
+
output_bytes = len(content.encode("utf-8"))
|
|
483
482
|
return {
|
|
484
483
|
"code": ERROR_CODE,
|
|
485
484
|
"message": (
|
|
@@ -489,6 +488,12 @@ def exceeds(content: str, *, to_file: bool) -> "Optional[dict[str, Any]]":
|
|
|
489
488
|
"hint": build_hint(basis=basis),
|
|
490
489
|
"expected": f"Output under {limit // 1000}K estimated tokens.",
|
|
491
490
|
"estimated_tokens": tokens,
|
|
491
|
+
"estimated_bytes": output_bytes,
|
|
492
|
+
"stdout_byte_budget": None,
|
|
493
|
+
"stdout_byte_budget_basis": (
|
|
494
|
+
"No independent byte trimming budget; estimated_tokens is derived "
|
|
495
|
+
f"from UTF-8 bytes / {BYTES_PER_TOKEN}."
|
|
496
|
+
),
|
|
492
497
|
"ceiling_tokens": limit,
|
|
493
498
|
"estimate_basis": (
|
|
494
499
|
f"UTF-8 bytes ÷ {BYTES_PER_TOKEN}, an ordering estimate for JSON — "
|
sourcecode/pr_impact.py
CHANGED
|
@@ -786,6 +786,23 @@ def _run_pr_impact_internal(
|
|
|
786
786
|
# `ProductHandlerMapping → blCatalogService → ProductDaoImpl` chain, caught
|
|
787
787
|
# structurally rather than by recognising a name.
|
|
788
788
|
disposition_blockers: list[_disposition.Blocker] = []
|
|
789
|
+
changed_wired = [
|
|
790
|
+
item for item in wired
|
|
791
|
+
if set(getattr(item, "kinds", ()) or ())
|
|
792
|
+
& {"container_configuration", "interception_point"}
|
|
793
|
+
]
|
|
794
|
+
if changed_wired:
|
|
795
|
+
_named = ", ".join(item.symbol for item in changed_wired[:3])
|
|
796
|
+
disposition_blockers.append(_disposition.Blocker(
|
|
797
|
+
reason=_disposition.REASON_CONTAINER_DISPATCH,
|
|
798
|
+
detail=(
|
|
799
|
+
f"{len(changed_wired)} changed container-wired component(s) "
|
|
800
|
+
f"({_named}{', …' if len(changed_wired) > 3 else ''}) are framework "
|
|
801
|
+
"dispatch points. Their route reach is not represented by the call "
|
|
802
|
+
"cone, so routes with no modeled path are unknown rather than "
|
|
803
|
+
"unaffected (NC-010)."
|
|
804
|
+
),
|
|
805
|
+
))
|
|
789
806
|
wired_in_cone = detect_container_wired_classes(
|
|
790
807
|
cir, sorted(cone_classes - class_fqns_set)
|
|
791
808
|
)
|
sourcecode/rule_catalog.py
CHANGED
|
@@ -56,6 +56,7 @@ RULES: tuple[Rule, ...] = (
|
|
|
56
56
|
Rule("SEC-006", "SEC", "Cookie created without the Secure attribute"),
|
|
57
57
|
Rule("SEC-007", "SEC", "Credential stored in a deployment descriptor"),
|
|
58
58
|
Rule("SEC-008", "SEC", "SQL built by MyBatis string interpolation"),
|
|
59
|
+
Rule("SEC-009", "SEC", "Anonymous request-chain exception"),
|
|
59
60
|
Rule("DEAD-001", "DEAD", "Security control present but disabled"),
|
|
60
61
|
Rule("GATE-001", "GATE", "Custom AOP/security gate can be bypassed by proxy limits"),
|
|
61
62
|
Rule("GATE-002", "GATE", "Custom gate advice appears to fail open"),
|
|
@@ -258,6 +258,28 @@ def _enclosing_type(source: str, offset: int, fallback: str) -> str:
|
|
|
258
258
|
def _scan_java(source: str, rel: str) -> "list[SecurityConfigObservation]":
|
|
259
259
|
out: "list[SecurityConfigObservation]" = []
|
|
260
260
|
|
|
261
|
+
# SEC-009 — an anonymous request-chain exception is a finding in its own
|
|
262
|
+
# right. Keep the matcher context requirement narrow so unrelated method
|
|
263
|
+
# names containing `permitAll()` do not become security findings.
|
|
264
|
+
permit = re.search(r"\bpermitAll\s*\(\s*\)", source)
|
|
265
|
+
if permit and re.search(
|
|
266
|
+
r"SecurityFilterChain|authorizeHttpRequests|authorizeRequests|"
|
|
267
|
+
r"requestMatchers|anyRequest",
|
|
268
|
+
source,
|
|
269
|
+
):
|
|
270
|
+
out.append(SecurityConfigObservation(
|
|
271
|
+
rule="SEC-009",
|
|
272
|
+
file=rel,
|
|
273
|
+
line=_line_of(source, permit.start()),
|
|
274
|
+
symbol=_enclosing_type(source, permit.start(), Path(rel).stem),
|
|
275
|
+
evidence=permit.group(0),
|
|
276
|
+
detail=(
|
|
277
|
+
"The active source contains a request-chain rule that permits "
|
|
278
|
+
"anonymous access. Verify its matcher and profile condition; this "
|
|
279
|
+
"exception does not prove that the whole chain is public."
|
|
280
|
+
),
|
|
281
|
+
))
|
|
282
|
+
|
|
261
283
|
# SEC-004 — a password stored under a fast unsalted digest.
|
|
262
284
|
if any(tok in source for tok in _ENCODER_CONTEXT):
|
|
263
285
|
for token in _WEAK_DIGESTS:
|
sourcecode/security_posture.py
CHANGED
|
@@ -672,6 +672,7 @@ def endpoint_security_surface(
|
|
|
672
672
|
*,
|
|
673
673
|
root: Optional[Path] = None,
|
|
674
674
|
posture: Optional[Any] = None,
|
|
675
|
+
access_decisions: "Optional[dict[str, str]]" = None,
|
|
675
676
|
) -> dict[str, dict]:
|
|
676
677
|
"""THE per-endpoint security answer, keyed by ``endpoint_id`` (ADR-0008 R1).
|
|
677
678
|
|
|
@@ -711,11 +712,24 @@ def endpoint_security_surface(
|
|
|
711
712
|
ep_id = v.get("endpoint_id") or ""
|
|
712
713
|
if not ep_id:
|
|
713
714
|
continue
|
|
715
|
+
verdict = v.get("verdict") or "coverage_unknown"
|
|
716
|
+
confidence = v.get("confidence") or Confidence.UNKNOWN.value
|
|
717
|
+
chain_decision = (
|
|
718
|
+
access_decisions.get(str(ep_id))
|
|
719
|
+
if access_decisions is not None else None
|
|
720
|
+
)
|
|
721
|
+
if chain_decision in {"permit_all", "anonymous"}:
|
|
722
|
+
verdict = "probably_exposed"
|
|
723
|
+
confidence = Confidence.VERIFIED.value
|
|
724
|
+
elif chain_decision in {"authenticated", "role_required", "deny_all"}:
|
|
725
|
+
verdict = "protected_standard"
|
|
726
|
+
confidence = Confidence.VERIFIED.value
|
|
714
727
|
out[ep_id] = {
|
|
715
|
-
"verdict":
|
|
716
|
-
"confidence":
|
|
728
|
+
"verdict": verdict,
|
|
729
|
+
"confidence": confidence,
|
|
717
730
|
# Evidence, not answer: the guard vocabulary declared on the handler.
|
|
718
731
|
"declared_policy": v.get("policy") or "unknown",
|
|
732
|
+
**({"chain_decision": chain_decision} if chain_decision is not None else {}),
|
|
719
733
|
}
|
|
720
734
|
return out
|
|
721
735
|
|
sourcecode/selftest.py
CHANGED
|
@@ -131,25 +131,49 @@ def _platform_label() -> str:
|
|
|
131
131
|
|
|
132
132
|
def _check_ask09(subject: Subject) -> "dict[str, Any]":
|
|
133
133
|
"""The ceiling hint names the bounding flags the command declares."""
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
probe_args = ("endpoints", str(subject.repo))
|
|
135
|
+
code, payload, stdout = subject.run(*probe_args)
|
|
136
|
+
executable = (
|
|
137
|
+
subject.entry_point
|
|
138
|
+
if subject.entry_point.endswith("ask")
|
|
139
|
+
else f"{sys.executable} {subject.entry_point}"
|
|
140
|
+
)
|
|
141
|
+
probe_command = " ".join((executable, *probe_args))
|
|
142
|
+
observed_excerpt = (stdout or json.dumps(payload or {}, sort_keys=True))[:500]
|
|
143
|
+
evidence = {
|
|
144
|
+
"probe_command": probe_command,
|
|
145
|
+
"observed_payload_excerpt": observed_excerpt,
|
|
146
|
+
}
|
|
147
|
+
document = payload if isinstance(payload, dict) else {}
|
|
148
|
+
body = _payload_of(document)
|
|
136
149
|
if code == 0:
|
|
137
150
|
return _result(
|
|
138
151
|
NOT_APPLICABLE,
|
|
139
152
|
"this repository's `endpoints` answer fits under the output ceiling, "
|
|
140
153
|
"so the guard this row is about never ran — a bigger repository is "
|
|
141
154
|
"needed to exercise it here",
|
|
155
|
+
**evidence,
|
|
142
156
|
)
|
|
143
|
-
|
|
157
|
+
# The output seam may wrap the command payload and keep the envelope error
|
|
158
|
+
# beside it. Read both shapes; dropping the envelope first turns a real
|
|
159
|
+
# OUTPUT_TOO_LARGE refusal into a false ERROR with code=None.
|
|
160
|
+
error = document.get("error") or body.get("error")
|
|
144
161
|
error_body = error if isinstance(error, dict) else body
|
|
162
|
+
if error_body.get("code") != "OUTPUT_TOO_LARGE":
|
|
163
|
+
return _result(
|
|
164
|
+
ERROR,
|
|
165
|
+
f"the probe exited {code} without OUTPUT_TOO_LARGE "
|
|
166
|
+
f"(observed code={error_body.get('code')!r})",
|
|
167
|
+
**evidence,
|
|
168
|
+
)
|
|
145
169
|
hint = str(error_body.get("hint") or "")
|
|
146
|
-
basis = body.get("hint_basis") or {}
|
|
170
|
+
basis = document.get("hint_basis") or body.get("hint_basis") or {}
|
|
147
171
|
declared = list(basis.get("declared_flags") or [])
|
|
148
172
|
if "No smaller inline variant is available" in hint and declared:
|
|
149
173
|
return _result(
|
|
150
174
|
FAIL,
|
|
151
175
|
f"the refusal denies a smaller view while declaring {declared}",
|
|
152
|
-
hint=hint, hint_basis=basis,
|
|
176
|
+
hint=hint, hint_basis=basis, **evidence,
|
|
153
177
|
)
|
|
154
178
|
if not basis:
|
|
155
179
|
return _result(
|
|
@@ -157,12 +181,13 @@ def _check_ask09(subject: Subject) -> "dict[str, Any]":
|
|
|
157
181
|
"the refusal carries no `hint_basis`, so what the hint was derived "
|
|
158
182
|
"from cannot be read from one run — the observability half of the row",
|
|
159
183
|
hint=hint,
|
|
184
|
+
**evidence,
|
|
160
185
|
)
|
|
161
186
|
return _result(
|
|
162
187
|
PASS,
|
|
163
188
|
f"the refusal derives its hint from {basis.get('derived_from')!r} "
|
|
164
189
|
f"(context_found={basis.get('context_found')}) and offers {declared}",
|
|
165
|
-
hint=hint, hint_basis=basis,
|
|
190
|
+
hint=hint, hint_basis=basis, **evidence,
|
|
166
191
|
)
|
|
167
192
|
|
|
168
193
|
|
sourcecode/spring_impact.py
CHANGED
|
@@ -1588,6 +1588,10 @@ class ImpactOrchestrator:
|
|
|
1588
1588
|
"indirect_caller_symbol_count_unit": "indirect caller list entries",
|
|
1589
1589
|
"caller_metric_note": CALLER_METRIC_RECONCILIATION,
|
|
1590
1590
|
"endpoints_total": len(endpoints_affected),
|
|
1591
|
+
"security_surfaces_unit": (
|
|
1592
|
+
"endpoint rows in the impact chain; each row is one affected "
|
|
1593
|
+
"endpoint and carries its security posture verdict"
|
|
1594
|
+
),
|
|
1591
1595
|
"findings_in_chain": len(impact_findings),
|
|
1592
1596
|
"findings_status": findings_status,
|
|
1593
1597
|
"findings_status_note": (
|
|
@@ -508,6 +508,7 @@ class _SEC00XConfigurationWeaknesses:
|
|
|
508
508
|
# A credential every reader of the repository already holds.
|
|
509
509
|
"SEC-007": "high",
|
|
510
510
|
"SEC-008": "high",
|
|
511
|
+
"SEC-009": "medium",
|
|
511
512
|
# C3-101 (c), decided in 5.8.7 and recorded here rather than absorbed.
|
|
512
513
|
# The row left this open on purpose after (a) and (b) shipped: 8 `high`
|
|
513
514
|
# findings and 0 true positives is a reason to ask whether the severity
|
|
@@ -527,6 +528,7 @@ class _SEC00XConfigurationWeaknesses:
|
|
|
527
528
|
"SEC-006": "Cookie created without the Secure attribute",
|
|
528
529
|
"SEC-007": "Credential stored in a deployment descriptor",
|
|
529
530
|
"SEC-008": "SQL built by MyBatis string interpolation",
|
|
531
|
+
"SEC-009": "Anonymous request-chain exception",
|
|
530
532
|
"DEAD-001": "Security control present but disabled",
|
|
531
533
|
}
|
|
532
534
|
#: A pattern that emits more than one rule id declares them all, so a catalogue
|
|
@@ -560,6 +562,11 @@ class _SEC00XConfigurationWeaknesses:
|
|
|
560
562
|
"an identifier such as an ORDER BY column, map it through a closed "
|
|
561
563
|
"allowlist before it reaches the mapper."
|
|
562
564
|
),
|
|
565
|
+
"SEC-009": (
|
|
566
|
+
"Confirm the matcher is intentionally public under the applicable "
|
|
567
|
+
"profile, keep it as narrow as possible, and do not treat it as "
|
|
568
|
+
"evidence that unrelated routes are protected or public."
|
|
569
|
+
),
|
|
563
570
|
"DEAD-001": (
|
|
564
571
|
"Decide explicitly: either restore the commented control and verify it in "
|
|
565
572
|
"the active profile, or delete the dead block with a note explaining the "
|
|
@@ -590,6 +597,11 @@ class _SEC00XConfigurationWeaknesses:
|
|
|
590
597
|
|
|
591
598
|
findings: list[SpringFinding] = []
|
|
592
599
|
for obs in observations:
|
|
600
|
+
profile_condition, active_by_default = _profile_evidence(root, obs.file)
|
|
601
|
+
evidence = {"matched": obs.evidence, "line": obs.line, "rule": obs.rule}
|
|
602
|
+
if profile_condition is not None:
|
|
603
|
+
evidence["profile_condition"] = profile_condition
|
|
604
|
+
evidence["active_under_default_profiles"] = active_by_default
|
|
593
605
|
findings.append(SpringFinding(
|
|
594
606
|
id=SpringFinding.make_id(obs.rule, f"{obs.file}:{obs.line}"),
|
|
595
607
|
pattern_id=obs.rule,
|
|
@@ -602,7 +614,7 @@ class _SEC00XConfigurationWeaknesses:
|
|
|
602
614
|
symbol=obs.symbol,
|
|
603
615
|
source_file=obs.file,
|
|
604
616
|
first_line=obs.line,
|
|
605
|
-
evidence=
|
|
617
|
+
evidence=evidence,
|
|
606
618
|
explanation=obs.detail,
|
|
607
619
|
fix_hint=self._FIX.get(obs.rule, ""),
|
|
608
620
|
limitations=[
|
|
@@ -618,6 +630,23 @@ def _line_of(source: str, offset: int) -> int:
|
|
|
618
630
|
return source.count("\n", 0, max(0, offset)) + 1
|
|
619
631
|
|
|
620
632
|
|
|
633
|
+
def _profile_evidence(root: Optional[Path], rel: str) -> tuple[Optional[str], Optional[bool]]:
|
|
634
|
+
"""Return static @Profile evidence without claiming runtime activation."""
|
|
635
|
+
if root is None:
|
|
636
|
+
return None, None
|
|
637
|
+
try:
|
|
638
|
+
source = (Path(root) / rel).read_text(encoding="utf-8", errors="replace")
|
|
639
|
+
except OSError:
|
|
640
|
+
return None, None
|
|
641
|
+
match = re.search(r"@Profile\s*\((?P<expr>[^)]*)\)", source)
|
|
642
|
+
if match is None:
|
|
643
|
+
return None, True
|
|
644
|
+
expr = match.group("expr").strip()
|
|
645
|
+
names = re.findall(r'"([^"]+)"', expr)
|
|
646
|
+
active_by_default = bool(names) and all(name.startswith("!") for name in names)
|
|
647
|
+
return expr, active_by_default
|
|
648
|
+
|
|
649
|
+
|
|
621
650
|
_JAVA_METHOD_RE = re.compile(
|
|
622
651
|
r"(?P<mods>(?:(?:public|protected|private|static|final|synchronized|abstract|native)\s+)*)"
|
|
623
652
|
r"(?P<ret>[\w.$<>\[\], ?]+)\s+"
|
|
@@ -1288,6 +1317,45 @@ def run_security_audit(
|
|
|
1288
1317
|
cir, model=model, tx_total=tx_index.stats()["total"]
|
|
1289
1318
|
)
|
|
1290
1319
|
|
|
1320
|
+
_profile_state: dict[str, Any] = {
|
|
1321
|
+
"selected": [],
|
|
1322
|
+
"resolution": "unresolved",
|
|
1323
|
+
"note": (
|
|
1324
|
+
"No runtime Spring profile was supplied to spring-audit; the listed "
|
|
1325
|
+
"profiles are static candidates, not proof of activation."
|
|
1326
|
+
),
|
|
1327
|
+
}
|
|
1328
|
+
_permit_all_rules: list[dict[str, Any]] = []
|
|
1329
|
+
if root is not None:
|
|
1330
|
+
try:
|
|
1331
|
+
from sourcecode.spring_profiles import profile_population
|
|
1332
|
+
from sourcecode.source_text import blank_java_comments
|
|
1333
|
+
|
|
1334
|
+
_profile_state["population"] = profile_population(
|
|
1335
|
+
Path(root), list(cir.files or [])
|
|
1336
|
+
).to_dict()
|
|
1337
|
+
for rel in (cir.files or []):
|
|
1338
|
+
if not isinstance(rel, str) or not rel.endswith(".java"):
|
|
1339
|
+
continue
|
|
1340
|
+
try:
|
|
1341
|
+
source = (Path(root) / rel).read_text(
|
|
1342
|
+
encoding="utf-8", errors="replace"
|
|
1343
|
+
)
|
|
1344
|
+
except OSError:
|
|
1345
|
+
continue
|
|
1346
|
+
live = blank_java_comments(source)
|
|
1347
|
+
for match in re.finditer(r"\bpermitAll\s*\(\s*\)", live):
|
|
1348
|
+
profile_condition, active_by_default = _profile_evidence(root, rel)
|
|
1349
|
+
_permit_all_rules.append({
|
|
1350
|
+
"file": rel,
|
|
1351
|
+
"line": live[:match.start()].count("\n") + 1,
|
|
1352
|
+
"evidence": match.group(0),
|
|
1353
|
+
"profile_condition": profile_condition,
|
|
1354
|
+
"active_under_default_profiles": active_by_default,
|
|
1355
|
+
})
|
|
1356
|
+
except Exception as exc:
|
|
1357
|
+
_profile_state["population_error"] = str(exc)
|
|
1358
|
+
|
|
1291
1359
|
_sec_limitations = [
|
|
1292
1360
|
"first_line is the earliest source token recorded for the symbol (SourceSpan index) — inside the declaration, not necessarily the declaration line itself; null when the symbol has no recorded span",
|
|
1293
1361
|
"SEC-001: only emitted for annotation_based security model",
|
|
@@ -1361,6 +1429,15 @@ def run_security_audit(
|
|
|
1361
1429
|
"separately under non_spring_rest_surface)."
|
|
1362
1430
|
),
|
|
1363
1431
|
"security_model": cir.metadata.get("security_model", "unknown"),
|
|
1432
|
+
"security_profiles": _profile_state,
|
|
1433
|
+
"permit_all_rules": {
|
|
1434
|
+
"rules": _permit_all_rules,
|
|
1435
|
+
"count": len(_permit_all_rules),
|
|
1436
|
+
"note": (
|
|
1437
|
+
"Rules are live source evidence. Profile fields are static hints; "
|
|
1438
|
+
"runtime activation still depends on the selected Spring profile."
|
|
1439
|
+
),
|
|
1440
|
+
},
|
|
1364
1441
|
"analysis_time_ms": elapsed_ms,
|
|
1365
1442
|
# C3-71: which families answered, and which the budget cut. Present on
|
|
1366
1443
|
# every run, so "all of them" is a published fact rather than an
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 5.8.
|
|
3
|
+
Version: 5.8.10
|
|
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
|
|
@@ -49,7 +49,7 @@ Description-Content-Type: text/markdown
|
|
|
49
49
|
|
|
50
50
|
**Context · Impact · Migration · Architecture · Review — everything from one structural model.**
|
|
51
51
|
|
|
52
|
-

|
|
53
53
|

|
|
54
54
|
|
|
55
55
|
Copyright (c) 2026 Dominique Haroun. All rights reserved. ASK Engine 5.7.0 and
|
|
@@ -139,7 +139,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
|
|
|
139
139
|
# pip / pipx
|
|
140
140
|
pipx install sourcecode # or: pip install sourcecode
|
|
141
141
|
|
|
142
|
-
ask version # ask 5.8.
|
|
142
|
+
ask version # ask 5.8.10 — and, on a build that has aged,
|
|
143
143
|
# how many releases have probably shipped since
|
|
144
144
|
```
|
|
145
145
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=N6jOxr62_YHW3o1udlxIduDuvRxhmGvpOs9L3F4lv28,309
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=yJBKjNpkY6bpueYJ2YnRezen3sYZDecEt7WaaNWdqug,9466
|
|
3
3
|
sourcecode/answer_bounds.py,sha256=_51_DCuYEUmI_xwUFxDMiprSavkNakkLiIBbPjevQ3M,9793
|
|
4
4
|
sourcecode/archetype.py,sha256=uubguXP5ltQt25ACNVm9kX4KRTe7JCqkFfg4H9zckCo,40971
|
|
@@ -22,13 +22,13 @@ sourcecode/caller_metrics.py,sha256=--sFGDnIog_YGu9xZHMcB91F5xZakfaAKvy15xU54hg,
|
|
|
22
22
|
sourcecode/caller_reach.py,sha256=M7Ffs5CtlimihfdSy3HtC4WpDP97bRFX15QYOlOfMJw,16626
|
|
23
23
|
sourcecode/canonical_ir.py,sha256=UmCvjGIz7kyjcKdZFYZuxa9y02EfsmDGZj3kUEKzCvo,36200
|
|
24
24
|
sourcecode/chain_rules.py,sha256=Bi6UHfgd-GxWswmnHRcPz5jdbAuqka3Zkz_P-MTvqhw,12767
|
|
25
|
-
sourcecode/change_plan.py,sha256=
|
|
25
|
+
sourcecode/change_plan.py,sha256=wJ896V27a3qb-2vcZhrcOSOqFAwCmEyzbhdbdxM2LlQ,12504
|
|
26
26
|
sourcecode/cir_graphs.py,sha256=RytqSv1WF8kdlfXj2fPQz16LY4akS2kyThbdWL8beCQ,14835
|
|
27
27
|
sourcecode/classifier.py,sha256=JBzPwSSrDG-tUHAbcKB678HRbjLpD-ohzbzzO62mgpo,20114
|
|
28
|
-
sourcecode/cli.py,sha256=
|
|
28
|
+
sourcecode/cli.py,sha256=BV6v8OKXO6mW-lFuc7NX5Z2yPToTZnSRptxr7Qekmic,724322
|
|
29
29
|
sourcecode/client_calls.py,sha256=YOPnfRZzRyqmdHvXSVeiunW8cuiuCuWXacNIQRgKMCw,22640
|
|
30
30
|
sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
|
|
31
|
-
sourcecode/compare.py,sha256=
|
|
31
|
+
sourcecode/compare.py,sha256=Lj8epBqrHLWBhiccE2X0ZbaP2H3hNb7qecoN9Jvsj7c,14794
|
|
32
32
|
sourcecode/confidence_analyzer.py,sha256=5li0NyOdS3Ie-f2ZY1gBmnjtkuRvI50l4OnGaKkXAOU,22358
|
|
33
33
|
sourcecode/constraint_diff.py,sha256=hWIO5vsxvHjsgQx23K3r_JMy1W6wQ2Ca4DqB9R_iMnY,5831
|
|
34
34
|
sourcecode/consumer_join.py,sha256=ERwx9SOu0ZE3NgCBYvB0l67iDLnWMdMpVhlCmbA_3Sw,19618
|
|
@@ -42,7 +42,7 @@ sourcecode/contract_init.py,sha256=bXRCqWM9tvrDLeT8j7ea6IWf87jB4VJxnlSzGBsRRXo,1
|
|
|
42
42
|
sourcecode/contract_model.py,sha256=nRxJKPMs1VHwFTa8AVXhGmaLjti3Lr2sjHDpWgv1bfE,3917
|
|
43
43
|
sourcecode/contract_pipeline.py,sha256=4XE-xhab7ysdExXyUOBl2hAH4MABo7moKnT9V4XcEwQ,30243
|
|
44
44
|
sourcecode/coverage_parser.py,sha256=X9scuUWxacahYTrTBGea2Ku-sf-WCVcnc9OEB35_3IU,19340
|
|
45
|
-
sourcecode/data_exposure.py,sha256=
|
|
45
|
+
sourcecode/data_exposure.py,sha256=PR-kX1UdG4_9A7ZDsKQVrPtos5GNTEMEIkpk9Aveoo0,18018
|
|
46
46
|
sourcecode/data_labels.py,sha256=c_hmgudTE68cVRdywfbfonr7nUWIL-JH2HOXB8Pplv0,8453
|
|
47
47
|
sourcecode/declarations.py,sha256=Jr7oJDg004sFw0TvJySNVggXFT2jB6hQgbeQK2qreVo,7000
|
|
48
48
|
sourcecode/defect_identity.py,sha256=XS2VFDImg9NLaRImybKTeyk-CoDSsXmchldg0dwvM7c,7028
|
|
@@ -64,7 +64,7 @@ sourcecode/error_schema.py,sha256=uwosfNaSujtYm11_732Hu92z5ITV040fQDaIyefSvR4,16
|
|
|
64
64
|
sourcecode/evidence_provider.py,sha256=GSSL44JEaouO5AHks2sB3d1YvC9xIKIld1yBYxZpXxo,4277
|
|
65
65
|
sourcecode/execution_plan.py,sha256=5LyoIsgM5Fg2D1hz-dTD7iqU3xVFS3EyR5yHBlJW5M8,25679
|
|
66
66
|
sourcecode/explain.py,sha256=o1xE-PEWOealHgdT-ZbfpdjHItghzz_HMR1CRCJQySg,31938
|
|
67
|
-
sourcecode/explain_endpoint.py,sha256=
|
|
67
|
+
sourcecode/explain_endpoint.py,sha256=NtAoNs4czD2WbBccWQx9KNHs40MyVEvTvutTIQhmEm4,19796
|
|
68
68
|
sourcecode/field_deprecations.py,sha256=AL-0VWD0rolQQWfJAjLrfiYrhVEHlKuG0xadg1yBckc,7001
|
|
69
69
|
sourcecode/file_chunker.py,sha256=3vkM3mDQ5eE_yTPvUgjyjpGFBIjkW6_mrBmIbrylnA8,16444
|
|
70
70
|
sourcecode/file_classifier.py,sha256=pJCeN9KqWpAwKMCgGP4KDsBjuWMeo4zlbj5fv3hk9dA,15587
|
|
@@ -92,7 +92,7 @@ sourcecode/openapi_surface.py,sha256=BTt0K-woZbkbWTN77IkqeBm_Okag9owR0848fmot8sk
|
|
|
92
92
|
sourcecode/openrewrite_recipe.py,sha256=4dyY5twRB6Xew-G9Zy5FafNZwlMU1S7DnRa2FqPkhpQ,12233
|
|
93
93
|
sourcecode/output_bounds.py,sha256=OtT7_g8ofuWkXRWlEeUSxx9X-CLMckXAbLCzzyG30so,7694
|
|
94
94
|
sourcecode/output_budget.py,sha256=GxoFnbwk0dvqScgmMRPZHj7m-zRIoHLwUKplrH0u89s,13872
|
|
95
|
-
sourcecode/output_ceiling.py,sha256=
|
|
95
|
+
sourcecode/output_ceiling.py,sha256=DZt9BtlMLuCLCwMdnLvFi8qEME2AKhDVuKZR5Ws1bMI,21003
|
|
96
96
|
sourcecode/output_encoding.py,sha256=OxiZQtr7uWBDUtblNQviNHsTH1Xo9AZEpsegPlQWKFk,2139
|
|
97
97
|
sourcecode/output_sink.py,sha256=Eya-PnXxqc3RGulJoLhuC9LUF73SZD0aO5O2B0xAMXU,8762
|
|
98
98
|
sourcecode/parallel.py,sha256=QocwdZoUabdxzd_jEz7f6B2XMsYWFaCFaIl0tIEQs3g,10546
|
|
@@ -105,7 +105,7 @@ sourcecode/phased_run.py,sha256=fZxxiCX-CykagFVR_Fp2Tin3H6FDSV8YRpYGtsRAXj8,1835
|
|
|
105
105
|
sourcecode/pipe_contract.py,sha256=PML0Er5d8uDyec4OrdUXuyqHbGPUYndjeaKUjkFz2u4,8369
|
|
106
106
|
sourcecode/posture.py,sha256=m1sYDLtGvVkvDEVqMxuDvAKz4-KPj0z5Q6x3V_5U-FM,83276
|
|
107
107
|
sourcecode/pr_comment_renderer.py,sha256=239PmJdf95av_ZW236C7_tvq_ahECeuF9AZxrheJ4OQ,15573
|
|
108
|
-
sourcecode/pr_impact.py,sha256=
|
|
108
|
+
sourcecode/pr_impact.py,sha256=taHp0X57tLHyKQ5Qvx7UM1HhX4_csw5t0bgLdN7jYm8,37010
|
|
109
109
|
sourcecode/prepare_context.py,sha256=-FFvr8NgCAHckTp93ZjWEdI3fFURaLkTZiwt3_0eqgg,239532
|
|
110
110
|
sourcecode/product_info.py,sha256=0zjZEutL7iDdp9aVyIumIrNyNn9VWYFc2lIytSbjBP4,727
|
|
111
111
|
sourcecode/progress.py,sha256=naKbWFqf-eTE3vtLsi-eHYILz0p3pxCASAzMmvxUIAI,21643
|
|
@@ -127,7 +127,7 @@ sourcecode/repository_ir.py,sha256=-tV6QuCu56kUaosz4-SgkPdL46DuOlvlRAYxMOhtvXU,4
|
|
|
127
127
|
sourcecode/ris.py,sha256=zlns4esv1tK8VDIYzOzRmw5yM6VLx7oxaPNB_kN3it4,28617
|
|
128
128
|
sourcecode/risk.py,sha256=hXddUjiI-SS_yQ0gZyhufs4lzKwWySOKY4Lj96gL9_A,89733
|
|
129
129
|
sourcecode/risk_model.py,sha256=RoC-G15qZP5BLEwwmcr4eJ0APgvIbh1WTqhTVT-5YFE,10895
|
|
130
|
-
sourcecode/rule_catalog.py,sha256=
|
|
130
|
+
sourcecode/rule_catalog.py,sha256=QCx6ToFTMCtDt5uuX1uBMLuw3b9Cd92gGRsX34kud4U,5328
|
|
131
131
|
sourcecode/rule_pass.py,sha256=DTNxD-nAecMJhIzI1LCd9FyRumInQSZjitacNh4Zoc8,14671
|
|
132
132
|
sourcecode/runs.py,sha256=o4oXkB3WFJGScbK_C10OAWzsE18B9Zc55u_IE8_yl4M,6291
|
|
133
133
|
sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
|
|
@@ -138,9 +138,9 @@ sourcecode/schema_producers.py,sha256=4XzW4wZXnFHOaJT7XenirXGCo0pPZhkdhb7zcDtqFn
|
|
|
138
138
|
sourcecode/schema_registry.py,sha256=ny9yqt_Mwt92TwRRviEXA8jfpeld-xB9KUTOaTiyhZs,20996
|
|
139
139
|
sourcecode/security_chain.py,sha256=1YZVeFLwy65kntMBhMNvz1cUpOoKCmvEqYICgxs66Gw,6823
|
|
140
140
|
sourcecode/security_config.py,sha256=LUppRDYQK0oKM4qdEOLObaeU3frFTS-paEZo7dMgtY8,5467
|
|
141
|
-
sourcecode/security_config_scan.py,sha256=
|
|
142
|
-
sourcecode/security_posture.py,sha256=
|
|
143
|
-
sourcecode/selftest.py,sha256=
|
|
141
|
+
sourcecode/security_config_scan.py,sha256=Wv7QqEx_KbgmhbOGuX7RQfotzkkif43PFU1LohKNWCM,33818
|
|
142
|
+
sourcecode/security_posture.py,sha256=wj6WwvasIh6r-minwBz3oz1T9Zl8SPsPhn7zxDD-yng,55861
|
|
143
|
+
sourcecode/selftest.py,sha256=a-6B-0k8tdVapGlXy0oC4FBxqgHgb0m4kb8oxDknnvI,19109
|
|
144
144
|
sourcecode/semantic_analyzer.py,sha256=xGwQvhU9xeOlZ4N-TPXFZT8HBHx4Rg0nPwu8nDFl00Y,95621
|
|
145
145
|
sourcecode/semantic_impact_engine.py,sha256=t09IirGC3JjQDy33JZd1_WKzQVKXkoNl3-XEUr5kjis,20563
|
|
146
146
|
sourcecode/semantic_integration_engine.py,sha256=ZjyKJKOj-adScWlYBuWx8Ee_P0F4_OImUJVnCYgopuU,20952
|
|
@@ -150,11 +150,11 @@ sourcecode/servlet_surface.py,sha256=CeiebkxLXKojUQxIf7HV7tdQAwLM32ggDUwBnVQIjdo
|
|
|
150
150
|
sourcecode/source_text.py,sha256=R52gmSljz7L6YVPbixv5W-LOZ5eKUVvSUP3S-QH_IWc,9047
|
|
151
151
|
sourcecode/spring_event_topology.py,sha256=5_ON_21Le5zbG-1GRc5GLIi5HJfy_QjcXLVPC5WeUGQ,18055
|
|
152
152
|
sourcecode/spring_findings.py,sha256=cW6NiQAFUM9mDYPitJr6MrQg--YBI41LxrSEMI9aeIQ,25272
|
|
153
|
-
sourcecode/spring_impact.py,sha256=
|
|
153
|
+
sourcecode/spring_impact.py,sha256=yngLmb3ABL9_6b59xSuU6sn2bAJj1MVCGy9dsjgNaic,88539
|
|
154
154
|
sourcecode/spring_model.py,sha256=7GqgcWnHC96ixusRJmD-9tDV93qTdkMh-8e7CBNK0pc,24126
|
|
155
155
|
sourcecode/spring_profiles.py,sha256=-kwrCK0O-MRjrCp6SA1d31t--o4Tg87iqPVrTwxoIUs,19605
|
|
156
156
|
sourcecode/spring_properties.py,sha256=kPTk5qAJxdbHc1hclhHAP0O-hCIzBn8PTh3lzl-I4a4,8400
|
|
157
|
-
sourcecode/spring_security_audit.py,sha256=
|
|
157
|
+
sourcecode/spring_security_audit.py,sha256=TJHQG_Qen_Fr3rLjDbvfZ08rp7LK-nYVBPIx3eG-qq8,69673
|
|
158
158
|
sourcecode/spring_semantic.py,sha256=UrmLg_4gmBVmMqvSnUMgSReVEERVGv3X7LYDBEs6k_o,16598
|
|
159
159
|
sourcecode/spring_tx_analyzer.py,sha256=WBibPgkjQm33kFgERx6zLtp87FpJNE5V_4JI3xZ5zD4,50094
|
|
160
160
|
sourcecode/sql_taint.py,sha256=As1DbEdjXMBFjCTXU5ojksOzuKkP6LmnuMVnzzmckzc,26998
|
|
@@ -240,13 +240,13 @@ sourcecode/telemetry/consent.py,sha256=pQdl-QeLl6Gcibn0eWHSKZrm-HYSsjpVqOnjrgFp8
|
|
|
240
240
|
sourcecode/telemetry/events.py,sha256=4_yeO58U-Cwc1Qb27VB0_EjhmroY0k91n3_VGxeALB8,2776
|
|
241
241
|
sourcecode/telemetry/filters.py,sha256=RzxauTz8HliO4BllQnXEXc7zTeqdCZi5MgqGEDuW7OQ,6570
|
|
242
242
|
sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
|
|
243
|
-
sourcecode/_docs/DEFECT-LEDGER.md,sha256=
|
|
243
|
+
sourcecode/_docs/DEFECT-LEDGER.md,sha256=l9rGV6X-RmLgpWuYBqMcvjjjBx2NHcmdlDH8epSd33A,736756
|
|
244
244
|
sourcecode/_docs/LICENSE,sha256=23-fk61oSWGVmkWyka-FIM0rhAPM2hcYHnsaridL8Uo,1859
|
|
245
245
|
sourcecode/_docs/NOTICE,sha256=Br-AA8Y3iJo1WXa5uP8QBpzqI5nDu3iGLbFyvDt3BOs,321
|
|
246
|
-
sourcecode/_docs/USER_GUIDE.md,sha256=
|
|
247
|
-
sourcecode-5.8.
|
|
248
|
-
sourcecode-5.8.
|
|
249
|
-
sourcecode-5.8.
|
|
250
|
-
sourcecode-5.8.
|
|
251
|
-
sourcecode-5.8.
|
|
252
|
-
sourcecode-5.8.
|
|
246
|
+
sourcecode/_docs/USER_GUIDE.md,sha256=PTMJ0zh_RAXmctfwsNA9-xBNudO62y1Io5rSYlI-Oqs,86740
|
|
247
|
+
sourcecode-5.8.10.dist-info/METADATA,sha256=WEFjTsf2cYQHfik7y8eS6Jy3Pp7JfLyMc2HjbVc-DE8,50810
|
|
248
|
+
sourcecode-5.8.10.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
249
|
+
sourcecode-5.8.10.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
|
|
250
|
+
sourcecode-5.8.10.dist-info/licenses/LICENSE,sha256=23-fk61oSWGVmkWyka-FIM0rhAPM2hcYHnsaridL8Uo,1859
|
|
251
|
+
sourcecode-5.8.10.dist-info/licenses/NOTICE,sha256=Br-AA8Y3iJo1WXa5uP8QBpzqI5nDu3iGLbFyvDt3BOs,321
|
|
252
|
+
sourcecode-5.8.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|