feat(assessment): verify installed composition evidence
This commit is contained in:
@@ -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",
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
1322
tools/assessments/govoplan_assessment/evidence.py
Normal file
1322
tools/assessments/govoplan_assessment/evidence.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user