Files
OpenShell/python/openshell/prover/binary_registry.py
T
Alexander Watson d7f1f8052d feat(prover): add OpenShell Policy Prover (OPP) to Python SDK
Integrate Z3-based formal verification for sandbox policies as
openshell.prover. Detects data exfiltration paths, write bypass
attacks, L7 enforcement gaps, overpermissive methods, and privilege
escalation via binary capability chains.

Closes #699

Signed-off-by: Alexander Watson <zredlined@gmail.com>
2026-03-31 08:31:00 -07:00

172 lines
5.2 KiB
Python

# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Load binary capability descriptors from YAML registry."""
from __future__ import annotations
import fnmatch
from dataclasses import dataclass, field
from enum import Enum
from typing import TYPE_CHECKING
import yaml
if TYPE_CHECKING:
from pathlib import Path
class ActionType(Enum):
READ = "read"
WRITE = "write"
DESTRUCTIVE = "destructive"
@dataclass
class BinaryAction:
name: str
type: ActionType
description: str = ""
@dataclass
class BinaryProtocol:
name: str
transport: str = ""
description: str = ""
bypasses_l7: bool = False
actions: list[BinaryAction] = field(default_factory=list)
@property
def can_write(self) -> bool:
return any(
a.type in (ActionType.WRITE, ActionType.DESTRUCTIVE) for a in self.actions
)
@property
def can_destroy(self) -> bool:
return any(a.type == ActionType.DESTRUCTIVE for a in self.actions)
@dataclass
class BinaryCapability:
path: str
description: str = ""
protocols: list[BinaryProtocol] = field(default_factory=list)
spawns: list[str] = field(default_factory=list)
can_exfiltrate: bool = False
exfil_mechanism: str = ""
can_construct_http: bool = False
@property
def bypasses_l7(self) -> bool:
return any(p.bypasses_l7 for p in self.protocols)
@property
def can_write(self) -> bool:
return any(p.can_write for p in self.protocols) or self.can_construct_http
@property
def can_destroy(self) -> bool:
return any(p.can_destroy for p in self.protocols)
@property
def write_mechanisms(self) -> list[str]:
mechanisms = []
for p in self.protocols:
if p.can_write:
for a in p.actions:
if a.type in (ActionType.WRITE, ActionType.DESTRUCTIVE):
mechanisms.append(f"{p.name}: {a.name}")
if self.can_construct_http:
mechanisms.append("arbitrary HTTP request construction")
return mechanisms
@dataclass
class BinaryRegistry:
binaries: dict[str, BinaryCapability] = field(default_factory=dict)
def get(self, path: str) -> BinaryCapability | None:
return self.binaries.get(path)
def get_or_unknown(self, path: str) -> BinaryCapability:
"""Return known capability or a default 'unknown' descriptor."""
if path in self.binaries:
return self.binaries[path]
# Check glob patterns (e.g., registry has /usr/bin/python* matching /usr/bin/python3.13)
for reg_path, cap in self.binaries.items():
if "*" in reg_path and fnmatch.fnmatch(path, reg_path):
return cap
return BinaryCapability(
path=path,
description="Unknown binary — not in registry",
can_exfiltrate=True, # conservative: assume unknown can exfil
can_construct_http=True, # conservative: assume unknown can make HTTP requests
)
def transitive_spawns(self, path: str, visited: set[str] | None = None) -> set[str]:
"""Return all binaries transitively spawned by this binary."""
if visited is None:
visited = set()
if path in visited:
return set()
visited.add(path)
cap = self.get(path)
if not cap:
return set()
result = set(cap.spawns)
for child in cap.spawns:
result |= self.transitive_spawns(child, visited)
return result
def load_binary_capability(path: Path) -> BinaryCapability:
"""Load a single binary capability descriptor from YAML."""
with open(path) as f: # noqa: PTH123
raw = yaml.safe_load(f)
protocols = []
for p_raw in raw.get("protocols", []):
actions = []
for a_raw in p_raw.get("actions", []):
actions.append(
BinaryAction(
name=a_raw.get("name", ""),
type=ActionType(a_raw.get("type", "read")),
description=a_raw.get("description", ""),
)
)
protocols.append(
BinaryProtocol(
name=p_raw.get("name", ""),
transport=p_raw.get("transport", ""),
description=p_raw.get("description", ""),
bypasses_l7=p_raw.get("bypasses_l7", False),
actions=actions,
)
)
return BinaryCapability(
path=raw.get("binary", ""),
description=raw.get("description", ""),
protocols=protocols,
spawns=raw.get("spawns", []),
can_exfiltrate=raw.get("can_exfiltrate", False),
exfil_mechanism=raw.get("exfil_mechanism", ""),
can_construct_http=raw.get("can_construct_http", False),
)
def load_binary_registry(registry_dir: Path) -> BinaryRegistry:
"""Load all binary capability descriptors from a registry directory."""
binaries = {}
binaries_dir = registry_dir / "binaries"
if binaries_dir.is_dir():
for yaml_file in binaries_dir.glob("*.yaml"):
cap = load_binary_capability(yaml_file)
binaries[cap.path] = cap
return BinaryRegistry(binaries=binaries)