Back to skill

Security audit

Kv Embed Store

Security checks for vulnerabilities and agentic risk

Overview

This KV store is broadly coherent, but it uses remote embedding services and includes undocumented credential-file access that can send a local API key and key/query text to a configurable endpoint.

Review before installing. Use only a trusted HTTPS embedding provider, avoid custom base URLs unless you control them, and do not store secrets or regulated data in this KV store. Be aware the standalone script does not match the documented KV_EMBED_* variable names and may read /userdata/硅基流动api-key if present.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/kv_store.py:14
Finding

Undocumented credential-file access with credential transmission to a configurable endpoint

Content
View full analysis

Vulnerability Details

File Location: scripts/kv_store.py, lines 14-38
Vulnerability Type: Undocumented credential access and credential egress
Risk Level: Medium

Vulnerable Code:

python
def get_api_key() -> str:
    """Resolve API key from env or common locations."""
    key = os.environ.get("SILICONFLOW_API_KEY")
    if key:
        return key
    # Try reading from file
    keyfile = os.path.expanduser("/userdata/硅基流动api-key")
    if os.path.exists(keyfile):
        return open(keyfile).read().strip()
    raise RuntimeError(
        "SILICONFLOW_API_KEY not set. Set env var or place key at /userdata/硅基流动api-key"
    )


BASE_URL = os.environ.get("SILICONFLOW_BASE_URL", "https://api.siliconflow.cn/v1")
MODEL = os.environ.get("EMBED_MODEL", "BAAI/bge-m3")
DEFAULT_THRESHOLD = 0.85


def embed(texts: list[str], api_key: str) -> np.ndarray:
    """Get embeddings from SiliconFlow API."""
    resp = requests.post(
        f"{BASE_URL}/embeddings",
        headers={"Authorization": f"Bearer {api_key}"},
        json={"model": MODEL, "input": texts, "encoding_format": "float"},
        timeout=30,
    )

Technical Analysis

The program automatically reads a credential from the fixed path /userdata/硅基流动api-key when SILICONFLOW_API_KEY is absent. This filesystem access is not disclosed in SKILL.md, which instead documents the differently named KV_EMBED_API_KEY, KV_EMBED_BASE_URL, and KV_EMBED_MODEL variables.

The recovered credential is subsequently inserted into a bearer Authorization header and sent to SILICONFLOW_BASE_URL. Because that URL is configurable and the implementation does not require HTTPS or validate the destination host, the credential and embedding input can be transmitted to an unintended or untrusted endpoint.

Sending key or query text to an embedding provider is necessary for the declared remote-embedding functionality. However, silently ...[truncated 1536 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove the implicit /userdata/硅基流动api-key fallback. If file-based credentials are required, add an explicit --credential-file option or environment variable and require deliberate user configuration.
  2. Align the implementation with the documented KV_EMBED_API_KEY, KV_EMBED_BASE_URL, and KV_EMBED_MODEL names.
  3. Require an https:// base URL by default and reject plaintext HTTP unless the user enables an explicit, clearly labeled development override.
  4. Validate or allowlist the intended embedding-provider hostname where operationally feasible.
  5. Display a clear notice that put keys and search queries are transmitted to the configured third-party provider.
  6. Ensure credentials are not logged, included in exceptions, or forwarded to a different host during redirects. Consider disabling redirects for embedding requests or explicitly validating every redirect target.
  7. Use a with open(...) context manager when reading an explicitly configured credential file and verify that its permissions are suitably restrictive.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/kv_store.py:67
Finding

Plaintext key-value store created without enforced restrictive permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/kv_store.py, lines 67-76
Vulnerability Type: Insecure local storage permissions and non-atomic persistence
Risk Level: Low

Vulnerable Code:

python
def _save(self):
    d = {
        "entries": self.data["entries"],
        "embeddings": {k: v.tolist() for k, v in self.data["embeddings"].items()},
        "config": {"threshold": self.threshold, "model": MODEL},
    }
    os.makedirs(os.path.dirname(self.path) or ".", exist_ok=True)
    with open(self.path, "w") as f:
        json.dump(d, f, ensure_ascii=False, indent=2)

Technical Analysis

The store supports arbitrary JSON values, which may include credentials, internal references, personal data, or other sensitive information. _save() writes all aliases, values, and embeddings to an unencrypted JSON file using the process's default creation permissions.

The code does not explicitly set a restrictive file mode such as 0600. Consequently, the effective permissions depend on the ambient umask and existing file permissions. In an environment with a permissive umask, other local users may be able to read the store.

Opening the destination directly with mode w also truncates the existing store before serialization completes. A process interruption, disk error, or concurrent invocation can therefore leave a partial or empty file. No file locking or atomic replacement protects concurrent updates.

Attack Path

  1. A user invokes put and stores confidential data in the value field.
  2. The process runs under a permissive umask, or the destination file already has overly broad permissions.
  3. _save() writes the full plaintext data set without enforcing owner-only access.
  4. Another local account or process with filesystem access reads the JSON file and obtains aliases and stored values.
  5. Alternatively, concurrent writes or an interruption during serialization truncate or corrupt ...[truncated 632 chars]
Remediation
View remediation

Remediation Suggestions

  1. Create new store files with owner-only mode 0600, for example by using os.open() with explicit flags and permissions.
  2. Check and, where appropriate, correct permissions on an existing store before writing it.
  3. Ensure newly created parent directories use restrictive permissions such as 0700 when they are dedicated to this application.
  4. Write serialized data to a temporary file in the same directory, flush it, call os.fsync(), apply restrictive permissions, and atomically replace the destination with os.replace().
  5. Add file locking or another concurrency-control mechanism to prevent lost updates from simultaneous processes.
  6. Document clearly that values are stored in plaintext and advise users not to store secrets unless the destination is adequately protected.
  7. If secret storage is an intended use case, provide encryption at rest backed by an operating-system credential store or a user-managed encryption key.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (14)

Tainted flow: 'BASE_URL' from os.environ.get (line 29, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
97% confidence
Finding

The request URL is derived from SILICONFLOW_BASE_URL, an environment-controlled value, and the code sends a Bearer API key plus user-provided texts to that destination. If an attacker can influence the environment, they can redirect requests to an attacker-controlled host and capture both the credential and all embedded content, turning this into credential theft and data exfiltration.

Content

Scanner excerpt · scripts/kv_store.py (reported line 36)May include surrounding context.

python
def embed(texts: list[str], api_key: str) -> np.ndarray:
    """Get embeddings from SiliconFlow API."""
    resp = requests.post(
        f"{BASE_URL}/embeddings",
        headers={"Authorization": f"Bearer {api_key}"},
        json={"model": MODEL, "input": texts, "encoding_format": "float"},

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The configuration documents the store path but does not clearly warn that values are persisted in a human-readable local JSON file. Users may assume this is opaque application state, then store credentials, tokens, internal URLs, or sensitive notes that remain readable on disk and may be exposed through backups, shared workspaces, or weak file permissions.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

This configuration advertises use of an external embedding endpoint, which means data used for embeddings can leave the local environment. In this skill's context, keys, aliases, and likely search queries are semantically rich and may reveal sensitive project information even if raw secrets are not intentionally stored.

Content

Scanner excerpt · SKILL.md (reported line 33)May include surrounding context.

| storePath | data/kv_store.json | Path to the store JSON file | | memoryIndex | false | Generate memory index with aliases and value previews | | apiKey | — | Embedding API key (or KV_EMBED_API_KEY env) | | baseUrl | https://api.siliconflow.cn/v1 | Embedding API base URL (or KV_EMBED_BASE_URL env) | | model | BAAI/bge-m3 | Embedding model (or KV_EMBED_MODEL env) |

json5

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The example configuration encourages users to set a live external provider URL without an adjacent warning about privacy implications. Because this is a storage/discovery skill, the transmitted content can include internal project names, aliases, and query terms, increasing the chance of unintended disclosure to a third party.

Content

Scanner excerpt · SKILL.md (reported line 45)May include surrounding context.

md
config: {
          storePath: "~/.openclaw/workspace/data/kv-store.json",
          apiKey: "sk-xxx",
          baseUrl: "https://api.siliconflow.cn/v1",
          model: "BAAI/bge-m3"
        }
      }

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The provider examples normalize remote embedding services as drop-in defaults but do not pair them with disclosure of what data is transmitted. This can mislead users into enabling the feature in environments where outbound transmission of internal metadata is restricted or prohibited.

Content

Scanner excerpt · SKILL.md (reported line 59)May include surrounding context.

bash
# SiliconFlow (default)
export KV_EMBED_API_KEY=***
export KV_EMBED_BASE_URL=https://api.siliconflow.cn/v1
export KV_EMBED_MODEL=BAAI/bge-m3

# OpenRouter

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The OpenAI provider example similarly indicates third-party transmission but lacks a corresponding notice about confidentiality risks. In a KV embedding store, even seemingly harmless lookup phrases can disclose customer names, repository identities, infrastructure labels, or other sensitive operational context.

Content

Scanner excerpt · SKILL.md (reported line 69)May include surrounding context.

OpenAI

export KV_EMBED_API_KEY=*** export KV_EMBED_BASE_URL=https://api.openai.com/v1 export KV_EMBED_MODEL=text-embedding-3-small

text

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explains embedding-based storage and names external providers, but it does not explicitly warn that key texts, stored values, and search queries may be sent to a third-party embedding API during normal use. In a KV store, users are likely to place project metadata, secrets-adjacent notes, or internal references into the store, so silent transmission to external services creates a meaningful confidentiality and compliance risk.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This code reads an API credential from the environment or filesystem and is designed to use it for a third-party embedding service. In an unknown-purpose skill, automatic access to local secrets and external transmission expands the trust boundary and can expose sensitive data without clear justification or user awareness.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/kv_store.py (reported line 29)May include surrounding context.

python
)


BASE_URL = os.environ.get("SILICONFLOW_BASE_URL", "https://api.siliconflow.cn/v1")
MODEL = os.environ.get("EMBED_MODEL", "BAAI/bge-m3")
DEFAULT_THRESHOLD = 0.85

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The embed function sends keys and queries to a third-party API over HTTP(S), which is an external data transfer capability not obviously required by a generic local key-value store. If stored keys or search queries contain secrets, identifiers, or proprietary text, they are disclosed to the remote provider.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

User-supplied keys and queries are transmitted to the embedding API without any warning, masking, or consent step. Because such inputs may contain sensitive business data or personal information, this can create unintended privacy and compliance exposure.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
93% confidence
Finding

The code performs outbound transmission of input texts and an authorization token to an external embedding service. In this skill context, that network egress is security-relevant because it can disclose sensitive content and introduces dependence on a third party for what appears to be local data management functionality.

Content

Scanner excerpt · scripts/kv_store.py (reported line 36)May include surrounding context.

python
def embed(texts: list[str], api_key: str) -> np.ndarray:
    """Get embeddings from SiliconFlow API."""
    resp = requests.post(
        f"{BASE_URL}/embeddings",
        headers={"Authorization": f"Bearer {api_key}"},
        json={"model": MODEL, "input": texts, "encoding_format": "float"},

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The remove method deletes aliases and can delete an entire entry from the backing store, then persists the change to disk. The code provides no confirmation prompt, visible warning, or user-facing disclosure that data may be removed irreversibly.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

When apply=True, dedup automatically merges entries and saves the modified store, which can alter aliases and remove merged entries. The code lacks a confirmation prompt or explicit user-facing warning about this destructive bulk modification behavior.

Content

No source excerpt is available for this finding.