Back to skill

Security audit

Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it gives a remote Kemia server too much control over local files and can upload sensitive agent files to an arbitrary URL without enough user control.

Review before installing. Only connect this skill to a Kemia server you control or strongly trust, avoid plain HTTP, treat config.json and generated login links as secrets, and do not run /import unless you trust the remote snapshot source because the current client can overwrite more than the intended agent markdown files if the server returns unsafe filenames.

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
scripts/connect.sh:85
Finding
Sensitive Agent Configuration and Credentials May Be Transmitted over Insecure Transport## Vulnerability Details **File Location**: `scripts/connect.sh`, lines 85–110; URL acceptance at lines 329–336 **Vulnerability Type**: Sensitive data exposure through an unvalidated network destination and insecure transport **Risk Level**: High ### Vulnerable Code ```bash export_agent_files() { # $1 = kemia base URL, $2 = api key → writes agentId back to config.json local base="$1" key="$2" local files_json="[]" for md_file in SOUL.md IDENTITY.md USER.md MEMORY.md AGENTS.md TOOLS.md HEARTBEAT.md; do local filepath="${WORKSPACE}/${md_file}" if [ -f "${filepath}" ]; then local content content=$(jq -Rs '.' < "${filepath}") files_json=$(echo "${files_json}" | jq --arg fn "${md_file}" --argjson ct "${content}" '. + [{"filename": $fn, "content": $ct}]') echo " ✓ ${md_file}" fi done if [ "$(echo "${files_json}" | jq 'length')" = "0" ]; then echo " (no workspace .md files found — skipping export)" return 0 fi local payload payload=$(jq -n --arg name "${AGENT_NAME}" --argjson files "${files_json}" '{name: $name, files: $files}') local response response=$(curl -sf -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${key}" \ -d "${payload}" \ "${base}/api/v1/agents") || { ``` The destination is ultimately derived from an unrestricted command-line argument: ```bash if [ -z "${KEMIA_URL_ARG}" ]; then echo "Usage: /kemia connect <kemia-url>" echo "" echo "Example: /kemia connect https://kemia.byte5.ai" exit 1 fi start_new_enrollment "${KEMIA_URL_ARG%/}" ``` ### Technical Analysis The connection workflow accepts an arbitrary base URL without requiring HTTPS or restricting the destination to a trusted host. That URL is persisted and subsequently used for authenticated API requests. During enrollment, the script uploads the contents of up to seven workspace fi ...[truncated 1960 chars]
Remediation
## Remediation Suggestions 1. Parse and validate the supplied URL before enrollment. 2. Require the `https` scheme for all non-loopback destinations. 3. If development support is necessary, allow plaintext HTTP only through an explicit opt-in flag and only for loopback addresses such as `127.0.0.1`, `::1`, or `localhost`. 4. Reject URLs containing embedded credentials, unexpected fragments, malformed ports, or unsupported schemes. 5. Before exporting files, display the canonical destination and exact list of files and obtain explicit user confirmation. 6. Make sensitive files such as `USER.md` and `MEMORY.md` opt-in rather than exporting them automatically. 7. Consider certificate pinning or a configurable trusted-host allowlist for managed deployments. 8. Use scoped, revocable API credentials with only the permissions required for the selected Agent.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/import.sh:62
Finding
Remote Snapshot Filenames Permit Path Traversal and Arbitrary File Overwrite## Vulnerability Details **File Location**: `scripts/import.sh`, lines 62–79 **Vulnerability Type**: Path traversal and arbitrary file write **Risk Level**: High ### Vulnerable Code ```bash # ---- Backup current files ---- BACKUP_DIR="${WORKSPACE}/.kemia-backup/$(date +%Y%m%d-%H%M%S)" mkdir -p "${BACKUP_DIR}" echo "Backing up current files to ${BACKUP_DIR}..." echo "${RESPONSE}" | jq -r '.files[].filename' | while read -r filename; do if [ -f "${WORKSPACE}/${filename}" ]; then cp "${WORKSPACE}/${filename}" "${BACKUP_DIR}/${filename}" echo " ↪ ${filename}" fi done # ---- Write imported files ---- echo "Importing files to workspace..." echo "${RESPONSE}" | jq -c '.files[]' | while read -r file; do FILENAME=$(echo "${file}" | jq -r '.filename') echo "${file}" | jq -r '.content' > "${WORKSPACE}/${FILENAME}" echo " ✓ ${FILENAME}" done ``` ### Technical Analysis The `filename` field comes from a remote API response and is directly concatenated with the workspace and backup paths. The client performs no validation against absolute paths, `..` traversal components, path separators, symbolic links, duplicate names, or an approved filename allowlist. The API documentation states that server-side filenames should match a restrictive pattern, but the client supports arbitrary self-hosted Kemia endpoints and cannot safely rely on every server to implement that validation. A compromised legitimate server could also return a malicious response. Shell quoting prevents ordinary shell command injection, but it does not prevent filesystem path traversal. For example, a filename such as `../../.ssh/config` resolves outside the intended workspace. Redirection then replaces the resolved file with remote content under the privileges of the user running the Skill. The backup operation is also unsafe: traversal components can cause the source or destination to escape their expected directories, and nes ...[truncated 1746 chars]
Remediation
## Remediation Suggestions 1. Enforce an explicit allowlist before any backup or write operation. For example, permit only: ```text SOUL.md IDENTITY.md USER.md MEMORY.md AGENTS.md TOOLS.md HEARTBEAT.md ``` 2. Reject empty filenames, absolute paths, `..`, `/`, backslashes, control characters, and duplicate filenames. 3. Canonicalize the workspace and candidate destination and verify that the destination remains directly beneath the workspace. 4. Reject symbolic links in every destination path component. Open output files with mechanisms that prevent symlink following where available. 5. Validate every entry in the complete response before writing any file, so an invalid later entry cannot leave a partial import. 6. Write each file to a secure temporary file inside the workspace, apply appropriate permissions, and atomically rename it into place only after validation succeeds. 7. Ensure backup destinations are regular files beneath the backup directory and create required directories safely if nested paths are intentionally supported. 8. Consider verifying a signed snapshot manifest so the client can authenticate both filenames and contents independently of transport security.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/import.sh:39
Finding
Predictable Shared Temporary Files Enable Symlink Attacks and Local Data Exposure## Vulnerability Details **File Location**: `scripts/import.sh`, lines 39–55; `scripts/status.sh`, lines 62–77 **Vulnerability Type**: Unsafe temporary file handling **Risk Level**: Medium ### Vulnerable Code `scripts/import.sh`: ```bash HTTP_CODE=$(curl -s -o /tmp/kemia-deploy-response.json -w "%{http_code}" \ -H "Authorization: Bearer ${API_KEY}" \ "${BASE_URL}/api/v1/agents/${AGENT_ID}/deploy") if [ "${HTTP_CODE}" = "404" ]; then echo "No deploy-ready snapshot found." echo "Mark a snapshot as 'deploy ready' in the kemia web interface first." exit 0 fi if [ "${HTTP_CODE}" != "200" ]; then echo "ERROR: API returned HTTP ${HTTP_CODE}" cat /tmp/kemia-deploy-response.json 2>/dev/null exit 1 fi RESPONSE=$(cat /tmp/kemia-deploy-response.json) rm -f /tmp/kemia-deploy-response.json ``` `scripts/status.sh`: ```bash DEPLOY_CODE=$(curl -s -o /tmp/kemia-status-deploy.json -w "%{http_code}" \ -H "Authorization: Bearer ${API_KEY}" \ "${BASE_URL}/api/v1/agents/${AGENT_ID}/deploy") if [ "${DEPLOY_CODE}" = "200" ]; then SNAP_NAME=$(jq -r '.snapshot.name' /tmp/kemia-status-deploy.json) SNAP_FILES=$(jq '.files | length' /tmp/kemia-status-deploy.json) echo " Deploy: ⚡ READY — '${SNAP_NAME}' (${SNAP_FILES} files)" echo "" echo "Run /import to apply the pending snapshot." else echo " Deploy: — no pending snapshot" fi rm -f /tmp/kemia-status-deploy.json ``` ### Technical Analysis Both scripts use fixed, globally predictable filenames in `/tmp`. On typical multi-user systems, another local account can create these paths before the Skill runs. The paths may be regular files owned by the attacker or symbolic links to another destination. The import response contains the complete remote snapshot, including Agent configuration contents. The scripts do not set a restrictive `umask`, create the files atomically, verify ownership, reject symbolic links, o ...[truncated 2030 chars]
Remediation
## Remediation Suggestions 1. Set a restrictive file-creation mask near the beginning of each script: ```bash umask 077 ``` 2. Create a unique temporary file with `mktemp`, check that creation succeeds, and retain the returned path: ```bash TEMP_FILE=$(mktemp "${TMPDIR:-/tmp}/kemia-deploy.XXXXXX") ``` 3. Register cleanup immediately: ```bash trap 'rm -f -- "${TEMP_FILE}"' EXIT HUP INT TERM ``` 4. Use the generated path for all `curl`, `jq`, and `cat` operations. 5. Prefer a private temporary directory created with `mktemp -d` if multiple related files are required. 6. Do not reuse predictable filenames across processes or invocations. 7. Verify that temporary objects are regular files owned by the current user before reading them. 8. Ensure cleanup runs on HTTP 404, API errors, parsing errors, signals, and successful completion.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (25)

Ae1

High
Category
analysis-evasion
Content
**Script:** `scripts/connect.sh [kemia-url]`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Tool Parameter Abuse

High
Category
Tool Misuse
Content
case "${HTTP_CODE}" in
      200)
        INSTANCE_NAME_LIVE=$(jq -r '.instanceName // "unknown"' /tmp/kemia-connect-probe.$$ 2>/dev/null || echo "unknown")
        rm -f /tmp/kemia-connect-probe.$$
        echo "✓ Already connected to kemia at ${BASE_URL}"
        echo "  Instance: ${INSTANCE_NAME_LIVE}"
        echo ""
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
case "${HTTP_CODE}" in
      200)
        INSTANCE_NAME_LIVE=$(jq -r '.instanceName // "unknown"' /tmp/kemia-connect-probe.$$ 2>/dev/null || echo "unknown")
        rm -f /tmp/kemia-connect-probe.$$
        echo "✓ Already connected to kemia at ${BASE_URL}"
        echo "  Instance: ${INSTANCE_NAME_LIVE}"
        echo ""
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
case "${HTTP_CODE}" in
      200)
        INSTANCE_NAME_LIVE=$(jq -r '.instanceName // "unknown"' /tmp/kemia-connect-probe.$$ 2>/dev/null || echo "unknown")
        rm -f /tmp/kemia-connect-probe.$$
        echo "✓ Already connected to kemia at ${BASE_URL}"
        echo "  Instance: ${INSTANCE_NAME_LIVE}"
        echo ""
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
case "${HTTP_CODE}" in
      200)
        INSTANCE_NAME_LIVE=$(jq -r '.instanceName // "unknown"' /tmp/kemia-connect-probe.$$ 2>/dev/null || echo "unknown")
        rm -f /tmp/kemia-connect-probe.$$
        echo "✓ Already connected to kemia at ${BASE_URL}"
        echo "  Instance: ${INSTANCE_NAME_LIVE}"
        echo ""
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
fi

RESPONSE=$(cat /tmp/kemia-deploy-response.json)
rm -f /tmp/kemia-deploy-response.json

SNAPSHOT_NAME=$(echo "${RESPONSE}" | jq -r '.snapshot.name')
SNAPSHOT_ID=$(echo "${RESPONSE}" | jq -r '.snapshot.id')
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The script writes server-supplied filenames directly to "${WORKSPACE}/${FILENAME}" without validating or normalizing the path. A malicious or compromised kemia server can return filenames containing ../ or absolute-style traversal components, causing overwrite of arbitrary files accessible to the user, not just files within the workspace.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
else
    echo "  Deploy:      — no pending snapshot"
  fi
  rm -f /tmp/kemia-status-deploy.json
else
  echo "  Agent ID:    (none — run /kemia connect to export)"
fi
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill documents shell-based scripts but does not declare any explicit tool scope or allowed-tools boundary. That omission weakens policy enforcement and reviewability, increasing the chance the skill is run with broader shell access than necessary or in environments that assume undeclared capabilities are safe.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
A one-time login URL is effectively a bearer token: anyone who obtains it within its validity window may be able to authenticate to the kemia web UI. Omitting a clear warning increases the risk that users paste it into chats, logs, tickets, or screenshots, causing accidental account or configuration access.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documented config.json contains an API key that can authorize actions against the kemia service, so treating it as ordinary configuration invites secret leakage through backups, repo commits, screenshots, or permissive file permissions. Because the skill also supports import and status operations, compromise of this file could enable unauthorized access to agent configuration and deployment state.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The one-time login URL contains the raw token as a bearer credential in the query string, and the documentation tells operators to send the URL to the user without emphasizing its sensitivity. Such URLs are easily leaked through chat logs, browser history, referrer headers, screenshots, or email forwarding, enabling session hijacking within the token validity window.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# 1. Start enrollment
RESPONSE=$(curl -sf -X POST -H "Content-Type: application/json" \
  -d '{"name":"My Agent","orchestrator":"openclaw"}' \
  https://kemia.byte5.ai/api/v1/enroll)
Confidence
60% 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
92% confidence
Finding
The integration example retrieves the API key and prints it directly to stdout, which commonly ends up in terminal scrollback, shell history workflows, CI logs, demos, or copied transcripts. Because the API key is a bearer credential for all subsequent API calls, accidental disclosure would allow unauthorized access to the kemia instance until the key is rotated.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The connect flow does more than establish connectivity: it reads multiple local workspace markdown files and sends their contents to the remote kemia service. Those files may contain prompts, memory, agent instructions, or sensitive operational data, so this creates an unexpected data exfiltration path relative to the stated skill role and can violate least surprise and least privilege.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The script automatically transmits local markdown files to the remote service immediately after connection without a just-in-time warning or consent prompt. Because these files can contain sensitive agent state or instructions, sending them without explicit notice increases the risk of unintended disclosure to a third-party endpoint.

External Transmission

Medium
Category
Data Exfiltration
Content
payload=$(jq -n --arg name "${AGENT_NAME}" --argjson files "${files_json}" '{name: $name, files: $files}')

  local response
  response=$(curl -sf -X POST \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ${key}" \
    -d "${payload}" \
Confidence
95% confidence
Finding
This POST sends bundled local agent file contents to an external service using the acquired API key. In the context of a connection utility, transmitting local workspace content off-host is security-relevant because it can expose sensitive prompts, memory, or configuration to a remote system the user may not realize is receiving them.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
agent_id=$(echo "${response}" | jq -r '.agentId // empty')
  if [ -n "${agent_id}" ]; then
    jq --arg aid "${agent_id}" '. + {agentId: $aid}' "${CONFIG_FILE}" > "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "${CONFIG_FILE}"
    chmod 600 "${CONFIG_FILE}"
    echo "✓ Agent '${AGENT_NAME}' exported (id: ${agent_id})"
  fi
}
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
agent_id=$(echo "${response}" | jq -r '.agentId // empty')
  if [ -n "${agent_id}" ]; then
    jq --arg aid "${agent_id}" '. + {agentId: $aid}' "${CONFIG_FILE}" > "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "${CONFIG_FILE}"
    chmod 600 "${CONFIG_FILE}"
    echo "✓ Agent '${AGENT_NAME}' exported (id: ${agent_id})"
  fi
}
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
agent_id=$(echo "${response}" | jq -r '.agentId // empty')
  if [ -n "${agent_id}" ]; then
    jq --arg aid "${agent_id}" '. + {agentId: $aid}' "${CONFIG_FILE}" > "${CONFIG_FILE}.tmp" && mv "${CONFIG_FILE}.tmp" "${CONFIG_FILE}"
    chmod 600 "${CONFIG_FILE}"
    echo "✓ Agent '${AGENT_NAME}' exported (id: ${agent_id})"
  fi
}
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
79% confidence
Finding
The script stores the returned API key in config.json with restrictive permissions, but the user is not clearly told that persistent credentials will be saved locally. For safety-sensitive operations involving credentials, the criteria call for some visible disclosure such as a print statement, prompt, or documentation warning.

External Transmission

Medium
Category
Data Exfiltration
Content
payload=$(jq -n --arg name "${INSTANCE_NAME}" \
      '{name: $name, orchestrator: "openclaw"}')
  fi
  response=$(curl -sf -X POST \
    -H "Content-Type: application/json" \
    -d "${payload}" \
    "${kemia_url}/api/v1/enroll") || {
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
92% confidence
Finding
The import operation overwrites existing workspace files immediately and non-interactively, which can destroy local changes or replace critical agent configuration with remote content. In this skill, importing edited configs from a remote service is the primary function, so destructive replacement is expected, but the lack of confirmation or dry-run increases operational risk.

Missing User Warnings

Low
Confidence
79% confidence
Finding
The script reads an API key from config.json and transmits it in an Authorization header during the curl request. While comments describe the behavior at a high level, there is no runtime disclosure that credentials and agent-related data are being sent over the network.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The script reads an API key from config and sends it in Authorization headers via curl to remote endpoints. Although this is part of checking status, there is no visible warning, prompt, or explanatory comment disclosing that credentials will be used for outbound network requests.

Static analysis

No suspicious patterns detected.