Back to skill

Security audit

Claw Store Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent remote memory tool, but it handles the long-term encryption key in ways that could expose saved memories.

Install only if you are comfortable with this skill storing AI memory remotely and managing a long-term local encryption key. Before use, create the key file with owner-only permissions, avoid running keygen or first-run setup where output is logged, and consider installing dependencies in an isolated environment with pinned versions.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
jackal-memory/client.py:42
Finding
Encryption Key Stored Without Explicitly Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `jackal-memory/client.py:42-53` **Vulnerability Type**: Insecure local secret storage **Risk Level**: High ### Vulnerable Code ```python if _KEY_FILE.exists(): return bytes.fromhex(_KEY_FILE.read_text().strip()) key_hex = os.urandom(32).hex() _KEY_FILE.parent.mkdir(parents=True, exist_ok=True) _KEY_FILE.write_text(key_hex) print( "\n[jackal-memory] Generated a new encryption key and saved it to:\n" f" {_KEY_FILE}\n\n" "Your memories are encrypted with this key. Back it up:\n" f" export JACKAL_MEMORY_ENCRYPTION_KEY={key_hex}\n", file=sys.stderr, ) ``` ### Technical Analysis The AES-256 encryption key is written to `~/.config/jackal-memory/key` using `Path.write_text()` without explicitly setting restrictive permissions. The resulting mode depends on the process umask. Under a common `022` umask, the key file may be created with mode `0644`, making it readable by other local users. The confidentiality guarantee of the remote memory store depends entirely on this key. Although AES-GCM protects the uploaded content from the storage provider, access to the local key allows any party possessing the ciphertext to decrypt the memory. The code also reads existing key files without verifying their ownership, type, or permissions. It therefore does not detect or repair an insecure key file created by a previous version or altered locally. ### Attack Path 1. A user invokes `save`, `load`, or `keygen` without an existing key. 2. The client generates an AES-256 key and writes it to `~/.config/jackal-memory/key`. 3. The host's umask permits group or world read access to the new file. 4. Another local account reads the key file. 5. The attacker separately obtains encrypted memory through access to the storage API, captured traffic at an authorized endpoint, backups, or another compromised component. 6. The attacker uses the stolen key to decrypt the stored memory. ### Impact Assessment A ...[truncated 415 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create the key file atomically with mode `0600`, rather than relying on the process umask. - Set the parent directory to mode `0700`. - Reject symbolic links and verify that the key file is a regular file owned by the current user. - Check existing file permissions before reading the key. Refuse access or repair permissions if group or world access is present. - Avoid overwriting an existing key through a non-atomic check-then-write sequence. - Use an operating-system credential store or keyring where available. For example, create the file with exclusive and restrictive flags: ```python _KEY_FILE.parent.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(_KEY_FILE.parent, 0o700) fd = os.open(_KEY_FILE, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) try: with os.fdopen(fd, "w") as key_file: key_file.write(key_hex) finally: os.chmod(_KEY_FILE, 0o600) ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
jackal-memory/client.py:47
Finding
Encryption Key Disclosed Through Standard Output and Error Streams<![CDATA[ ## Vulnerability Details **File Location**: `jackal-memory/client.py:47-53, 106-110` **Vulnerability Type**: Sensitive information exposure through process output **Risk Level**: High ### Vulnerable Code ```python print( "\n[jackal-memory] Generated a new encryption key and saved it to:\n" f" {_KEY_FILE}\n\n" "Your memories are encrypted with this key. Back it up:\n" f" export JACKAL_MEMORY_ENCRYPTION_KEY={key_hex}\n", file=sys.stderr, ) ``` ```python def cmd_keygen() -> None: key = _encryption_key() key_hex = key.hex() print(f"\nActive encryption key:\n\n {key_hex}\n") print("Set this in your environment to use the same key on other machines:") print(f" export JACKAL_MEMORY_ENCRYPTION_KEY={key_hex}\n") print("Keep this key safe — lose it and your encrypted memories are unrecoverable.") ``` ### Technical Analysis The first-run flow prints the complete encryption key to standard error, and the `keygen` command prints it to standard output. Agent runtimes, CI systems, terminal recorders, support bundles, process supervisors, and shell wrappers commonly capture these streams. This behavior conflicts with the documentation's instruction never to expose `JACKAL_MEMORY_ENCRYPTION_KEY` in output. Redirection to standard error does not protect a secret because standard error is frequently retained in the same logs as standard output. The key protects all memory encrypted by the client. Consequently, disclosure is materially more sensitive than exposing a single memory record. ### Attack Path 1. A user or agent invokes the client for the first time, or invokes `client.py keygen`. 2. The complete AES key is printed to standard error or standard output. 3. The surrounding agent platform, CI runner, shell history capture, logging service, or terminal recording retains the output. 4. A user or service with access to those logs extracts the hexadecimal key. 5. The attacker obtains corresponding encrypted memor ...[truncated 569 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not print the complete key during normal initialization. - Replace the first-run message with the key file path and backup instructions that do not contain the secret. - Require an explicit, interactive recovery or export action before revealing a key. - Refuse to reveal the key when output is not attached to a terminal, unless the user supplies a deliberate override. - Warn users that agent transcripts, CI logs, shell recordings, and support bundles may capture displayed secrets. - Prefer secure transfer through an operating-system credential manager or an encrypted backup workflow. - If command-line export is retained, require confirmation and emit the secret only to an explicitly selected secure destination rather than normal process output. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:21
Finding
Unpinned Cryptography Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:21-24` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Medium ### Vulnerable Code ```markdown ## Setup 1. Get an API key: https://web-production-5cce7.up.railway.app/auth/login 2. Set environment variable: `JACKAL_MEMORY_API_KEY=<your-key>` 3. Install dependency: `pip install cryptography` 4. On first save, an encryption key is auto-generated and saved to `~/.config/jackal-memory/key`. ``` ### Technical Analysis The setup instructions install `cryptography` without a version constraint, lock file, or artifact hash. Installation therefore resolves whichever release and transitive dependencies are available from the configured package index at execution time. This makes installations non-reproducible and allows the reviewed code to run against dependency versions that were not part of the audit. A future compromised release, compromised package index, unsafe configured mirror, or incompatible update could introduce malicious behavior or break cryptographic processing. No evidence was found that the currently named `cryptography` package is malicious. The risk arises from unconstrained future dependency resolution rather than confirmed package compromise. ### Attack Path 1. A user follows the documented setup command. 2. `pip` queries the user's configured package index or mirror and resolves the latest available package artifacts. 3. The index, mirror, release account, or resolved artifact has been compromised, or an unreviewed incompatible version has been published. 4. The package is installed into the user's Python environment. 5. Package code executes during import when the client performs encryption or decryption. 6. Malicious dependency code can act with the privileges of the user running the Skill, including reading environment variables, local files, API credentials, and encryption keys. ### Impact Assessment A compromised dependency would execute with the sa ...[truncated 415 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin `cryptography` to a reviewed, compatible version or narrow reviewed version range. - Provide a lock file or requirements file containing hashes for all resolved artifacts. - Install dependencies with hash enforcement, such as: ```bash python -m pip install --require-hashes -r requirements.txt ``` - Document the expected Python version and supported platforms so the locked artifacts can be verified. - Recommend installation in an isolated virtual environment rather than the user's global Python environment. - Review and update the pinned dependency through a controlled process that includes vulnerability scanning and regression testing. - Recommend trusted package indexes and caution users that custom or untrusted mirrors alter the supply-chain trust boundary. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill requests and documents capabilities that access environment secrets, local files, and a remote network service, but it does not declare any explicit tool scope or allowed-tools restrictions. That increases the blast radius because an agent runtime may grant broader-than-necessary access, allowing unintended exposure of API keys, encryption keys, or sensitive memory contents through file and network operations.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The client auto-generates a long-term encryption key and writes it to a predictable location in the user's home directory without setting restrictive file permissions or requiring explicit user consent before persisting secret material. If the file is readable by other local users, included in backups, or harvested by malware, an attacker who also obtains stored ciphertext can decrypt all memories protected by that key.

Natural-Language Policy Violations

Low
Confidence
80% confidence
Finding
The docstring explicitly states, "Encryption is always on — there is no opt-out." This imposes a fixed behavior without offering user choice or documenting a justified policy need, which matches the natural-language policy concern around forced settings without opt-in.

Static analysis

No suspicious patterns detected.