Back to skill

Security audit

Anytype

Security checks for vulnerabilities and agentic risk

Overview

This Anytype skill is mostly purpose-aligned, but it ships private instance setup details and encourages high-impact data changes that require careful review before installation.

Install only after removing SETUP.md or replacing it with placeholders, rotating or revoking the exposed Anytype invite/hash if they are real, and deciding whether the bot account's Anytype permissions are narrowly scoped. Store the API key in a safer secret store or a locked-down file outside shared workspace content, and require explicit user confirmation plus a recoverable backup before any delete or delete-and-recreate update.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
SETUP.md:3
Finding
Instance-Specific Sharing Material Included in the Distributed Package<![CDATA[ ## Vulnerability Details **File Location**: `SETUP.md`, lines 3-10 **Vulnerability Type**: Exposure of instance-specific access and sharing configuration **Risk Level**: High ### Vulnerable Code ```markdown This file holds instance-specific config for the Anytype skill. Not for publishing. ## Space - **Primary space ID:** `bafyreial7tzkey5sntoizw7scv2lrywqdicd7m6ru2k6wae7w3z6igm5ke.1f4pitw5ca9gc` - **Invite ID:** `bafybeifel75s42deh74lbjx3socdyung4ojspjgbr64jxrduf3dghlx35i` - **Hash:** `CvFB12csDDVDpYxi5J1FewXmdsLmifnLx4p3fBCRG6Jt` - **Public link format:** `https://object.any.coop/{object_id}?spaceId={space_id}&inviteId={invite_id}#{hash}` ``` ### Technical Analysis The package contains a configuration file explicitly identified as “Not for publishing,” but the file was nevertheless included in the project. It exposes a concrete Anytype space identifier, invitation identifier, and sharing hash. These values are not required in a reusable Skill package and exceed the minimum information necessary to communicate with the local Anytype API. The documented public-link template shows how the exposed values are combined with an object identifier to construct externally accessible Anytype URLs. The API key itself is not present in this file. Nevertheless, invitation and sharing parameters may function as access material depending on the space’s current sharing configuration. The disclosure also exposes internal collection, property, and tag identifiers elsewhere in the same file. ### Attack Path 1. An attacker downloads or otherwise obtains the Skill package. 2. The attacker opens `SETUP.md` and extracts the space ID, invite ID, and sharing hash. 3. The attacker obtains an object ID from the same file, another disclosure, shared content, logs, or prior knowledge. 4. The attacker constructs a URL using the documented format: `https://object.any.coop/{object_id}?spaceId={space_id}&inviteId={invite_id}#{hash}` 5. The attacker attempts to access t ...[truncated 751 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the instance-specific `SETUP.md` from the distributed package and version-control history. 2. Rotate or revoke the exposed invitation and sharing secret where supported. 3. Review the affected Anytype space’s public-sharing configuration and access logs. 4. Replace the file with a redacted `SETUP.example.md` containing placeholders only. 5. Load instance-specific identifiers from user-controlled configuration or environment variables. 6. Add the real setup file to `.gitignore` and package exclusion rules. 7. Add automated secret and sensitive-configuration scanning to the release process. 8. Avoid publishing object, collection, tag, and property identifiers unless they are intentionally public and operationally necessary. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:45
Finding
API Bearer Credential Stored in a Shared Plaintext Environment File<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 45-47 **Vulnerability Type**: Insecure plaintext credential storage **Risk Level**: Medium ### Vulnerable Code ```bash # 5. Store the key echo "ANYTYPE_API_KEY=<key>" >> ~/.openclaw/workspace/.env ``` The corresponding credential-loading behavior appears in `scripts/anytype_api.py`, lines 13-24: ```python def load_api_key(): """Read only ANYTYPE_API_KEY from the workspace .env — nothing else.""" if "ANYTYPE_API_KEY" in os.environ: return os.environ["ANYTYPE_API_KEY"] env_path = os.path.expanduser("~/.openclaw/workspace/.env") if os.path.exists(env_path): with open(env_path) as f: for line in f: line = line.strip() if line.startswith("ANYTYPE_API_KEY="): return line.split("=", 1)[1].strip() return "" ``` ### Technical Analysis The setup instructions append a bearer credential to a shared workspace `.env` file using the process’s ambient `umask`. The command does not create the file with restrictive permissions, verify existing ownership or permissions, or protect against symbolic-link redirection. Appending also permits multiple `ANYTYPE_API_KEY` entries to accumulate. Because the loader returns the first matching entry, key rotation may not work as intended if an obsolete value precedes the new value. Entering a real key directly into the displayed shell command can additionally expose it through shell history, terminal logs, session recording, or process instrumentation. A workspace-level `.env` may also be visible to unrelated tools or agents operating in the same workspace. Reading `ANYTYPE_API_KEY` is necessary for the declared authenticated API functionality, and the implementation only selects that variable. The risk therefore arises from the storage method and shared location, not from the need to access a credential. ### Attack Path 1. A user follows the documented setup c ...[truncated 1279 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer an operating-system keyring, secret manager, or securely injected environment variable instead of a workspace `.env`. 2. If file storage is unavoidable, use a dedicated credential file rather than a shared workspace configuration file. 3. Create the file with restrictive permissions before writing: ```bash umask 077 install -m 600 /dev/null "$HOME/.openclaw/anytype.env" ``` 4. Verify that the credential file is a regular file owned by the current user and reject symbolic links. 5. Replace an existing key atomically instead of appending duplicate entries. 6. Do not place the literal secret in shell command arguments. Read it without terminal echo and write it through a controlled setup utility. 7. Update the loader to reject credential files with unsafe ownership or permissions. 8. Ensure credential files are excluded from version control, backups with broad readership, diagnostic bundles, and logs. 9. Use a dedicated bot account with access only to the required Anytype spaces and rotate the key periodically. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:103
Finding
Delete-Before-Recreate Update Workflow Can Cause Irrecoverable Data and Reference Loss<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 103-126 **Vulnerability Type**: Unsafe destructive update procedure **Risk Level**: High ### Vulnerable Code ```markdown ⚠️ **CRITICAL: PATCH does NOT update the body/content field.** Sending `body` or `markdown` in a PATCH silently succeeds (HTTP 200) but the content is NOT updated in Anytype. Only metadata fields like `name` are updated via PATCH. **The only reliable way to update an object's content is: DELETE + recreate.** ⚠️ **This is destructive.** Always save the old content before deleting: ```python # Step 0: fetch and save existing content before deleting old = requests.get(f"{BASE}/v1/spaces/{space_id}/objects/{old_id}", headers=headers).json() old_content = old.get("object", {}).get("snippet", "") # keep a local copy # Step 1: delete old object (irreversible via API — confirm before running) requests.delete(f"{BASE}/v1/spaces/{space_id}/objects/{old_id}", headers=headers) # Step 2: create new object with full updated content resp = requests.post(f"{BASE}/v1/spaces/{space_id}/objects", json={"name": name, "type_key": "page", "body": new_content}, headers=headers) new_id = resp.json()["object"]["id"] ``` Store the new object ID — callers must update any references (e.g. `related_pages`) after recreation. Deleted objects may be recoverable from the Anytype bin in the desktop app. ``` ### Technical Analysis The documented workflow deletes the original object before creating and validating its replacement. This ordering is non-transactional: deletion may succeed while recreation fails because of an API error, malformed content, authorization change, service interruption, or process termination. The purported backup stores only the response’s `snippet` field in memory. It does not demonstrate preservation of the complete body, properties, tags, icon, type, list memberships, backlinks, timestamps, or other metadata. It is therefore not a reliable restoration artifa ...[truncated 2206 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Verify PATCH behavior against the pinned Anytype API version and use non-destructive PATCH updates whenever supported. 2. Remove the unconditional statement that deletion and recreation is the only reliable update method. 3. If replacement is unavoidable, create the replacement first and validate its complete content and metadata before deleting the original. 4. Export and persist a complete backup of the original object, including body, properties, type, icon, tags, list memberships, and relationships. 5. Obtain explicit user confirmation immediately before deletion and display the affected object name and ID. 6. Check every HTTP response and expected response field before proceeding to the next stage. 7. Update inbound and outbound references, list memberships, hubs, and external mappings before retiring the original object. 8. Keep the old object until all migration checks pass; prefer archiving or moving it to a recoverable bin over permanent deletion. 9. Add a dry-run mode and require an explicit destructive-operation flag for generic DELETE requests. 10. Add integration tests against the supported API version to detect changes in PATCH semantics. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

Credential Access

High
Category
Privilege Escalation
Content
anytype auth apikey create my-key

# 5. Store the key
echo "ANYTYPE_API_KEY=<key>" >> ~/.openclaw/workspace/.env
```

## API Usage
Confidence
95% confidence
Finding
The skill instructs storing an API key in a plaintext .env file under a workspace path, which creates a durable credential exposure point. If the workspace is readable by other tools, users, logs, backups, or accidental commits, the key can be stolen and used to access or modify Anytype data.

Credential Access

High
Category
Privilege Escalation
Content
def load_api_key():
    if "ANYTYPE_API_KEY" in os.environ:
        return os.environ["ANYTYPE_API_KEY"]
    env_path = os.path.expanduser("~/.openclaw/workspace/.env")
    if os.path.exists(env_path):
        for line in open(env_path):
            if line.strip().startswith("ANYTYPE_API_KEY="):
Confidence
93% confidence
Finding
The code reads a bearer token directly from a workspace .env file, expanding the set of components and prompts that can access the credential. In agent environments, broad file-read capability can make plaintext token retrieval a practical credential theft path.

Credential Access

High
Category
Privilege Escalation
Content
BASE = "http://127.0.0.1:31012"

def load_api_key():
    """Read only ANYTYPE_API_KEY from the workspace .env — nothing else."""
    if "ANYTYPE_API_KEY" in os.environ:
        return os.environ["ANYTYPE_API_KEY"]
    env_path = os.path.expanduser("~/.openclaw/workspace/.env")
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"""Read only ANYTYPE_API_KEY from the workspace .env — nothing else."""
    if "ANYTYPE_API_KEY" in os.environ:
        return os.environ["ANYTYPE_API_KEY"]
    env_path = os.path.expanduser("~/.openclaw/workspace/.env")
    if os.path.exists(env_path):
        with open(env_path) as f:
            for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"""Read only ANYTYPE_API_KEY from the workspace .env — nothing else."""
    if "ANYTYPE_API_KEY" in os.environ:
        return os.environ["ANYTYPE_API_KEY"]
    env_path = os.path.expanduser("~/.openclaw/workspace/.env")
    if os.path.exists(env_path):
        with open(env_path) as f:
            for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill performs sensitive operations involving environment variables and HTTP requests, but it does not declare any explicit tool scope or permission boundaries. That increases the risk that an agent may invoke network and credential-handling behaviors without clear user visibility or policy enforcement.

Session Persistence

Medium
Category
Rogue Agent
Content
## Setup (one-time)

```bash
# 1. Create a dedicated bot account (generates a key, NOT mnemonic-based)
anytype auth create my-bot

# 2. Install and start as a user service
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

External Transmission

Medium
Category
Data Exfiltration
Content
requests.delete(f"{BASE}/v1/spaces/{space_id}/objects/{old_id}", headers=headers)

# Step 2: create new object with full updated content
resp = requests.post(f"{BASE}/v1/spaces/{space_id}/objects",
    json={"name": name, "type_key": "page", "body": new_content},
    headers=headers)
new_id = resp.json()["object"]["id"]
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
81% confidence
Finding
The file documents bearer authentication and an endpoint that returns an API key, but does not include any caution about treating the key as sensitive credential material. For markdown files, omitting privacy or security warnings around credential-handling behavior is a reportable missing warning.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
This markdown file lists create, update, and delete operations for spaces, including `DELETE /v1/spaces/{space_id}`, but does not warn that these actions can modify or permanently remove user data. Under the markdown-specific warning criterion, descriptions of behaviours affecting user data or system integrity should disclose those risks.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The Objects section includes create, update, and delete endpoints for user content, but provides no warning that these operations can alter or remove stored objects. Because this is a markdown description of data-affecting behavior, the omission leaves safety-relevant consequences undisclosed.

Natural-Language Policy Violations

Low
Confidence
77% confidence
Finding
The instructions say to default to `page` for all content and frame `note` as a narrow exception, which imposes a fixed content-organization policy rather than offering a user choice. This is a natural-language constraint on user workflow/preferences and is not justified as a region- or compliance-specific requirement.

Static analysis

No suspicious patterns detected.