Skip to content

Commit 89b7411

Browse files
committed
refactor(go): state the degradation notice once per batch
A degraded analysis is a property of the whole analysis -- the approximation is somewhere in the SDG and nothing can say which pairs ran through it -- so it is one diagnostic under a key no pair claims, which is taint_verdict's own mechanism for a diagnostic it cannot attribute. The verdict is unchanged (nothing certified, batch not complete); filing per pair said the same thing m*n times, measured at 6 identical copies on a 3x2 batch and 400 on a 40x10. Refs #436
1 parent 5086d63 commit 89b7411

2 files changed

Lines changed: 38 additions & 7 deletions

File tree

‎cldk/analysis/go/backend.py‎

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -551,10 +551,18 @@ def taint(
551551
dsts = [self.resolve_value(name, within=within) for name, within in sinks]
552552
cuts, cut_callables = resolve_sanitizers(sanitizers, resolve_callable=self.resolve_callable, edge_vars_in=self._edge_vars_in)
553553
rows, blocked = self._taint_walk(srcs, dsts, cuts=cuts, cut_callables=cut_callables, depth=depth, max_paths=max_paths)
554-
# A degraded analysis cannot certify an absence (trap 4). The diagnostic is filed under
555-
# *every* requested pair, not under one key of this method's devising: ``taint_verdict``
556-
# reads the keys to decide ``exhausted``, and a key no requested pair claims would cost
557-
# every pair its certification for want of anything able to attribute it.
554+
# A degraded analysis cannot certify an absence (trap 4), and the condition is a property of
555+
# the **whole analysis** rather than of any one pair: the approximation is somewhere in the
556+
# SDG, and nothing here can say which pairs' routes ran through it.
557+
#
558+
# So it is filed under one key that **no requested pair claims**, which is exactly the
559+
# mechanism :func:`~cldk.analysis.commons.graphs.taint_verdict` already has for a diagnostic
560+
# it cannot attribute: an unclaimed key lands in ``unresolved`` and empties ``exhausted``
561+
# for the batch. That is the intended outcome here, not a side effect — a batch-wide
562+
# approximation must cost every pair its certification.
563+
#
564+
# Filing it per pair instead gives the identical verdict and m*n copies of one message (400
565+
# of them on a 40x10 batch, measured), so the single key is the same answer said once.
558566
degraded = self.get_degraded()
559567
if degraded:
560568
codes = sorted({d.code for d in degraded})
@@ -563,11 +571,12 @@ def taint(
563571
message=(
564572
f"this analysis carries {len(degraded)} degradation marker(s) ({', '.join(codes)}), so part of the "
565573
"SDG is approximated: a path returned here is a real may-flow, but the absence of one is not a "
566-
"proof. See get_degraded() for the affected packages and callables."
574+
"proof, and no pair in this batch is certified. See get_degraded() for the affected packages "
575+
"and callables."
567576
),
568577
)
569-
per_pair = {(a.ref, b.ref): [note] for a in srcs for b in dsts}
570-
blocked = {k: list(blocked.get(k, [])) + v for k, v in per_pair.items()} | {k: v for k, v in blocked.items() if k not in per_pair}
578+
blocked = dict(blocked)
579+
blocked[("", "")] = [note]
571580
return taint_verdict(sources, sinks, srcs, dsts, rows=rows, blocked=blocked, depth=depth, max_paths=max_paths)
572581

573582
@abstractmethod

‎tests/analysis/go/test_go_dataflow.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,28 @@ def test_trap4_a_degraded_analysis_cannot_certify_an_absence(backend, monkeypatc
204204
assert found.paths and found.exhausted == []
205205

206206

207+
def test_the_degradation_notice_is_stated_once_per_batch_not_once_per_pair(backend, monkeypatch):
208+
"""The condition is a property of the whole analysis — the approximation is somewhere in the
209+
SDG and nothing can say which pairs ran through it — so it is one diagnostic, filed under a key
210+
no pair claims, which is ``taint_verdict``'s own mechanism for a diagnostic it cannot attribute.
211+
212+
The verdict is identical either way; filing per pair would say the same thing m×n times (400
213+
copies on a 40×10 batch), which buries the ledger a caller is meant to read.
214+
"""
215+
from cldk.models.go import GoDegraded
216+
217+
monkeypatch.setattr(type(backend), "get_degraded", lambda self: [GoDegraded(code="no_vta")])
218+
batch = backend.taint(
219+
sources=[("id", "sqlRepo.Find"), ("id", "sqlRepo.SanitizedFind"), ("user", "sqlRepo.Count")],
220+
sinks=[(SINK_ARG, SINK), ("db", SINK)],
221+
)
222+
assert len(batch.unresolved) == 1, "one batch-wide condition produced more than one diagnostic"
223+
assert batch.unresolved[0].code == "analysis_degraded"
224+
# The verdict is what matters, and it is the strict one: nothing certified, batch not complete.
225+
assert batch.exhausted == [] and batch.complete is False
226+
assert batch.paths, "the witnesses are still reported -- a degraded graph is not a refusal"
227+
228+
207229
# ======================================================================================
208230
# Addressing: the spellings spec §7 requires, and the ambiguities that must raise.
209231
# ======================================================================================

0 commit comments

Comments
 (0)