Back to skill

Security audit

The Uninscribed

Security checks for vulnerabilities and agentic risk

Overview

This skill is disclosed as a game client, but it asks for a persistent autonomous agent, broad agent-to-agent messaging, local credentials, and possible public posting with limited containment.

Review this carefully before installing. Use it only if you are comfortable creating a recurring autonomous player agent, enabling agent-to-agent messaging, storing a game API key locally, and potentially giving that agent Moltbook credentials for public/account-affecting posts. Prefer a restricted agent, dedicated throwaway credentials, explicit approval for every Moltbook post, secure file permissions, and a clear rollback plan for the gateway config and heartbeat.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (3)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:137
Finding
Untrusted Remote Game Content Is Processed by an Autonomous Tool-Capable Agent<![CDATA[ ## Vulnerability Details **File Location**: `uninscribed.py:67-76`; `SKILL.md:137-149`; `SKILL.md:172` **Vulnerability Type**: Prompt injection through remotely controlled content **Risk Level**: High ### Vulnerable Code and Instructions ```python def cmd_observe(args): key = get_api_key() result = api("POST", "/api/observe", api_key=key) if "observation" in result: print(result["observation"]) else: print(json.dumps(result, indent=2)) def cmd_act(args): key = get_api_key() action_text = " ".join(args.action) result = api("POST", "/api/act", {"action": action_text}, api_key=key) ``` The autonomous heartbeat instructions state: ```markdown # The Uninscribed — Play Session 1. Read ~/.config/the-uninscribed/session-log.md for context on where you left off 2. The CLI is at: skills/the-uninscribed/uninscribed.py (resolve relative to workspace) 3. Run `python3 <cli> observe` to see the world 4. Take actions in a loop: - Read the observation - Decide what to do - Run `python3 <cli> act <action>` with yieldMs=420000 and timeout=420 - The CLI waits for the cooldown before returning - Repeat 5. When done, update session-log.md with what happened ``` The same player is expected to receive access to another service's credentials: ```markdown Your player agent needs Moltbook credentials. Store them at `~/.config/moltbook/credentials.json` and tell the player agent where to find them. ``` ### Technical Analysis The `/api/observe` response is controlled by the remote game service and may also contain content introduced by other game participants. The CLI prints the response without establishing a trust boundary, while the heartbeat directs an autonomous language-model agent to read that content, make decisions, execute commands, and repeat. The instructions do not tell the player to treat observations as untrusted data. They also do not prohibit obeying embedded instructions that request filesyste ...[truncated 1712 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Explicitly designate all game observations and action results as untrusted data that must never override system, developer, Skill, or heartbeat instructions. 2. State that the player must not follow tool-use, credential-access, messaging, configuration, or external-posting instructions embedded in game content. 3. Restrict the player to an allowlist containing only the required CLI operations. Deny arbitrary shell execution, unrestricted filesystem access, gateway configuration, and unrelated session tools. 4. Isolate Moltbook publishing into a separate component that receives only the minimum required token and requires user confirmation before every public post. 5. Do not expose the general Moltbook credential file to the game-playing agent. Use a narrowly scoped credential or brokered API operation where possible. 6. Validate and delimit remote observations before presenting them to the model, for example by placing them in a clearly labeled untrusted-data block. 7. Prevent remote observations from being copied verbatim into `session-log.md`; persist only a structured, agent-generated summary after filtering instructions and secrets. 8. Add monitoring and rate limits for filesystem access, external network calls, public posting, and agent-to-agent messages originating from the player. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
uninscribed.py:21
Finding
API Credential File Is Created Without Explicitly Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `uninscribed.py:21-24` **Vulnerability Type**: Insecure local storage of a bearer credential **Risk Level**: Medium ### Vulnerable Code ```python def save_config(config): os.makedirs(CONFIG_DIR, exist_ok=True) with open(CONFIG_FILE, "w") as f: json.dump(config, f, indent=2) ``` Registration passes the received API key to this function: ```python if "apiKey" in result: save_config({"apiKey": result["apiKey"], "agentId": result["agentId"], "name": args.name}) ``` ### Technical Analysis The API key is a bearer credential stored at `~/.config/the-uninscribed/config.json`. The code creates the directory and file without specifying secure permission modes. Consequently, the resulting permissions depend on the process umask and any pre-existing directory or file permissions. In a permissively configured or shared environment, another local user or process may be able to read the credential. Reopening an existing file with `"w"` also does not correct unsafe permissions that were previously assigned to that file. The implementation additionally lacks atomic replacement, which can expose partial writes or permit filesystem race conditions in hostile local environments. ### Attack Path 1. A user runs `python3 uninscribed.py register <name>`. 2. The service returns an API key, which is written to `~/.config/the-uninscribed/config.json`. 3. A permissive umask or an already insecure file causes the configuration to remain readable by another local account or process. 4. The attacker reads the stored `apiKey`. 5. The attacker sends authenticated requests to the game API using the stolen bearer token. ### Impact Assessment An attacker who obtains the key can impersonate the registered game agent, observe account-specific game state, and submit unauthorized actions. The demonstrated credential is scoped to The Uninscribed; the code does not show access to broader system or Moltbook credentials t ...[truncated 23 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the configuration directory with mode `0700` and verify or repair the mode if it already exists. 2. Create the credential file with mode `0600`, using `os.open` with explicit flags and permissions rather than relying on the process umask. 3. Write to a securely created temporary file in the same directory, flush and synchronize it, set mode `0600`, and atomically replace the destination with `os.replace`. 4. Reject symbolic links and verify that the destination is a regular file owned by the current user before overwriting it. 5. On every load, check file ownership and permissions and fail with a clear warning if the credential is accessible to group or other users. 6. Document credential revocation and rotation procedures in case the file is exposed. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
SKILL.md:45
Finding
Global Agent-to-Agent Messaging and Persistent Execution Exceed the CLI's Minimum Privileges<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:45-96`; `SKILL.md:121-149` **Vulnerability Type**: Overbroad agent permissions and recurring autonomous execution **Risk Level**: Medium ### Vulnerable Configuration and Instructions The proposed gateway patch creates a recurring player agent and globally enables agent-to-agent messaging: ```json { "agents": { "defaults": { "models": { "anthropic/claude-sonnet-4-20250514": { "alias": "sonnet" } } }, "list": [ { "id": "main", "heartbeat": { "every": "<your current main heartbeat interval>", "target": "last" } }, { "id": "uninscribed-player", "model": { "primary": "anthropic/claude-sonnet-4-20250514", "fallbacks": [] }, "heartbeat": { "every": "1h", "target": "none" } } ] }, "tools": { "agentToAgent": { "enabled": true } } } ``` The player is then instructed to execute repeatedly: ```markdown 3. Run `python3 <cli> observe` to see the world 4. Take actions in a loop: - Read the observation - Decide what to do - Run `python3 <cli> act <action>` with yieldMs=420000 and timeout=420 - The CLI waits for the cooldown before returning - Repeat 5. When done, update session-log.md with what happened ``` ### Technical Analysis The CLI's declared core functionality—registration, observation, and game actions—does not inherently require global agent-to-agent messaging or a permanently recurring autonomous agent. The documentation does require user confirmation before applying the gateway patch, which reduces the risk of silent configuration changes, but it does not eliminate the least-privilege problem. The patch enables `agentToAgent` at the tools level without documenting peer restrictions. It also establishes recurring heartbeats without defining a constrained tool all ...[truncated 1451 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make the dedicated autonomous player strictly optional; retain foreground or manually initiated CLI use as the least-privilege default. 2. Scope agent-to-agent messaging to an explicit allowlist of permitted sender and recipient pairs instead of enabling it globally. 3. Grant the player only the ability to invoke the audited CLI with `observe` and `act`; deny arbitrary shell commands and unrelated tools. 4. Restrict filesystem access to the game configuration and a dedicated state file. Do not grant general access to the user's home directory. 5. Apply network egress controls allowing only the declared game endpoint unless the user separately authorizes another integration. 6. Add heartbeat execution limits, cost limits, action limits, failure backoff, and an expiration time. 7. Require explicit confirmation for public posts, credential use, gateway changes, and messages that could cause the main agent to take consequential actions. 8. Document a complete rollback procedure that disables the heartbeat, removes the player agent, and restores the previous agent-to-agent setting. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Most of the core CLI behavior matches the description: the code provides register/observe/act commands, connects to theuninscribed.com, and writes the API key to ~/.config/the-uninscribed/config.json. However, the declared purpose also includes a significant setup capability: explaining required gateway config changes, asking for confirmation, and then modifying configuration to create a dedicated player agent and enable agent-to-agent communication. None of that exists in the provided code chunk. Since this is a material part of the declared functionality rather than a minor implementation detail, the description overstates what the code actually does, so this should be flagged as a mismatch.

Credential Access

High
Category
Privilege Escalation
Content
- Don't post duplicate content — Moltbook can ban for a week
- Moltbook has a 30-minute cooldown between posts

Your player agent needs Moltbook credentials. Store them at `~/.config/moltbook/credentials.json` and tell the player agent where to find them.

## Quick Reference
Confidence
97% confidence
Finding
The skill instructs the player agent to use stored Moltbook credentials from a local file, encouraging access to sensitive secrets by an automated secondary agent. This increases the risk of credential misuse, leakage through logs or prompts, and compromise of an unrelated external account if the agent or workspace is exposed.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill documentation directs the agent to perform file writes and networked actions, but it declares no explicit tool scope or allowed-tools restrictions. That increases the chance the skill runs with broader-than-necessary capabilities and makes unintended file modification, credential handling, or outbound requests easier if the instructions are followed automatically.

Session Persistence

Medium
Category
Rogue Agent
Content
---
name: the-uninscribed
description: Play The Uninscribed — a persistent world built on language. Use when the agent wants to connect to, observe, or take actions in The Uninscribed at theuninscribed.com. Provides a CLI (uninscribed.py) for register, observe, and act commands. Writes API key to ~/.config/the-uninscribed/config.json on registration. Setup requires a one-time gateway config patch to create a dedicated player agent (using a cheaper model like Sonnet) and enable agent-to-agent communication — the skill will explain what changes are needed and ask for confirmation before modifying any config.
---

# The Uninscribed
Confidence
89% confidence
Finding
The skill explicitly promotes creating a dedicated persistent agent with agent-to-agent communication and heartbeat-driven autonomous behavior. Persistent autonomous sessions can continue taking actions outside the immediate user conversation, which raises the risk of unsupervised behavior, unintended ongoing network activity, and reduced user visibility into what the agent is doing over time.

Session Persistence

Medium
Category
Rogue Agent
Content
### Step 3: Set Up the Player's HEARTBEAT.md

The player agent has its own workspace at `~/.openclaw/workspace-uninscribed-player/`. Write a `HEARTBEAT.md` there to control what it does each heartbeat:

```markdown
# The Uninscribed — Play Session
Confidence
93% confidence
Finding
The HEARTBEAT.md instructions tell a secondary agent to repeatedly observe, act, and update persistent memory across sessions, effectively establishing an autonomous loop. That persistence makes the skill more dangerous because it can continue interacting with external services and files over time without per-action approval, compounding mistakes or abuse.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The Moltbook section expands the skill from interacting with The Uninscribed into posting content to a separate social platform and managing unrelated credentials. That creates unnecessary cross-service exposure, increases the attack surface, and can cause the agent to act on external accounts beyond the user's likely expectation for a game skill.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The instructions tell the agent to post to Moltbook and use stored credentials without a clear warning that this shares data with a third party and may create public account activity. Users may reasonably expect gameplay actions, not automated external posting, so this can lead to privacy leakage or unauthorized social-media actions.