Back to skill

Security audit

War/Den Governance

Security checks for vulnerabilities and agentic risk

Overview

This governance skill is not clearly malicious, but it needs review because its cache can bypass required approvals and its logging can retain or send full action details without redaction.

Review this carefully before installing. Use only least-privileged OpenClaw accounts, avoid enabling enterprise API keys until raw-payload logging and cloud disclosure are fixed, and disable or patch the ALLOW decision cache before relying on this skill to enforce approvals for deletes, payments, or other high-impact actions.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
warden_governance/sentinel_client.py:34
Finding
Governance decisions can be bypassed through an under-scoped authorization cache<![CDATA[ ## Vulnerability Details **File Location**: `warden_governance/sentinel_client.py:34-48, 107-111`; related policies in `policies/openclaw_default.yaml:11-24, 27-40, 51-60` **Vulnerability Type**: Authorization decision cache confusion **Risk Level**: Critical ### Vulnerable Code ```python def get(self, action_type: str, env: str) -> CheckResult | None: key = f"{action_type}:{env}" entry = self._cache.get(key) if entry is None: self._misses += 1 return None if time.time() - entry["cached_at"] > self._ttl: del self._cache[key] self._misses += 1 return None self._hits += 1 return entry["result"] def set(self, action_type: str, env: str, result: CheckResult) -> None: if result.decision != Decision.ALLOW: return key = f"{action_type}:{env}" self._cache[key] = {"result": result, "cached_at": time.time()} ``` The cached result is returned before policy evaluation: ```python env = action.context.get("env", "dev") cached = self.cache.get(action.type.value, env) if cached is not None: return cached ``` The affected policies distinguish operations using fields that are absent from the cache key: ```yaml - name: protect-email-delete match: action.type: data.write action.data.openclaw_original: email.delete decision: review - name: protect-file-delete match: action.type: data.write action.data.openclaw_original: file.delete decision: review - name: review-payments match: action.type: api.call action.data.openclaw_original: payment.create decision: review ``` ### Technical Analysis The cache key contains only the normalized action type and environment. However, policy evaluation also depends on the original OpenClaw action, action data, service, data classification, agent identity, and arbitrary context fields. Several different operations are normalized to the same governance type. For example, `file.write`, `email.delete`, `fi ...[truncated 1475 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not cache authorization decisions unless cache equivalence can be proven. - Prefer disabling decision caching for actions that write, delete, execute code, send messages, or create payments. - If caching is retained, derive the key from a canonical serialization of every policy-relevant field: - normalized action type; - complete action data; - complete context; - agent identity; - policy file and policy-pack version; - governance mode and tenant identity. - Hash the canonical representation to avoid oversized dictionary keys. - Clear the cache whenever policies or policy packs change. - Consider limiting caching to explicit policies marked as safe to cache. - Add regression tests in which a benign `file.write` is followed by `email.delete`, `file.delete`, and `calendar.delete`. - Add an equivalent test in which an ordinary API call is followed by `payment.create`. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
warden_governance/audit_log.py:52
Finding
Audit hash chain excludes security-relevant event fields<![CDATA[ ## Vulnerability Details **File Location**: `warden_governance/audit_log.py:52-83, 111-126` **Vulnerability Type**: Incomplete integrity protection **Risk Level**: High ### Vulnerable Code The database stores action data, context, reasons, and policy identifiers: ```python action_data = json.dumps(action.data, sort_keys=True) context = json.dumps(action.context, sort_keys=True) prev_hash = self._get_last_hash() hash_input = ( f"{prev_hash}{action.agent_id}{action.type.value}" f"{decision.value}{timestamp}" ) event_hash = hashlib.sha256(hash_input.encode()).hexdigest() with sqlite3.connect(self.db_path) as conn: conn.execute( """ INSERT INTO audit_log (id, prev_hash, agent_id, action_type, action_data, context, decision, reason, policy_id, hash, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( event_id, prev_hash, action.agent_id, action.type.value, action_data, context, decision.value, reason, policy_id, event_hash, timestamp, ), ) ``` Verification protects only the same incomplete subset: ```python prev_hash = "" for row in rows: expected_input = ( f"{prev_hash}{row['agent_id']}{row['action_type']}" f"{row['decision']}{row['timestamp']}" ) expected_hash = hashlib.sha256(expected_input.encode()).hexdigest() if row["hash"] != expected_hash: return False, row["id"] if row["prev_hash"] != prev_hash: return False, row["id"] prev_hash = row["hash"] ``` ### Technical Analysis The event hash includes only the previous hash, agent ID, normalized action type, decision, and timestamp. It excludes: - event ID; - complete action data; - action context; - decision reason; - matched policy ID. These excluded fields are essential to understanding what operation occurred, its tar ...[truncated 1514 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Build a canonical event object containing every stored field except the final hash. - Serialize it deterministically, for example with sorted JSON keys and explicit separators. - Hash the previous hash together with that canonical serialization. - Include the event ID, action data, context, decision, reason, policy ID, agent ID, action type, and timestamp. - Add unambiguous length framing or canonical JSON instead of raw string concatenation. - Use an HMAC key stored outside the database, or a digital signature, when records must remain authentic against an attacker with database write access. - Protect signature keys using an operating-system key store or dedicated secrets manager. - Add tests that alter each database column individually and assert that verification fails. - Document the distinction between hash-chain consistency and cryptographically authenticated audit records. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
warden_governance/skill.py:90
Finding
Complete action, result, and context objects are transmitted to cloud memory without redaction<![CDATA[ ## Vulnerability Details **File Location**: `warden_governance/skill.py:90-99`; transmission occurs in `warden_governance/engramport_client.py:58-77` **Vulnerability Type**: Excessive collection and remote disclosure of sensitive data **Risk Level**: High ### Vulnerable Code The post-action hook records the complete objects rather than a minimal event summary: ```python def after_action(self, action: dict, result: dict, context: dict) -> None: """Hook: write action result to governed memory.""" try: self.memory.write( content=f"action={action.get('type')} status={result.get('status')}", namespace="openclaw_actions", metadata={ "action": action, "result": result, "context": context, }, ) except Exception as exc: logger.warning("War/Den after_action memory write failed: %s", exc) ``` When enterprise memory is enabled, the complete metadata object is serialized into the remote request: ```python url = f"{self.base_url}/remember" payload = { "content": content, "context": json.dumps(metadata), "session_id": namespace, } try: with httpx.Client(timeout=30) as client: resp = client.post( url, json=payload, headers={ "X-API-Key": self.api_key, "Content-Type": "application/json", }, ) ``` ### Technical Analysis When `ENGRAMPORT_API_KEY` is configured, `MemoryClient` selects `EngramPortClient`. Every successful action then causes `after_action()` to copy the complete raw action, action result, and execution context into metadata. `EngramPortClient.write()` serializes that metadata and transmits it to the configured cloud-memory endpoint. There is no field allowlist, recursive secret redaction, payload-size limit, or sensitivity classification. Depending on the OpenClaw action schema, these objects may contain: - ...[truncated 1662 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace raw object logging with an explicit allowlist containing only: - action type; - non-sensitive action identifier; - status; - timestamp; - policy or audit correlation ID. - Recursively redact keys such as `authorization`, `token`, `api_key`, `cookie`, `password`, `secret`, and private-key material. - Exclude message bodies, file contents, request bodies, and response bodies by default. - Introduce configurable logging profiles such as `minimal`, `metadata`, and `full`, with `minimal` as the default. - Require explicit informed consent before enabling full-payload cloud retention. - Apply payload-size limits and reject non-JSON-safe or unexpectedly nested objects. - Provide retention controls and deletion mechanisms. - Clearly document which fields are sent to each external service. - Add tests proving that nested credentials and authorization headers are removed before transmission. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
warden_governance/audit_log.py:25
Finding
Sensitive audit and memory records are stored in plaintext without enforced filesystem permissions<![CDATA[ ## Vulnerability Details **File Location**: `warden_governance/audit_log.py:25-33, 52-83`; `warden_governance/local_store.py:21-28, 47-77` **Vulnerability Type**: Insecure local storage of sensitive information **Risk Level**: Medium ### Vulnerable Code The audit database and its parent directory are created without explicit restrictive modes: ```python def __init__(self, db_path: str = "~/.warden/audit.db"): self.db_path = os.path.expanduser(db_path) os.makedirs(os.path.dirname(self.db_path), exist_ok=True) self._init_db() def _init_db(self) -> None: with sqlite3.connect(self.db_path) as conn: conn.execute(""" CREATE TABLE IF NOT EXISTS audit_log ( id TEXT PRIMARY KEY, prev_hash TEXT, agent_id TEXT, action_type TEXT, action_data TEXT, context TEXT, decision TEXT, reason TEXT, policy_id TEXT, hash TEXT, timestamp TEXT ) """) ``` Action and context data are stored as plaintext JSON: ```python action_data = json.dumps(action.data, sort_keys=True) context = json.dumps(action.context, sort_keys=True) ``` The memory database uses the same creation pattern: ```python def __init__(self, config: Settings): self.db_path = os.path.expanduser(config.warden_memory_db) os.makedirs(os.path.dirname(self.db_path), exist_ok=True) self._init_db() def _init_db(self) -> None: with sqlite3.connect(self.db_path) as conn: conn.execute(""" CREATE TABLE IF NOT EXISTS memories ( memory_id TEXT PRIMARY KEY, bot_id TEXT NOT NULL, namespace TEXT NOT NULL, content TEXT NOT NULL, metadata TEXT, created_at TEXT, ttl_days INTEGER, expires_at TEXT ) """) ``` Memory co ...[truncated 2184 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create storage directories with mode `0700`. - Create database files with mode `0600` and verify permissions after opening them. - Refuse or warn when a configured database path is in a world-readable or group-readable directory. - Avoid storing complete action payloads; apply the same minimization and redaction controls used for remote logging. - Provide encryption at rest for sensitive deployments, with keys stored separately from the database files. - Define and enforce retention limits for audit and memory records. - Secure SQLite sidecar files, including WAL and shared-memory files, under the same permissions. - Add automated tests that verify directory, database, WAL, and shared-memory permissions. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (29)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def test_before_action_blocks_deny(self, tmp_path):
        skill = _make_skill(tmp_path, WARDEN_POLICY_PACKS="basic_safety")
        result = skill.before_action(
            {"type": "shell.execute", "data": {"command": "rm -rf /"}},
            {"agent_id": "bot-1", "env": "prod", "user": "u"},
        )
        assert result["proceed"] is False
Confidence
100% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def test_before_action_blocks_deny(self, tmp_path):
        skill = _make_skill(tmp_path, WARDEN_POLICY_PACKS="basic_safety")
        result = skill.before_action(
            {"type": "shell.execute", "data": {"command": "rm -rf /"}},
            {"agent_id": "bot-1", "env": "prod", "user": "u"},
        )
        assert result["proceed"] is False
Confidence
100% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def test_before_action_blocks_deny(self, tmp_path):
        skill = _make_skill(tmp_path, WARDEN_POLICY_PACKS="basic_safety")
        result = skill.before_action(
            {"type": "shell.execute", "data": {"command": "rm -rf /"}},
            {"agent_id": "bot-1", "env": "prod", "user": "u"},
        )
        assert result["proceed"] is False
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def test_full_action_flow_deny(self, tmp_path):
        skill = _make_skill(tmp_path, WARDEN_POLICY_PACKS="basic_safety")
        result = skill.before_action(
            {"type": "code.execute", "data": {"code": "import os; os.system('rm -rf /')"}},
            {"agent_id": "bot-1", "env": "prod", "user": "u"},
        )
        assert result["proceed"] is False
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README describes enterprise features and endpoints but does not prominently warn users that enabling `SENTINEL_API_KEY` and/or `ENGRAMPORT_API_KEY` causes governance events, action metadata, and memory content to be transmitted to external cloud services. For a governance skill that may process sensitive emails, files, API calls, and memory, this omission can lead to unintentional data disclosure and misinformed deployment decisions.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises hooks that can govern every action and references capabilities including environment-variable access, networked enterprise clients, and shell-based installation/testing, but it declares no explicit tool scope or permission boundaries. In a security-sensitive governance skill, missing capability declarations increases the chance of overbroad execution or accidental exposure of privileged operations because operators cannot easily constrain what the skill is allowed to do.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The upgrade guide instructs users to enable Sentinel_OS cloud governance and later EngramPort cloud memory, but it does not clearly warn that governance events, action metadata, and potentially memory content may be transmitted to external third-party services. In a governance skill, users may reasonably assume policy enforcement and audit data remain local unless explicitly disclosed, so the omission creates a real security and privacy risk.

External Transmission

Medium
Category
Data Exfiltration
Content
1. Register at [engram.eideticlab.com](https://engram.eideticlab.com) or via API:

```bash
curl -X POST https://mandeldb.com/api/v1/portal/register \
  -H "Content-Type: application/json" \
  -d '{"bot_name": "my-openclaw-bot", "bot_type": "assistant"}'
```
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Vague Triggers

Medium
Confidence
84% confidence
Finding
This is a JSON manifest file, so vague-trigger review applies. The description says 'Govern your OpenClaw bot with War/Den' and 'Every action evaluated before executing,' but it does not clearly specify the exact activation scope, when the skill intercepts actions, or any exclusion conditions, which could lead to overly broad or unintended invocation expectations.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The tests explicitly codify behavior where an unrecognized action type is silently mapped to API_CALL. In a governance or policy-enforcement component, fail-open handling of unknown actions is dangerous because new or malformed action types may bypass stricter classifications, risk labeling, or review logic that would otherwise apply.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The serializer sends the entire action payload and full context object to the governance endpoint without any minimization, filtering, or redaction. If those structures contain secrets, personal data, memory contents, or other sensitive metadata, the bridge will disclose them to another system unnecessarily, expanding the data exposure surface during every governance check.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The export() method returns the complete audit log contents, including action_data, context, reason, and policy metadata, without any access control, redaction, or warning. In this skill context, audit entries are likely to contain sensitive operational and governance data, so any caller that can invoke export() can exfiltrate potentially confidential information.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The client sends arbitrary memory content and serialized metadata to an external service over the network, but this code provides no consent gate, policy enforcement, or data-classification check before exfiltrating potentially sensitive data. In a governance or memory component, users may reasonably expect local handling unless remote transfer is explicit, making this a real privacy and compliance risk rather than a harmless implementation detail.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The `delete` method issues an authenticated HTTP DELETE request to remove a memory and returns success based on status code alone. The code has no confirmation prompt, visible user disclosure, or warning comment indicating that this operation is destructive and potentially irreversible.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This code writes user-provided content and metadata into a persistent SQLite database and also deletes stored records, but it does not include any confirmation prompt, logging/print statement, or warning comment/docstring describing these data-affecting operations. For a code file, persistent storage and deletion are safety-relevant actions that should have some visible disclosure unless clearly communicated elsewhere.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The delete method carries out a destructive operation via self.store.delete after a governance check, but there is no confirmation prompt, user-visible log/print, or warning comment/docstring indicating that stored memory will be removed. Governance enforcement is present, but it is not a user disclosure mechanism under this rule.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
In enterprise mode, the client serializes the full action into a JSON payload and sends it to a remote cloud endpoint using the configured base URL and API key. Because there is no explicit user-facing consent, disclosure, or data-minimization control at the point of transmission, sensitive prompts, context, secrets, or operational metadata embedded in the action may be exfiltrated off-host unexpectedly, which is especially risky in a governance component that may inspect high-sensitivity actions.

Context-Inappropriate Capability

Medium
Confidence
85% confidence
Finding
The manifest context names the skill 'warden-governance', but this settings module provisions credentials, base URLs, and local storage for a separate memory capability ('Engramport' and memory.db). Memory persistence may be useful in some systems, but it is not an obvious or declared requirement of a governance-focused skill based on the provided purpose context.

Ssd 3

Medium
Confidence
93% confidence
Finding
The skill writes the full action, result, and context objects into governed memory metadata, which can capture secrets, prompts, tokens, file contents, user data, or other sensitive runtime state. Because this hook runs after every action, it creates broad and persistent data retention with no visible minimization, redaction, or allowlisting, increasing the blast radius of any memory compromise or later misuse.

Ssd 3

Medium
Confidence
90% confidence
Finding
The error handler records action data plus the stringified error into memory, which may expose sensitive inputs, internal paths, stack-derived details, credentials embedded in exception messages, or private user data involved in the failed action. Error paths are especially risky because exceptions often contain unfiltered diagnostic information that would not normally be persisted.

Missing User Warnings

Low
Confidence
90% confidence
Finding
The EngramPort registration example sends bot-identifying information to an external service, but the guide does not call out that this registration transmits metadata off-platform. While the sample data is limited, the omission can still mislead operators about external disclosure during setup.

Unverifiable Dependency: pyyaml has 8 known advisory(ies) (CVE-2019-20477 (Deserialization of Untrusted Data in PyYAML); CVE-2020-1747 (Improper Input Validation in PyYAML); CVE-2020-14343 (Improper Input Validation in PyYAML) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
95% confidence
Finding
This duplicate finding refers to the same unpinned PyYAML dependency at line 34. As with the first occurrence, the issue is supply-chain uncertainty rather than evidence of an actively vulnerable resolved version, but for a governance/security-focused package any ambiguity in dependency safety is undesirable.

Unverifiable Dependency: httpx has 2 known advisory(ies) (CVE-2021-41945 (Improper Input Validation in httpx); CVE-2021-41945 (Encode OSS httpx <=1.0.0.beta0 is affected by improper input validation in `http)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
89% confidence
Finding
The optional enterprise dependency `httpx>=0.24` is not pinned, so enterprise installations may resolve to different versions across environments. This creates uncertainty around exposure to known or future `httpx` issues and weakens reproducibility for a governance/security-related skill that may perform network operations in enterprise mode.

Unverifiable Dependency: pytest has 2 known advisory(ies) (CVE-2025-71176 (pytest has vulnerable tmpdir handling); CVE-2025-71176 (pytest has vulnerable tmpdir handling)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unverifiable Dependency: setuptools has 10 known advisory(ies) (CVE-2013-1633 (Setuptools vulnerable to Man-in-the-middle attacks); CVE-2025-47273 (setuptools has a path traversal vulnerability in PackageIndex.download that lead); CVE-2024-6345 (setuptools vulnerable to Command Injection via package URL) +7 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
87% confidence
Finding
`wheel` is listed without a version constraint in the build system, which makes the build environment non-reproducible and leaves advisory exposure unverifiable. In isolation this is low impact, but packaging-tool weaknesses can be relevant in supply-chain scenarios, especially when users or CI systems build the project from source.

Static analysis

No suspicious patterns detected.