diff --git a/continuityos/company_twin_lifecycle.py b/continuityos/company_twin_lifecycle.py index 1c95a418..75d9764b 100644 --- a/continuityos/company_twin_lifecycle.py +++ b/continuityos/company_twin_lifecycle.py @@ -81,6 +81,8 @@ def _authorize_actor(actor: Mapping[str, Any], operation: str) -> None: elif operation == "EXPORT" and actor_kind == "HUMAN": if authority not in {"OWNER", "WORKER", "READ_ONLY"}: raise LifecycleAuthorizationError("unsupported human export authority") + elif operation == "EXPORT": + raise LifecycleAuthorizationError("unsupported export actor kind") def retention_state( diff --git a/tests/test_company_twin_lifecycle.py b/tests/test_company_twin_lifecycle.py index 958155ea..d880833b 100644 --- a/tests/test_company_twin_lifecycle.py +++ b/tests/test_company_twin_lifecycle.py @@ -316,3 +316,19 @@ def test_hold_release_requires_owner(): set_hold( held, hold=False, reason=None, changed_at="2026-01-03T00:00:00Z", changed_by=ROBOT ) + + +@pytest.mark.parametrize("actor_kind,authority", [ + ("ALIEN", "NONSENSE"), + ("SERVICE", "NONE"), + ("", "OWNER"), + ("AGENT", "OWNER"), +]) +def test_export_fail_closed_on_unrecognized_or_unauthorized_actor(actor_kind, authority): + requester = {"actor_id": "untrusted:synthetic", "actor_kind": actor_kind, "authority_class": authority} + with pytest.raises(LifecycleAuthorizationError): + build_export_bundle( + [record("cti_test")], tenant_id="tenant_demo", + authorized_scopes=["team:engineering"], + requested_at="2026-02-01T00:00:00Z", requested_by=requester, + ) diff --git a/tests/test_person_twin_privacy_provenance.py b/tests/test_person_twin_privacy_provenance.py index e08d4119..acb5d658 100644 --- a/tests/test_person_twin_privacy_provenance.py +++ b/tests/test_person_twin_privacy_provenance.py @@ -764,3 +764,24 @@ def test_r2_module_has_no_network_connector_oauth_or_runtime_effect_surface(): "can_trade", ) assert all(token not in text for token in forbidden) + + +@pytest.mark.parametrize("actor_kind,authority", [ + ("ALIEN", "NONSENSE"), + ("SERVICE", "NONE"), + ("", "OWNER"), +]) +def test_person_export_fails_closed_on_unrecognized_actor(actor_kind, authority): + from continuityos.company_twin_lifecycle import LifecycleAuthorizationError + identity, source, consent, ledger, record = _build() + requester = { + "actor_id": "untrusted:synthetic", "actor_kind": actor_kind, "authority_class": authority, + } + with pytest.raises(LifecycleAuthorizationError): + pp.build_person_twin_export_bundle( + [record], identity, authorized_scopes=[record["scope"]], + source_records={source["id"]: source}, + consent_receipts={consent["consent_receipt_id"]: consent}, + current_revocation_ledger=ledger, + purpose="person-memory", requested_at=AT, requested_by=requester, + )