From b5e8d4ec3fab9cfea27276e120086cd77709a676 Mon Sep 17 00:00:00 2001 From: Beon de Nood Date: Fri, 17 Apr 2026 00:31:32 -0400 Subject: [PATCH 1/2] fix(security): fix trust level mapping and get_agent stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GA-007: Fix stubs and trust level mapping - TRUST_LEVEL_UNSPECIFIED (proto enum 0) now maps to '0' (none) instead of '1' (DV). Previous mapping silently elevated unverified badges to domain-verified trust level. - Implement RegistryClient.get_agent() — was silently returning (None, 'not yet implemented'). Now returns agent dict or error. --- capiscio_sdk/_rpc/client.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/capiscio_sdk/_rpc/client.py b/capiscio_sdk/_rpc/client.py index 21e8dc3..110396c 100644 --- a/capiscio_sdk/_rpc/client.py +++ b/capiscio_sdk/_rpc/client.py @@ -1760,8 +1760,17 @@ def get_agent(self, did: str) -> tuple[Optional[dict], Optional[str]]: if response.error_message: return None, response.error_message - # TODO: Convert response.agent to dict - return None, "not yet implemented" + agent = response.agent + if not agent or not agent.did: + return None, "agent not found" + + return { + "id": agent.id, + "did": agent.did, + "name": agent.name, + "domain": agent.domain, + "status": agent.status, + }, None def _claims_to_dict(claims) -> dict: @@ -1770,9 +1779,10 @@ def _claims_to_dict(claims) -> dict: return {} # Map proto enum to human-readable trust level string - # Note: UNSPECIFIED (0) defaults to "1" (DV) for compatibility + # SECURITY: UNSPECIFIED (0) MUST map to "0" (none), never to a verified level. + # Mapping UNSPECIFIED to DV would silently elevate unverified badges. trust_level_map = { - 0: "1", # TRUST_LEVEL_UNSPECIFIED -> default to DV (1) + 0: "0", # TRUST_LEVEL_UNSPECIFIED -> none/unverified 1: "0", # TRUST_LEVEL_SELF_SIGNED (Level 0) 2: "1", # TRUST_LEVEL_DV (Level 1) 3: "2", # TRUST_LEVEL_OV (Level 2) From a36ee24afb1005f7f47d9296ba4e4c47d78997b5 Mon Sep 17 00:00:00 2001 From: Beon de Nood Date: Fri, 17 Apr 2026 00:49:23 -0400 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20address=20Copilot=20review=20?= =?UTF-8?q?=E2=80=94=20proto=20fields,=20include=5Fbadge,=20comment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - get_agent(): use actual RegisteredAgent proto fields (did, name, description, status, agent_card_json, capabilities, tags) instead of non-existent 'id' and 'domain' fields - get_agent(): set include_badge=True and derive domain from badge - _claims_to_dict(): clarify UNSPECIFIED is coerced to Level 0 (lowest trust level), not a separate 'none' concept --- capiscio_sdk/_rpc/client.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/capiscio_sdk/_rpc/client.py b/capiscio_sdk/_rpc/client.py index 110396c..bd7994c 100644 --- a/capiscio_sdk/_rpc/client.py +++ b/capiscio_sdk/_rpc/client.py @@ -1754,7 +1754,7 @@ def ping(self) -> dict: def get_agent(self, did: str) -> tuple[Optional[dict], Optional[str]]: """Get an agent by DID.""" - request = registry_pb2.GetAgentRequest(did=did) + request = registry_pb2.GetAgentRequest(did=did, include_badge=True) response = self._stub.GetAgent(request) if response.error_message: @@ -1764,13 +1764,21 @@ def get_agent(self, did: str) -> tuple[Optional[dict], Optional[str]]: if not agent or not agent.did: return None, "agent not found" - return { - "id": agent.id, + result = { "did": agent.did, "name": agent.name, - "domain": agent.domain, + "description": agent.description, "status": agent.status, - }, None + "agent_card_json": agent.agent_card_json, + "capabilities": list(agent.capabilities), + "tags": list(agent.tags), + } + + # Include badge domain if badge is present + if agent.badge and agent.badge.domain: + result["domain"] = agent.badge.domain + + return result, None def _claims_to_dict(claims) -> dict: @@ -1779,10 +1787,11 @@ def _claims_to_dict(claims) -> dict: return {} # Map proto enum to human-readable trust level string - # SECURITY: UNSPECIFIED (0) MUST map to "0" (none), never to a verified level. - # Mapping UNSPECIFIED to DV would silently elevate unverified badges. + # SECURITY: UNSPECIFIED (0) is coerced to Level 0 (self-signed / unverified). + # This is the lowest trust level. Mapping it higher would silently elevate + # unverified badges. trust_level_map = { - 0: "0", # TRUST_LEVEL_UNSPECIFIED -> none/unverified + 0: "0", # TRUST_LEVEL_UNSPECIFIED -> coerced to Level 0 (lowest) 1: "0", # TRUST_LEVEL_SELF_SIGNED (Level 0) 2: "1", # TRUST_LEVEL_DV (Level 1) 3: "2", # TRUST_LEVEL_OV (Level 2)