feat(security): inventory runtime supply chain

This commit is contained in:
ogt
2026-07-14 14:34:38 +08:00
parent d4bc3cb453
commit ca8771a210
4 changed files with 560 additions and 5 deletions

View File

@@ -377,6 +377,9 @@ def test_runtime_collection_reports_failed_sources_without_reconciling_them(
async def fake_gitea_bundle():
return [], []
async def fake_gitea_ci():
return [], []
monkeypatch.setattr(asset_scanner_job, "_fetch_kubectl_json", fake_kubectl)
monkeypatch.setattr(
asset_scanner_job,
@@ -398,6 +401,11 @@ def test_runtime_collection_reports_failed_sources_without_reconciling_them(
"_collect_gitea_bundle_assets",
fake_gitea_bundle,
)
monkeypatch.setattr(
asset_scanner_job,
"_collect_gitea_ci_assets",
fake_gitea_ci,
)
assets, relationships, prefixes, errors = asyncio.run(
asset_scanner_job._collect_runtime_assets()
@@ -654,6 +662,103 @@ services:
assert len(registry_relationships) == len(registry_assets)
def test_workload_package_projection_never_pulls_images_or_uses_github() -> None:
assets, relationships = asset_scanner_job._build_workload_package_assets(
{
"metadata": {
"namespace": "awoooi-prod",
"name": "awoooi-api",
"labels": {"system": "awoooi"},
},
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "api",
"image": "192.168.0.110:5000/library/api:abc123",
}
],
"initContainers": [
{
"name": "blocked-source",
"image": "ghcr.io/example/tool@sha256:abcd",
}
],
}
}
},
},
"Deployment",
)
assert len(assets) == 2
assert all(asset["asset_type"] == "package" for asset in assets)
assert assets[0]["metadata"]["image_content_pulled_by_scanner"] is False
assert assets[0]["metadata"]["registry_api_called_by_scanner"] is False
assert assets[0]["metadata"]["forbidden_source_domain_detected"] is None
assert assets[1]["metadata"]["digest_pinned"] is True
assert assets[1]["metadata"]["forbidden_source_domain_detected"] == "ghcr.io"
assert len(relationships) == 2
def test_gitea_ci_projection_requires_gitea_authority_and_github_freeze() -> None:
assets, relationships = asset_scanner_job._build_gitea_ci_assets(
{
"schema_version": "gitea_workflow_runner_health_v1",
"workflow_records": [
{
"workflow_id": "cd",
"display_name": "CD",
"file_ref": ".gitea/workflows/cd.yaml",
"status": "manifest_mapped",
"risk_level": "high",
"triggers": ["push", "workflow_dispatch"],
"runner_labels": ["awoooi-non110-ubuntu"],
"runner_evidence_status": "non110_runner_mapped",
"job_count": 2,
"notify_bridge_calls": 1,
}
],
},
{
"source_control_authority": "gitea",
"status": "gitea_cicd_alert_receipt_chain_ready",
"ready": True,
"operation_boundaries": {"github_api_allowed": False},
"summary": {"workflow_notify_bridge_count": 6},
},
)
assert [asset["asset_type"] for asset in assets] == ["ci_pipeline"]
assert assets[0]["metadata"]["receipt_chain_ready"] is True
assert assets[0]["metadata"]["workflow_triggered_by_scanner"] is False
assert assets[0]["metadata"]["github_api_used"] is False
assert relationships == [
{
"from_key": "gitea/workflow/cd",
"to_key": "gitea/repo/wooo/awoooi",
"relationship_type": "depends_on",
}
]
try:
asset_scanner_job._build_gitea_ci_assets(
{
"schema_version": "gitea_workflow_runner_health_v1",
"workflow_records": [{"workflow_id": "cd"}],
},
{
"source_control_authority": "gitea",
"operation_boundaries": {"github_api_allowed": True},
},
)
except RuntimeError as exc:
assert str(exc) == "ci_github_freeze_not_evidenced"
else:
raise AssertionError("CI projection must fail closed without GitHub freeze")
def test_runtime_collection_integrates_new_asset_types_and_reconciliation_prefixes(
monkeypatch,
) -> None:
@@ -696,7 +801,18 @@ def test_runtime_collection_integrates_new_asset_types_and_reconciliation_prefix
"system": "awoooi",
},
},
"spec": {},
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "web",
"image": "192.168.0.110:5000/library/web:test",
}
]
}
}
},
"status": {},
},
{
@@ -708,7 +824,18 @@ def test_runtime_collection_integrates_new_asset_types_and_reconciliation_prefix
"system": "awoooi",
},
},
"spec": {},
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "api",
"image": "192.168.0.110:5000/library/api:test",
}
]
}
}
},
"status": {},
},
]
@@ -724,7 +851,18 @@ def test_runtime_collection_integrates_new_asset_types_and_reconciliation_prefix
"app.kubernetes.io/component": "log-collector"
},
},
"spec": {},
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "otel-collector",
"image": "otel/collector:0.96.0",
}
]
}
}
},
"status": {},
}
]
@@ -837,6 +975,11 @@ services:
"_collect_gitea_bundle_assets",
empty_collector,
)
monkeypatch.setattr(
asset_scanner_job,
"_collect_gitea_ci_assets",
empty_collector,
)
assets, relationships, prefixes, errors = asyncio.run(
asset_scanner_job._collect_runtime_assets()
@@ -858,6 +1001,7 @@ services:
"dashboard",
"cache",
"message_queue",
"package",
} <= {asset["asset_type"] for asset in assets}
assert {
"k8s/secret-ref/",
@@ -873,6 +1017,8 @@ services:
"service-registry/dashboard/",
"service-registry/cache/",
"service-registry/message_queue/",
"k8s/package/deployment/",
"k8s/package/daemonset/",
} <= set(prefixes)
assert any(
relationship["to_key"] == "k8s/secret-ref/prod/database-ref"

View File

@@ -162,6 +162,14 @@ def test_builds_live_asset_siem_audit_and_ai_control_plane_without_false_closure
assert domain_by_id["siem_soc_threat_intel"]["controlled_percent"] == 50
assert len(payload["siem"]["pipeline"]) == 8
assert payload["security_audit"]["mcp_tool_audit_count_24h"] == 11
assert payload["summary"]["package_digest_unpinned_count"] == 0
assert payload["summary"]["forbidden_github_supply_chain_asset_count"] == 0
assert payload["supply_chain"]["package_inventory_evidenced"] is False
assert payload["supply_chain"]["github_freeze_compliant"] is False
assert payload["supply_chain"]["runtime_image_policy_compliant"] is False
assert payload["supply_chain"]["raw_image_identity_returned"] is False
assert payload["supply_chain"]["registry_api_called"] is False
assert payload["supply_chain"]["github_api_used"] is False
assert any(
item["work_item_id"] == "AIA-P0-006-02" for item in payload["work_items"]
)
@@ -177,6 +185,71 @@ def test_builds_live_asset_siem_audit_and_ai_control_plane_without_false_closure
assert 'secret_value_collection_allowed": true' not in public_text.lower()
def test_runtime_image_policy_gaps_are_visible_and_never_false_green() -> None:
now = datetime(2026, 7, 11, 4, 0, tzinfo=UTC)
payload = build_iwooos_security_asset_control_plane(
discovery_row=_row(
latest_status="success",
latest_scan_depth="full",
latest_started_at=now - timedelta(minutes=10),
latest_ended_at=now - timedelta(minutes=9),
latest_success_ended_at=now - timedelta(minutes=9),
latest_total_assets=31,
),
inventory_rows=[
_row(
asset_type="package",
cnt=31,
unowned_count=0,
stale_count=0,
forbidden_github_source_count=3,
unpinned_package_count=31,
),
],
coverage_rows=[],
relationship_row=_row(relationship_count=31, orphan_asset_count=0),
compliance_rows=[],
automation_rows=[],
ai_trace_row=_row(trace_count=0, accepted_trace_count=0),
siem_row=_row(),
audit_row=_row(),
generated_at=now,
)
assert payload["summary"]["package_digest_unpinned_count"] == 31
assert payload["summary"]["forbidden_github_supply_chain_asset_count"] == 3
assert payload["supply_chain"] == {
"package_asset_count": 31,
"package_inventory_evidenced": True,
"package_digest_pinned_count": 0,
"package_digest_unpinned_count": 31,
"forbidden_github_supply_chain_asset_count": 3,
"github_freeze_compliant": False,
"runtime_image_policy_compliant": False,
"raw_image_identity_returned": False,
"registry_api_called": False,
"github_api_used": False,
}
work_item = next(
item
for item in payload["work_items"]
if item["work_item_id"] == "AIA-P0-006-02A"
)
assert work_item["gap_count"] == 34
assert work_item["status"] == "open"
domains = {
domain["domain_id"]: domain for domain in payload["security_program_domains"]
}
assert domains["software_supply_chain"]["gap_code"] == (
"forbidden_github_supply_chain_source_detected"
)
assert domains["software_supply_chain"]["status"] != "controlled"
public_text = json.dumps(payload, ensure_ascii=False).lower()
assert "ghcr.io/" not in public_text
assert "raw.githubusercontent.com/" not in public_text
def test_unknown_coverage_and_compliance_create_p0_work_items() -> None:
now = datetime(2026, 7, 11, 4, 0, tzinfo=UTC)
payload = build_iwooos_security_asset_control_plane(
@@ -413,6 +486,11 @@ def test_service_bounds_source_concurrency_for_database_budget(monkeypatch) -> N
)
assert "ORDER BY snapshot_id DESC" in compliance_query
assert "LIMIT 100000" in compliance_query
inventory_query = next(
statement for statement in statements if "FROM asset_inventory" in statement
)
assert "forbidden_source_domain_detected" in inventory_query
assert "digest_pinned" in inventory_query
def test_public_api_returns_live_aggregate(monkeypatch) -> None: