Back to skill

Security audit

karakeep-sh

Security checks for vulnerabilities and agentic risk

Overview

This is a real Karakeep helper, but it needs review because it can change or delete bookmark account data with an API key and lacks important safety checks.

Install only if you trust the Karakeep server endpoint and understand that the configured API key can read, create, update, tag, list, and delete bookmark data. Use HTTPS, prefer a limited-scope API key if available, and manually confirm deletions because the helper does not enforce confirmation itself.

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

Warning
Location
scripts/karakeep-script.sh:95
Finding
Bookmark deletion is not protected by the required confirmation control## Vulnerability Details **File Location**: `scripts/karakeep-script.sh`, lines 95–108 **Vulnerability Type**: Missing confirmation for a destructive operation **Risk Level**: Medium ### Vulnerable Code ```bash # Delete bookmark # Usage: kb-delete <bookmark_id> kb-delete() { check_config || return 1 local bookmark_id="$1" if [ -z "$bookmark_id" ]; then echo "Usage: kb-delete <bookmark_id>" return 1 fi curl -s -X DELETE "$KARAKEEP_API_URL/bookmarks/$bookmark_id" \ -H "Authorization: Bearer $KARAKEEP_API_KEY" echo "Deleted bookmark: $bookmark_id" } ``` ### Technical Analysis `SKILL.md` explicitly requires the user to be asked for confirmation before a bookmark is deleted. The implementation does not enforce that requirement: supplying a nonempty bookmark ID immediately sends an authenticated HTTP `DELETE` request. Relying only on an instruction in the documentation is insufficient because the function can be invoked directly by an agent, automation, or user without following the documented workflow. The function also uses silent `curl` output without `--fail` or HTTP status validation and unconditionally prints a deletion-success message. Consequently, unsuccessful requests can be represented as successful deletions. ### Attack Path 1. The script is sourced in an environment containing a valid `KARAKEEP_SERVER_URL` and `KARAKEEP_API_KEY`. 2. An agent, automation process, or user invokes `kb-delete` with the ID of an existing bookmark. 3. The function validates only that the ID is nonempty. 4. It immediately sends an authenticated `DELETE` request without prompting for confirmation or requiring an explicit confirmation token. 5. The targeted bookmark is deleted if the configured API key is authorized. 6. The function prints `Deleted bookmark` regardless of whether the server actually accepted the request. ### Impact Assessment Exploitation does not grant ...[truncated 440 chars]
Remediation
## Remediation Suggestions - Require an explicit confirmation argument, such as `--confirm`, before issuing the request. - For interactive use, display the target bookmark ID and require an exact confirmation response. - Keep confirmation enabled by default; do not silently bypass it in noninteractive environments. - Consider separating preparation and execution so the agent can present the planned deletion before it occurs. - Use `curl --fail-with-body` and inspect the exit status or HTTP status code. - Print the success message only after the server confirms successful deletion. - Return a nonzero status on transport failures and non-successful HTTP responses. - Apply equivalent safeguards to other destructive operations where user intent should be reconfirmed. Example hardened pattern: ```bash kb-delete() { check_config || return 1 local bookmark_id="$1" local confirmation="$2" if [ -z "$bookmark_id" ]; then echo "Usage: kb-delete <bookmark_id> --confirm" return 1 fi if [ "$confirmation" != "--confirm" ]; then echo "Deletion requires explicit confirmation." return 1 fi if curl --fail-with-body -sS -X DELETE \ "$KARAKEEP_API_URL/bookmarks/$bookmark_id" \ -H "Authorization: Bearer $KARAKEEP_API_KEY"; then echo "Deleted bookmark: $bookmark_id" else echo "Failed to delete bookmark: $bookmark_id" >&2 return 1 fi } ```

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/karakeep-script.sh:6
Finding
Bearer credentials can be transmitted over unencrypted HTTP## Vulnerability Details **File Location**: `scripts/karakeep-script.sh`, line 6; lines 10–23; representative authenticated request at lines 63–66 **Vulnerability Type**: Missing transport-security validation for sensitive credentials **Risk Level**: Medium ### Vulnerable Code ```bash # Set API base URL KARAKEEP_API_URL="${KARAKEEP_SERVER_URL}/api/v1" # Check configuration check_config() { if [ -z "$KARAKEEP_SERVER_URL" ]; then echo "Error: KARAKEEP_SERVER_URL environment variable not set" echo "Set your API address and run:" echo " export KARAKEEP_SERVER_URL=https://your-instance.com" return 1 fi if [ -z "$KARAKEEP_API_KEY" ]; then echo "Error: KARAKEEP_API_KEY environment variable not set" echo "Get your API key from Karakeep settings and run:" echo " export KARAKEEP_API_KEY=your_api_key_here" return 1 fi return 0 } ``` A representative authenticated request is: ```bash curl -s -X POST "$KARAKEEP_API_URL/bookmarks" \ -H "Authorization: Bearer $KARAKEEP_API_KEY" \ -H "Content-Type: application/json" \ -d "$json" | jq '.' ``` ### Technical Analysis The configuration check verifies only that the server URL and API key are nonempty. It does not require an HTTPS URL, reject an HTTP URL, or limit plaintext transport to an explicitly approved local-development mode. Every API operation places the API key in an `Authorization: Bearer` header. If `KARAKEEP_SERVER_URL` begins with `http://`, the bearer credential and any transmitted bookmark data are sent without TLS protection. Bearer credentials provide access based on possession, so an intercepted key can be replayed directly. The example configuration uses HTTPS, but that example does not enforce transport security in executable code. ### Attack Path 1. The user or deployment environment configures `KARAKEEP_SERVER_URL` with an `http://` URL, whether accidentally, through ...[truncated 1024 chars]
Remediation
## Remediation Suggestions - Validate `KARAKEEP_SERVER_URL` before constructing the API URL. - Require the URL to begin with `https://` in normal operation. - If plaintext HTTP is required for local testing, permit it only through an explicit opt-in variable and restrict it to loopback hosts such as `localhost`, `127.0.0.1`, or `::1`. - Reject unsupported schemes, embedded credentials, control characters, and malformed URLs. - Continue relying on normal certificate validation; do not introduce `curl --insecure`. - Use `curl --fail-with-body --show-error` so TLS and HTTP failures are visible. - Rotate the API key if it has previously been used over an untrusted plaintext connection. Example validation: ```bash case "$KARAKEEP_SERVER_URL" in https://*) ;; http://localhost*|http://127.0.0.1*) if [ "$KARAKEEP_ALLOW_INSECURE_LOCALHOST" != "1" ]; then echo "Error: HTTP requires an explicit localhost-only override" >&2 return 1 fi ;; *) echo "Error: KARAKEEP_SERVER_URL must use HTTPS" >&2 return 1 ;; esac ``` The API URL should also be derived after configuration validation rather than once when the file is initially sourced.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/karakeep-script.sh:215
Finding
List creation constructs JSON through unsafe string interpolation## Vulnerability Details **File Location**: `scripts/karakeep-script.sh`, lines 215–219 **Vulnerability Type**: Improper JSON construction **Risk Level**: Low ### Vulnerable Code ```bash curl -s -X POST "$KARAKEEP_API_URL/lists" \ -H "Authorization: Bearer $KARAKEEP_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"name\":\"$name\",\"icon\":\"$icon\"}" | jq '.' ``` ### Technical Analysis The `name` and `icon` values are interpolated directly into a JSON string. JSON metacharacters are not escaped before insertion. Inputs containing double quotes, backslashes, newlines, or other control characters can therefore produce malformed JSON. A crafted value containing a closing quote and additional property syntax can also alter the structure of the request body. This differs from the safer pattern already used elsewhere in the script, where `jq --arg` performs correct JSON encoding. This issue is request-body injection rather than shell command injection: the variables remain inside a quoted shell argument, so the reviewed code does not directly execute shell syntax contained in the values. ### Attack Path 1. The script is configured with a valid Karakeep API key. 2. An attacker influences a list name or icon passed to `kb-create-list`. 3. The supplied value includes JSON metacharacters, such as a quote followed by an additional property. 4. Direct interpolation places those characters into the request body without JSON escaping. 5. The resulting request is either rejected as malformed or interpreted with a structure different from the intended `{name, icon}` object. 6. If the Karakeep endpoint accepts additional or duplicate fields, unintended field values may reach the API. ### Impact Assessment The direct impact is confined to list-creation requests made with the already configured API key. Exploitation can cause denial of the requested operation through malformed JSON or potentially manipu ...[truncated 193 chars]
Remediation
## Remediation Suggestions - Construct the entire payload with `jq -n` and `--arg`. - Never interpolate untrusted values directly into JSON syntax. - Check that `jq` successfully generated the payload before sending the request. - Use `curl --data-binary "$json"` or `--data "$json"` with the generated payload. - Use `curl --fail-with-body --show-error` and propagate failures to the caller. - Optionally validate reasonable name and icon lengths before submission. Recommended implementation: ```bash local json if ! json=$(jq -n \ --arg name "$name" \ --arg icon "$icon" \ '{name: $name, icon: $icon}'); then echo "Failed to construct list payload" >&2 return 1 fi curl --fail-with-body -sS -X POST "$KARAKEEP_API_URL/lists" \ -H "Authorization: Bearer $KARAKEEP_API_KEY" \ -H "Content-Type: application/json" \ --data-binary "$json" | jq '.' ```
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (9)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises and relies on a shell script but does not declare any tool scope or allowed-tools constraints. That means an agent/runtime may permit broader shell usage than users expect, increasing the chance of unintended command execution or abuse if later skill content or parameters are handled unsafely.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill instructs users to send bookmark data, notes, search queries, and possibly fetched content to a configured Karakeep server, but it does not warn that this may transmit potentially sensitive user data off-platform. Users may unknowingly expose private notes, URLs, tags, or embedded content to a remote server they do not control or have not fully vetted.

External Transmission

Medium
Category
Data Exfiltration
Content
json=$(echo "$json" | jq --arg n "$note" '. + {note: $n}')
  fi

  curl -s -X POST "$KARAKEEP_API_URL/bookmarks" \
       -H "Authorization: Bearer $KARAKEEP_API_KEY" \
       -H "Content-Type: application/json" \
       -d "$json" | jq '.'
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
# Use jq to properly escape the note content (handles newlines, quotes, etc.)
  local json=$(echo '{}' | jq --arg n "$note" '{note: $n}')

  curl -s -X PATCH "$KARAKEEP_API_URL/bookmarks/$bookmark_id" \
       -H "Authorization: Bearer $KARAKEEP_API_KEY" \
       -H "Content-Type: application/json" \
       -d "$json" | jq '.'
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
95% confidence
Finding
The delete helper performs irreversible bookmark deletion immediately on invocation without any confirmation prompt, dry-run option, or safety guard. In an agent or automation context, this increases the chance of accidental or prompt-induced destructive actions that remove user data.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest says the skill is a 'Karakeep bookmark manager with full native RESTful API support including notes, updates, and deletion,' which describes bookmark-focused operations. The code additionally implements list management and tag attachment/removal endpoints, expanding the skill beyond bookmark CRUD into broader organizational-resource management not stated in the manifest description.

External Transmission

Medium
Category
Data Exfiltration
Content
return 1
  fi

  curl -s -X POST "$KARAKEEP_API_URL/lists" \
       -H "Authorization: Bearer $KARAKEEP_API_KEY" \
       -H "Content-Type: application/json" \
       -d "{\"name\":\"$name\",\"icon\":\"$icon\"}" | jq '.'
Confidence
79% confidence
Finding
This request constructs JSON for list creation by directly interpolating shell variables into the payload instead of using jq or equivalent safe encoding. A crafted list name or icon containing quotes or special characters can break the JSON structure and potentially cause unintended request semantics or command misuse in downstream automation.

External Transmission

Medium
Category
Data Exfiltration
Content
local tags=$(printf '%s\n' "$@" | jq -R . | jq -s 'map({tagName: .})')

  curl -s -X POST "$KARAKEEP_API_URL/bookmarks/$bookmark_id/tags" \
       -H "Authorization: Bearer $KARAKEEP_API_KEY" \
       -H "Content-Type: application/json" \
       -d "{\"tags\":$tags}"
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
local tags=$(printf '%s\n' "$@" | jq -R . | jq -s 'map({tagName: .})')

  curl -s -X DELETE "$KARAKEEP_API_URL/bookmarks/$bookmark_id/tags" \
       -H "Authorization: Bearer $KARAKEEP_API_KEY" \
       -H "Content-Type: application/json" \
       -d "{\"tags\":$tags}"
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.