Back to skill

Security audit

Semantic Vector Store

Security checks for vulnerabilities and agentic risk

Overview

The skill’s semantic memory purpose is coherent, but its local vector store can automatically unpickle persisted files, creating a real code-execution risk if those files are tampered with.

Review this skill before installing in environments where local files can be shared, synced, imported, or modified by other users or processes. Do not use vector databases or FAISS sidecar files from untrusted sources until pickle persistence is replaced with a safe format. Also confirm whether your embedding setup is local or remote before storing sensitive memories.

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 (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/semantic_vector_store.py:191
Finding

Unsafe Pickle Deserialization Enables Arbitrary Code Execution

Content
View full analysis

Vulnerability Details

File Location: scripts/semantic_vector_store.py, lines 191–192
Vulnerability Type: Unsafe deserialization
Risk Level: High

Vulnerable Code

python
id_map_file = Path(filepath).with_suffix('.ids.pkl')
if id_map_file.exists():
    with open(id_map_file, 'rb') as f:
        self.vector_ids = pickle.load(f)

Related unsafe deserialization also occurs at lines 275 and 487:

python
vector = pickle.loads(embedding_blob)

Technical Analysis

Python's pickle format is executable serialization rather than a safe data-interchange format. During pickle.load() or pickle.loads(), specially crafted objects can invoke attacker-selected callables through pickle reduction opcodes. Consequently, deserializing a malicious pickle can execute arbitrary Python code before the application validates or uses the resulting value.

The implementation automatically loads the .ids.pkl file adjacent to the configured FAISS index when VectorStore initializes. It also deserializes embedding blobs retrieved from the SQLite database while rebuilding the index and conducting linear searches. None of these operations authenticate the serialized data, restrict the allowed object types, or verify file integrity.

Attack Path

  1. An attacker creates a malicious pickle whose deserialization invokes an attacker-selected command or Python callable.
  2. The attacker places it at the ID-map path derived from the configured database and FAISS index, or inserts it into an embedding field in the SQLite database.
  3. The victim initializes VectorStore using the affected storage path, rebuilds the FAISS index from the database, or performs a linear search.
  4. The application invokes pickle.load() or pickle.loads() on the attacker-controlled data.
  5. The malicious reduction operation executes with the privileges of the process running the Skill.

Exploitation requires the attacker to ...[truncated 558 chars]

Remediation
View remediation

Remediation Suggestions

  1. Eliminate pickle for persistent data that may cross a trust boundary.
  2. Store the FAISS ID mapping as JSON containing an array of strings. After loading, verify that every entry is a string and that the number of entries equals the number of vectors in the FAISS index.
  3. Store embeddings in a non-executable representation, such as fixed-width float32 bytes or a NumPy data format that does not permit object arrays. Validate the byte length, dtype, dimension, shape, and finiteness of every vector before use.
  4. Add a migration utility that converts trusted legacy pickle artifacts into the new format. Do not automatically deserialize legacy files.
  5. If legacy pickle support is temporarily unavoidable, require explicit administrative opt-in and verify artifacts against a trusted cryptographic signature before loading. File permissions alone should not be treated as sufficient protection.
  6. Restrict storage files to an application-owned directory with least-privilege permissions, reject symbolic links where appropriate, and avoid accepting vector databases from untrusted sources.
  7. On malformed or unauthenticated data, fail closed and rebuild the index only from validated records.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill explicitly advertises external vector backends such as Pinecone and Weaviate, and supports multiple embedding providers, but does not warn users that memory content may be transmitted to third-party services for embedding, indexing, or search. In a memory/semantic search context, that omission can lead to unintentional disclosure of sensitive user memories or metadata to external systems, especially when operators assume all processing is local.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module-level description and many user-visible strings are entirely in Chinese, which imposes a specific language/locale in the skill's natural-language interface. There is no indication that the user can choose another language or that the locale restriction is intentional and documented.

Content

No source excerpt is available for this finding.

Insecure deserialization: pickle.load()

Medium
Category
Dangerous Code Execution
Confidence
99% confidence
Finding

Loading self.vector_ids via pickle.load() from a sidecar .ids.pkl file is unsafe because any attacker-controlled file at that path can trigger arbitrary code execution on load. The risk is elevated by automatic loading during startup/index initialization, which makes exploitation low-friction if local files are writable or replaceable.

Content

Scanner excerpt · scripts/semantic_vector_store.py (reported line 192)May include surrounding context.

python
id_map_file = Path(filepath).with_suffix('.ids.pkl')
            if id_map_file.exists():
                with open(id_map_file, 'rb') as f:
                    self.vector_ids = pickle.load(f)
            else:
                # 重建向量ID(假设顺序匹配)
                self.vector_ids = [f"vector_{i}" for i in range(self.index.ntotal)]

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code initializes a persistent SQLite database under the user's home directory and later stores text content, embeddings, and metadata in it, while also supporting cleanup deletion of stored records. Although there are internal docstrings and logs, there is no clear user-facing warning at the points where data is persisted or deleted to disclose that user content will be written to local disk and may later be removed.

Content

No source excerpt is available for this finding.

Insecure deserialization: pickle.loads()

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

The code deserializes embedding_blob from the SQLite database using pickle.loads(). If an attacker can tamper with the database file or influence stored blob contents, unpickling can execute arbitrary code during index rebuild, making this a real code-execution risk rather than a mere data-integrity issue.

Content

Scanner excerpt · scripts/semantic_vector_store.py (reported line 275)May include surrounding context.

python
for memory_id, embedding_blob in rows:
            try:
                # 反序列化向量
                vector = pickle.loads(embedding_blob)
                if len(vector) == self.dimension:
                    vector_ids.append(memory_id)
                    vectors.append(vector)

Insecure deserialization: pickle.loads()

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

This linear-search fallback also deserializes embeddings from the database with pickle.loads(), exposing the same arbitrary code execution path whenever FAISS is unavailable or bypassed. Because this path may be hit in degraded environments, it broadens exposure instead of containing it.

Content

Scanner excerpt · scripts/semantic_vector_store.py (reported line 487)May include surrounding context.

python
for memory_id, text_content, embedding_blob, metadata_json in rows:
            try:
                vector = pickle.loads(embedding_blob)
                vector_np = np.array(vector, dtype=np.float32)
                
                # 计算余弦相似度

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The natural-language instructions and usage documentation are entirely in Chinese, with no indication that users may choose another language or that the skill is intentionally limited to a Chinese-speaking context. Under the policy, forcing a specific language without user opt-in is a language/locale policy concern.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The fallback model is documented and logged as using '随机向量' (random vectors), but the code seeds NumPy with a hash of the input text before generation. That means the same text will always produce the same vector within a process, which contradicts the stated behavior rather than merely omitting detail.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The inline comment states '计算余弦相似度' and justifies it by saying vectors are already normalized, but this method deserializes stored vectors directly and does not verify or enforce normalization before taking the dot product. Since add_vector accepts arbitrary precomputed vectors, the code can compute a plain dot product on non-normalized vectors, contradicting the comment's stated semantics.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.