Back to skill

Security audit

Kannaka Memory

Security checks for vulnerabilities and agentic risk

Overview

This memory skill is mostly purpose-aligned, but its installer creates a persistent extension with a command-injection flaw and builds unpinned remote code, so it needs careful review before installation.

Install only after reviewing or fixing the generated extension to avoid shell execution from tool inputs, and prefer a pinned, verified upstream source or signed release. Treat the swarm feature as sending presence or phase state to the configured NATS server, and avoid enabling destructive memory deletion without backup or confirmation expectations.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/install.sh:42
Finding
Arbitrary Command Execution Through Shell Command Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.sh:42-83` **Vulnerability Type**: Shell command injection in the generated OpenClaw extension **Risk Level**: Critical ### Vulnerable Code ```typescript function runCli(args: string): string { try { const env = { ...process.env, KANNAKA_DATA_DIR: DATA_DIR }; const result = execSync(`"${BINARY}" ${args}`, { timeout: 600000, encoding: "utf-8", cwd: DATA_DIR, env }); return result.trim(); } catch (err: any) { const stdout = err.stdout?.trim() || ""; if (stdout) return stdout; throw new Error(err.stderr?.trim() || err.message); } } ``` Attacker-controlled tool parameters are incorporated into the command string, including: ```typescript async execute(_id: string, p: any) { const escaped = p.content.replace(/"/g, '\\"').replace(/\n/g, ' '); const args = [`remember "${escaped}"`]; if (p.importance) args.push(`--importance ${p.importance}`); if (p.category) args.push(`--category ${p.category}`); if (p.tags?.length) args.push(`--tags "${p.tags.join(",")}"`); const text = runCli(args.join(" ")); return { content: [{ type: "text", text: `Stored memory with ID: ${text}` }] }; } ``` ```typescript async execute(_id: string, p: any) { const text = runCli( `recall "${p.query.replace(/"/g, '\\"')}" --limit ${p.limit || 5}` ); } ``` ```typescript async execute(_id: string, p: any) { return { content: [{ type: "text", text: runCli(`hear "${p.file_path.replace(/"/g, '\\"')}"`) }] }; } ``` Other affected parameters include memory IDs, relation types, dream modes, agent IDs, display names, numeric options, categories, and tags. ### Technical Analysis The generated extension passes a dynamically constructed string to Node.js `execSync`. By default, `execSync` executes that string through a system shell. Consequently, shell syntax present in any interpolated tool parameter is interpreted rather than passed to the Kannak ...[truncated 2332 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace shell-based execution with an API that accepts an argument vector: ```typescript import { execFileSync } from "child_process"; function runCli(args: string[]): string { const env = { ...process.env, KANNAKA_DATA_DIR: DATA_DIR }; return execFileSync(BINARY, args, { timeout: 600000, encoding: "utf-8", cwd: DATA_DIR, env }).trim(); } ``` 2. Pass every CLI argument as a distinct array element: ```typescript runCli(["remember", p.content, "--importance", String(p.importance)]); runCli(["recall", p.query, "--limit", String(p.limit ?? 5)]); runCli(["hear", p.file_path]); ``` 3. Do not attempt to solve shell injection through manual escaping. Avoid invoking a shell entirely. 4. Strengthen the input schemas: - Restrict importance and boost values to the documented range of `0.0` through `1.0`. - Restrict result limits to a reasonable positive integer range. - Define dream mode as an enumeration containing only `lite` and `deep`. - Constrain identifiers and relation types to explicitly supported character sets and lengths. - Set maximum lengths and item counts for content, tags, paths, and display names. 5. Where possible, validate memory identifiers using the exact identifier format produced by Kannaka. 6. Add automated security tests using payloads containing `$()`, backticks, semicolons, pipes, redirections, quotes, newlines, and whitespace. Verify that these values are delivered literally to the binary and never interpreted by a shell. 7. Run the extension under a least-privileged account with restricted filesystem and network access to reduce impact if another command-execution defect is introduced. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/install.sh:22
Finding
Mutable Remote Source Is Retrieved, Built, and Installed Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.sh:22-30` **Vulnerability Type**: Unpinned remote payload retrieval and supply-chain exposure **Risk Level**: High ### Vulnerable Code ```bash REPO_DIR=$(mktemp -d) trap "rm -rf $REPO_DIR" EXIT echo "" echo "[1/4] Cloning repository..." git clone --depth 1 https://github.com/NickFlach/kannaka-memory.git "$REPO_DIR" echo "" echo "[2/4] Building binary (1-3 minutes)..." cd "$REPO_DIR" cargo build --release --features "hrm,nats" BINARY="$LOCAL_BIN/kannaka" cp target/release/kannaka "$BINARY" chmod +x "$BINARY" ``` ### Technical Analysis The installer clones the current default branch of a remote GitHub repository without selecting an immutable commit and without verifying a cryptographic checksum or signature. It then invokes Cargo on the retrieved source and installs the resulting executable into `~/.local/bin`. The effective code executed during installation is therefore not limited to the files present in the audited Skill package. It can change after this review whenever the upstream default branch changes. Building a Rust project may also execute project-controlled build scripts and retrieve Cargo dependencies, while the installed binary subsequently executes whenever the generated extension invokes it. HTTPS protects the transport connection but does not establish that the retrieved revision is the specific revision reviewed by the Skill publisher. It also does not mitigate compromise of the upstream account, repository, release process, or mutable branch. ### Attack Path 1. The upstream repository, maintainer account, or publishing workflow is compromised, or an unsafe change is pushed to its default branch. 2. Malicious code is placed in the Rust source, a build script, or dependency configuration. 3. A user runs `scripts/install.sh` as documented. 4. The installer retrieves the current unpinned repository state. 5. `cargo build` processes the attacker-controlled project and ...[truncated 913 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the installation to an immutable, audited Git commit rather than the current default branch: ```bash EXPECTED_COMMIT="full-40-character-reviewed-commit-id" git clone --filter=blob:none --no-checkout \ https://github.com/NickFlach/kannaka-memory.git "$REPO_DIR" git -C "$REPO_DIR" checkout --detach "$EXPECTED_COMMIT" ``` 2. Verify that the checked-out commit exactly matches the expected value before building: ```bash ACTUAL_COMMIT=$(git -C "$REPO_DIR" rev-parse HEAD) [ "$ACTUAL_COMMIT" = "$EXPECTED_COMMIT" ] || { echo "Unexpected source revision" >&2 exit 1 } ``` 3. Prefer a signed release artifact with a published SHA-256 or stronger checksum. Verify the signature and checksum before executing or installing it. 4. If source builds are required: - Commit and enforce `Cargo.lock`. - Build with `cargo build --locked`. - Review transitive dependencies and project build scripts. - Consider vendoring dependencies and verifying the vendor directory. - Use a controlled, isolated build environment without unnecessary secrets. 5. Associate each Skill release with a specific upstream revision and update that revision only after a new security review. 6. Perform installation with least privilege and avoid exposing sensitive environment variables to the build process. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Missing User Warnings

High
Confidence
99% confidence
Finding
The generated TypeScript extension constructs shell command strings with untrusted tool inputs and passes them to execSync, enabling command injection through multiple parameters that are not safely quoted or validated, such as category, memory_id, relation_type, display_name, and numeric fields. Because the extension exposes file operations, memory deletion, and arbitrary CLI invocation paths in an agent tool context, a malicious prompt or crafted input could execute arbitrary commands as the user, making this substantially more dangerous than a normal local utility.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill clearly instructs users to run shell installation scripts and use environment-driven network endpoints, yet it declares no explicit tool scope or permissions boundary. This weakens least-privilege controls and can cause an agent platform or user to underestimate the skill's ability to execute shell actions and interact with external services.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill advertises swarm synchronization through a default external NATS server (`nats://swarm.ninja-portal.com:4222`) and states that phase gossip and presence are published remotely, but it does not prominently warn that agent metadata and potentially derived memory/state information may leave the local environment. In a memory skill, this context increases sensitivity because users may assume persistence is local while swarm features introduce external transmission by default.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This shell script performs network access and persistent file writes by cloning a remote repository, building it, and copying artifacts into ~/.local/bin and ~/.openclaw. Although it prints progress messages, those messages do not clearly disclose the safety impact or ask for confirmation before modifying the user's environment.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The documented `kannaka_forget` capability enables deletion of stored memory, but the skill documentation does not explicitly warn that the action may be permanent or describe whether recovery/versioning exists. In a persistent memory system, unclear destructive semantics can lead to accidental data loss by users or agents invoking the tool without confirmation safeguards.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/install.sh:47