feat(assessment): verify installed composition evidence

This commit is contained in:
2026-07-22 18:34:02 +02:00
parent d0dc916837
commit 6c0b003dee
8 changed files with 2785 additions and 44 deletions

View File

@@ -1,5 +1,10 @@
"""Repeatable GovOPlaN product-assessment tooling."""
from .capability_fit import review_capability_fit, render_review
from .evidence import collect_installed_composition
__all__ = ("render_review", "review_capability_fit")
__all__ = (
"collect_installed_composition",
"render_review",
"review_capability_fit",
)

View File

@@ -25,6 +25,13 @@ import govoplan_core.core.module_package_catalog as module_package_catalog
from govoplan_release.source_provenance import catalog_source_selection
from govoplan_release.version_alignment import candidate_catalog_version_issues
from .evidence import (
BoundaryEvidenceReview,
InstalledEvidenceReview,
review_boundary_evidence,
review_installed_composition,
)
RELEASE_REF_PATTERN = re.compile(
r"^(?P<channel>[a-z][a-z0-9_-]*)-catalog-(?P<sequence>[0-9]+)$"
@@ -49,6 +56,13 @@ def review_capability_fit(
published_keyring: dict[str, Any],
trusted_keyring: dict[str, Any],
workspace_root: Path | None = None,
installed_evidence: dict[str, Any] | None = None,
installed_evidence_schema: dict[str, Any] | None = None,
boundary_evidence: dict[str, Any] | None = None,
boundary_evidence_schema: dict[str, Any] | None = None,
boundary_authority_keyring: dict[str, Any] | None = None,
boundary_authority_keyring_schema: dict[str, Any] | None = None,
verification_time: datetime | None = None,
) -> dict[str, Any]:
"""Return a secret-free release-drift report for one fit assessment."""
@@ -68,6 +82,8 @@ def review_capability_fit(
local_tag_provenance_attempted=0,
local_tag_provenance_expected=0,
local_tag_catalog_scope_complete=False,
installed_review=None,
boundary_review=None,
)
catalog_validation = validate_catalog(
@@ -340,11 +356,47 @@ def review_capability_fit(
)
)
installed_review = review_installed_composition(
assessment=assessment,
catalog_entries=catalog_entries,
selected_versions=source_selection.selected_versions,
selected_commits=source_selection.selected_commits,
evidence=installed_evidence,
schema=installed_evidence_schema,
)
findings.extend(
Finding(item.severity, item.code, item.message, item.assessment_ids)
for item in installed_review.findings
)
changes.extend(installed_review.changes)
changed_repositories.update(installed_review.changed_repositories)
boundary_review = review_boundary_evidence(
assessment=assessment,
installed_review=installed_review,
evidence=boundary_evidence,
evidence_schema=boundary_evidence_schema,
authority_keyring=boundary_authority_keyring,
authority_keyring_schema=boundary_authority_keyring_schema,
verification_time=verification_time,
)
findings.extend(
Finding(item.severity, item.code, item.message, item.assessment_ids)
for item in boundary_review.findings
)
review_targets = conclusions_needing_review(
assessment=assessment,
changed_repositories=changed_repositories,
release_changed=assessed_sequence != catalog_sequence,
)
review_targets = merge_review_targets(
review_targets,
installed_evidence_review_targets(
assessment=assessment,
installed_review=installed_review,
),
)
return build_report(
assessment=assessment,
catalog=catalog,
@@ -355,6 +407,8 @@ def review_capability_fit(
local_tag_provenance_attempted=local_tag_provenance_attempted,
local_tag_provenance_expected=local_tag_provenance_expected,
local_tag_catalog_scope_complete=local_tag_catalog_scope_complete,
installed_review=installed_review,
boundary_review=boundary_review,
)
@@ -512,6 +566,7 @@ def catalog_entries_by_id(
entries[module_id] = {
"module_id": module_id,
"repository": catalog_repository(item),
"package": catalog_package(item),
"version": _text(item.get("version")),
"tags": tuple(
tag for tag in (catalog_ref_tag(value) for value in refs) if tag
@@ -534,6 +589,11 @@ def catalog_repository(entry: dict[str, Any]) -> str | None:
return None
def catalog_package(entry: dict[str, Any]) -> str | None:
package = _text(entry.get("python_package"))
return package.split("[", 1)[0].strip() if package else None
def catalog_ref_tag(value: str) -> str | None:
match = TAG_PATTERN.search(value)
return match.group("tag") if match else None
@@ -709,6 +769,51 @@ def conclusions_needing_review(
return [targets[key] for key in sorted(targets)]
def installed_evidence_review_targets(
*,
assessment: dict[str, Any],
installed_review: InstalledEvidenceReview,
) -> list[dict[str, Any]]:
if not installed_review.findings:
return []
targets: dict[str, dict[str, Any]] = {
"assessment.installed_composition": {
"id": "assessment.installed_composition",
"section": "installed_composition",
"reason": "Installed-composition evidence is incomplete, mutable, extra, missing, or inconsistent with the assessed release.",
}
}
components = {
str(item.get("module_id")): item
for item in assessment.get("composition", [])
if isinstance(item, dict)
}
for module_id in sorted(installed_review.affected_module_ids):
component = components.get(module_id)
target_id = f"composition.{module_id}"
targets[target_id] = {
"id": target_id,
"section": "composition",
"repositories": [component.get("repository")]
if component is not None
else [],
"reason": "The observed installed distribution, manifest, RECORD integrity, or immutable provenance differs from the assessed composition.",
}
return [targets[key] for key in sorted(targets)]
def merge_review_targets(
*groups: Iterable[dict[str, Any]],
) -> list[dict[str, Any]]:
merged: dict[str, dict[str, Any]] = {}
for group in groups:
for item in group:
item_id = str(item.get("id") or "")
if item_id and item_id not in merged:
merged[item_id] = item
return [merged[key] for key in sorted(merged)]
def build_report(
*,
assessment: dict[str, Any],
@@ -720,6 +825,8 @@ def build_report(
local_tag_provenance_attempted: int,
local_tag_provenance_expected: int,
local_tag_catalog_scope_complete: bool,
installed_review: InstalledEvidenceReview | None,
boundary_review: BoundaryEvidenceReview | None,
) -> dict[str, Any]:
finding_list = sorted(
findings,
@@ -736,7 +843,9 @@ def build_report(
)
catalog_valid = trusted_signature_valid and published_keyring_valid
release_valid = catalog_valid and not any(
item.severity in {"blocker", "review"} for item in finding_list
item.severity in {"blocker", "review"}
and not item.code.startswith(("installed_", "boundary_"))
for item in finding_list
)
tag_provenance_checked = (
local_tag_provenance_expected > 0
@@ -752,8 +861,54 @@ def build_report(
status = "review_required"
else:
status = "current"
installed_scope = (
installed_review.proof_scope
if installed_review is not None
else {
"installed_artifacts": {"checked": False, "valid": None},
"installed_record_integrity": {"checked": False, "valid": None},
"installed_source_provenance": {"checked": False, "valid": None},
"runtime_activation": {"checked": False, "valid": None},
}
)
boundary_scope = (
boundary_review.proof_scope
if boundary_review is not None
else {
"target_environment": {"checked": False, "valid": None},
"external_providers": {"checked": False, "valid": None},
"production_approval": {"checked": False, "valid": None},
}
)
proof_scope = {
"assessment_schema": {"checked": True, "valid": schema_valid},
"catalog_signature_and_keyring": {
"checked": catalog_checked,
"valid": catalog_valid if catalog_checked else None,
},
"catalog_signature_and_trusted_keyring": {
"checked": catalog_checked,
"valid": trusted_signature_valid if catalog_checked else None,
},
"published_keyring_hash": {
"checked": catalog_checked,
"valid": published_keyring_valid if catalog_checked else None,
},
"release_metadata": {
"checked": catalog_checked,
"valid": release_valid if catalog_checked else None,
},
"local_tag_provenance": {
"checked": tag_provenance_checked,
"valid": tag_provenance_valid if tag_provenance_checked else None,
"attempted_count": local_tag_provenance_attempted,
"expected_count": local_tag_provenance_expected,
},
**installed_scope,
**boundary_scope,
}
return {
"report_version": "0.1.0",
"report_version": "0.2.0",
"status": status,
"assessment_id": assessment.get("assessment_id"),
"assessment_release": assessment.get("release", {}).get("ref")
@@ -764,35 +919,7 @@ def build_report(
"sequence": catalog.get("sequence"),
"generated_at": catalog.get("generated_at"),
},
"proof_scope": {
"assessment_schema": {"checked": True, "valid": schema_valid},
"catalog_signature_and_keyring": {
"checked": catalog_checked,
"valid": catalog_valid if catalog_checked else None,
},
"catalog_signature_and_trusted_keyring": {
"checked": catalog_checked,
"valid": trusted_signature_valid if catalog_checked else None,
},
"published_keyring_hash": {
"checked": catalog_checked,
"valid": published_keyring_valid if catalog_checked else None,
},
"release_metadata": {
"checked": catalog_checked,
"valid": release_valid if catalog_checked else None,
},
"local_tag_provenance": {
"checked": tag_provenance_checked,
"valid": tag_provenance_valid if tag_provenance_checked else None,
"attempted_count": local_tag_provenance_attempted,
"expected_count": local_tag_provenance_expected,
},
"installed_artifacts": {"checked": False, "valid": None},
"target_environment": {"checked": False, "valid": None},
"external_providers": {"checked": False, "valid": None},
"production_approval": {"checked": False, "valid": None},
},
"proof_scope": proof_scope,
"findings": [asdict(item) for item in finding_list],
"changes": sorted(
changes,
@@ -803,11 +930,22 @@ def build_report(
def render_review(report: dict[str, Any]) -> str:
installed_checked = (
report.get("proof_scope", {}).get("installed_artifacts", {}).get("checked")
is True
)
lines = [
f"Capability fit rerun: {report['status']}",
f"Assessment: {report.get('assessment_id') or '-'} ({report.get('assessment_release') or '-'})",
f"Catalog: {report['catalog'].get('channel') or '-'} sequence {report['catalog'].get('sequence') or '-'}",
"Scope: schema, signed release metadata, and optional local tag provenance only.",
(
"Scope: schema, signed release metadata, optional local tag provenance, "
+ (
"and supplied installed-composition evidence."
if installed_checked
else "and no installed-composition evidence."
)
),
]
findings = report.get("findings") or []
if findings:
@@ -822,9 +960,23 @@ def render_review(report: dict[str, Any]) -> str:
if targets:
lines.append("Conclusions requiring review:")
lines.extend(f"- {item['id']}: {item['reason']}" for item in targets)
lines.append(
"No installed-artifact, target-provider, or production proof was performed."
)
unchecked_boundaries = [
label
for key, label in (
("target_environment", "target-environment"),
("external_providers", "external-provider"),
("production_approval", "production-approval"),
)
if report.get("proof_scope", {}).get(key, {}).get("checked") is not True
]
if not installed_checked:
unchecked_boundaries.insert(0, "installed-artifact")
if not installed_checked and len(unchecked_boundaries) == 4:
lines.append(
"No installed-artifact, target-provider, or production proof was performed."
)
elif unchecked_boundaries:
lines.append("No " + ", ".join(unchecked_boundaries) + " proof was performed.")
return "\n".join(lines) + "\n"

File diff suppressed because it is too large Load Diff