T09 · Insecure Skill Coding Practices
- Location
jackal-memory/client.py:393- Finding
Unnecessary Exposure of the Complete Process Environment to Third-Party Dependency Code
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
Claw Store has a coherent memory-storage purpose, but it handles highly sensitive wallet and encryption secrets with several under-scoped runtime behaviors users should review before installing.
Install only if you are comfortable with a persistent memory tool that uses local wallet credentials, external Jackal providers, a Railway-hosted API, and Node dependencies. Use a clean environment with only the needed Jackal variables, avoid running first-use setup where stderr is logged, back up and protect the generated key and mnemonic, and review or update the npm dependency tree before using it for sensitive memory.
jackal-memory/client.py:393Unnecessary Exposure of the Complete Process Environment to Third-Party Dependency Code
jackal-memory/client.py:67Encryption Key Disclosed in Standard Error During Implicit First-Use Generation
jackal-memory/package-lock.json:43Wallet Operations Depend on Deprecated Cryptographic Packages with Explicit Private-Key Risk
protobufjs 6.11.4 carries multiple critical advisories including denial of service from crafted schemas/messages, code-injection risks in generated code paths, and unbounded recursion issues. This is particularly concerning in an agent memory/storage integration because protobuf is central to network message handling, and attacker-controlled chain or service responses could trigger parsing vulnerabilities or service crashes.
protobufjs 7.5.4 is also flagged with critical issues including denial of service and code-injection-related flaws in generated/object-conversion paths. Because this dependency appears in multiple transitive toolchains, leaving it unresolved broadens the attack surface for malicious protobuf payloads or unsafe schema-driven processing within the skill's storage and RPC ecosystem.
Passing the entire os.environ into a child process needlessly exposes all ambient secrets available to the parent process, not just the Jackal values needed for upload. In an agent environment, that can include API keys, cloud credentials, and tokens, and any compromised or overly chatty Node dependency could read or exfiltrate them.
file=sys.stderr)
sys.exit(1)
address = _mnemonic_to_jackal_address(mnemonic)
env = {**os.environ, "JACKAL_MNEMONIC": mnemonic, "JACKAL_ADDRESS": address}
r = subprocess.run(
["node", str(_JACKAL_CLIENT), "upload", key],
This download path repeats the same broad secret exposure pattern by forwarding the full parent environment to a subprocess. Because the child process and its dependencies are external code, this unnecessarily enlarges the blast radius if that code is malicious, compromised, or logs environment variables.
safe_key = re.sub(r'[^a-zA-Z0-9._-]', '_', key)
cid = f"Home/jackal-memory/{safe_key}"
env = {**os.environ, "JACKAL_MNEMONIC": mnemonic, "JACKAL_ADDRESS": address}
r = subprocess.run(
["node", str(_JACKAL_CLIENT), "download", cid],
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
// Downloads bypass fetch entirely via nodeHttpGet (require('https')), which is
// the proven fix for SSL renegotiation issues on Windows.
// ── Load .env ─────────────────────────────────────────────────────────────────
const fs = require('fs');
const path = require('path');
const envPath = path.join(__dirname, '..', '.env');
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
// ── Load .env ─────────────────────────────────────────────────────────────────
const fs = require('fs');
const path = require('path');
const envPath = path.join(__dirname, '..', '.env');
if (fs.existsSync(envPath)) {
for (const line of fs.readFileSync(envPath, 'utf8').split('\n')) {
const m = line.match(/^\s*([\w]+)\s*=\s*(.*)\s*$/);
This lockfile includes axios 0.21.4 under @cosmjs/launchpad, and that version is flagged with multiple high-severity advisories including SSRF, prototype-pollution-related issues, and denial-of-service conditions. In a storage/networking skill that talks to decentralized services, vulnerable HTTP client behavior can increase exposure to malicious endpoints, proxy-bypass issues, or unsafe request handling.
The transitive dependency @cosmjs/socket pins ws 7.5.10, which is reported vulnerable to memory-exhaustion denial of service. Because this skill depends on websocket communication for chain/RPC interactions, a malicious or hostile peer could potentially trigger resource exhaustion and disrupt the agent's availability.
The lockfile includes axios 1.13.6, which static analysis reports as affected by multiple high-severity advisories such as MITM/prototype-pollution gadgets and NO_PROXY bypass leading to SSRF. Since this skill performs remote network access, weaknesses in the HTTP client can materially affect confidentiality and request integrity when interacting with untrusted or attacker-influenced infrastructure.
form-data 4.0.5 is flagged for CRLF injection via unescaped multipart field names and filenames, which can allow malformed requests or header injection when attacker-controlled values are included in multipart payloads. If this skill or its transitive libraries ever construct multipart requests from external input, that can become a request-smuggling or downstream parsing risk.
The top-level ws 8.19.0 dependency is reported vulnerable to memory disclosure and memory-exhaustion DoS issues. Since websocket transport is an explicit dependency of this skill and likely exposed to remote peers or RPC nodes, exploitation could leak process memory or degrade availability during agent operation.
The package declares ws 8.19.0, which is flagged with advisories for uninitialized memory disclosure and memory exhaustion denial of service. Because this skill supports client-side signing and decentralized storage interactions, a vulnerable WebSocket library may expose sensitive data in memory or allow remote resource exhaustion during network communication, making the context more security-sensitive than a generic utility package.
The skill performs sensitive operations including network access, shell execution, environment-variable use, and local file reads/writes, yet it declares no explicit tool scope or permission boundaries. This creates an overbroad execution model where an agent platform may permit more capability than users expect, increasing the blast radius of prompt misuse, credential exposure, or unintended file/system access.
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
The mnemonic controls their on-chain storage. Losing it means losing sovereign access to stored files.
**Important:** Never ask the user to paste their API key, encryption key, or wallet mnemonic into chat. Always direct them to set it as an environment variable on their machine.
## Base URL
The skill instructs the agent to persist memory across sessions, load identity before work, and save on significant state changes, creating durable cross-session state. Persistent memory can store sensitive user data, enable unintended retention beyond user expectations, and amplify prompt-injection or data-poisoning risks if untrusted content is later reloaded as trusted context.
## Behaviour guidelines
- Load your identity/memory blob on startup before doing any work
- Write locally during the session as normal
- Call save at session end or on significant state changes
- Use descriptive keys: `identity`, `session-2026-02-26`, `project-jackal`
- Never log or expose `JACKAL_MEMORY_API_KEY` in output
The manifest stores plaintext-derived metadata such as user keys, update times, and plaintext byte length on disk, even though the actual content is encrypted. For a memory client, those metadata fields can reveal sensitive topics, identifiers, or usage patterns and may surprise users who expect local secrecy.
A memory-storage skill that silently installs and runs external Node components introduces unnecessary execution capability and supply-chain exposure. This is especially concerning for agent environments because first-run behavior can execute unreviewed package scripts without clear operator consent.
Automatically running npm install executes dependency lifecycle scripts from package metadata, which can run arbitrary code on the host during first use. In an agent skill context, this expands the trust boundary from the reviewed Python file to the entire Node dependency tree and any compromised package or install script.
if not _NODE_MODULES.exists():
print("[jackal-memory] Installing Jackal dependencies (first run — takes ~30s)...",
file=sys.stderr)
r = subprocess.run(
["npm", "install", "--prefix", str(_SKILL_DIR)],
capture_output=True, text=True,
)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
address = _mnemonic_to_jackal_address(mnemonic)
env = {**os.environ, "JACKAL_MNEMONIC": mnemonic, "JACKAL_ADDRESS": address}
r = subprocess.run(
["node", str(_JACKAL_CLIENT), "upload", key],
input=data_b64, text=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
address = _mnemonic_to_jackal_address(mnemonic)
env = {**os.environ, "JACKAL_MNEMONIC": mnemonic, "JACKAL_ADDRESS": address}
r = subprocess.run(
["node", str(_JACKAL_CLIENT), "upload", key],
input=data_b64, text=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
env = {**os.environ, "JACKAL_MNEMONIC": mnemonic, "JACKAL_ADDRESS": address}
r = subprocess.run(
["node", str(_JACKAL_CLIENT), "download", cid],
text=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
env = {**os.environ, "JACKAL_MNEMONIC": mnemonic, "JACKAL_ADDRESS": address}
r = subprocess.run(
["node", str(_JACKAL_CLIENT), "download", cid],
text=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
Exporting the manifest to an arbitrary path can leak sensitive metadata to less protected locations, backups, shared directories, or other processes. Because the exported data contains saved-key identifiers and timestamps, it may disclose confidential memory structure even without plaintext content.
This code performs network storage and retrieval of user-provided data using external Jackal providers, including reading upload content from stdin and downloading file contents from provider URLs. Although there are stderr logs for operational progress, there is no clear user disclosure or confirmation that user data will be transmitted to third-party network endpoints during these operations.
Skill requests more permissions than appear necessary for its stated functionality. Review if elevated access is justified.
- **Data leaving your machine:** encrypted payloads + key metadata + your Jackal public address for provisioning
- **Data not leaving your machine:** plaintext memory content, encryption key, wallet mnemonic/private key
- **No runtime wordlist download:** BIP39 English wordlist is vendored locally in this skill package
- **Secret file permissions:** locally generated key/mnemonic files are written with `0600` permissions
- **Homepage:** https://obsideo.io
- **Source code:** https://github.com/Regan-Milne/jackal-memory
Detected: suspicious.env_credential_access