T05 · Unauthorized Access and Privilege Escalation
- Location
src/handler.py:20- Finding
Spoofable Caller Authorization Allows Impersonation of Privileged Operators
- Content
View full analysis
set[str]: raw = os.environ.get("OPENCLAW_ALLOWED_CALLERS", "architect,chief-of-staff") out = set() for part in raw.split(","): v = part.strip() if v: out.add(v) return out def authorize(task: dict) -> tuple[bool, str]: action = str(task.get("action", "")).strip() caller = str(task.get("caller", "")).strip() if not caller: return False, "missing caller" allowed = _allowed_callers() if caller not in allowed: return False, f"caller '{caller}' not authorized" if action in HIGH_RISK_ACTIONS: allow_high_risk = os.environ.get("OPENCLAW_ALLOW_HIGH_RISK", "0") == "1" if not allow_high_risk: return False, f"action '{action}' blocked (set OPENCLAW_ALLOW_HIGH_RISK=1 to enable)" return True, "ok" ``` The task schema also defines `caller` as an ordinary task-controlled string: ```json { "properties": { "task_id": {"type":"string"}, "action": {"type":"string"}, "params": {"type":"object"}, "caller": {"type":"string"}, "correlation_id": {"type":"string"} } } ``` ### Technical Analysis The authorization decision treats the `caller` value embedded in the submitted JSON document as proof of identity. Because the same party submitting or modifying a task can choose this field, an attacker can claim to be any name in `OPENCLAW_ALLOWED_CALLERS`, including the default privileged identities `architect` and `chief-of-staff`. No cryptographic signature, authenticated transport identity, trusted gateway assertion, token, file ownership check, or other mechanism binds the claimed caller to a verified principal. The handler also does not validate the task against `schemas/task.schema.json`, although schema ...[truncated 1814 chars]- Remediation
View remediation
