T09 · Insecure Skill Coding Practices
- Location
rag/novel_rag.py:254- Finding
Arbitrary Code Execution Through Unsafe Pickle Deserialization
- Content
View full analysis
Optional[NovelRAGIndex]: """Load persisted index from disk.""" path = Path(project_dir) / RAG_CACHE_FILE if not path.exists(): return None try: data = pickle.loads(path.read_bytes()) idx = cls.__new__(cls) idx.project_dir = Path(project_dir) idx._docs = data["docs"] idx._vectorizer = data["vectorizer"] idx._tfidf_matrix = data["tfidf_matrix"] idx._story_state = None print(f"[NovelRAG] Index loaded from {path} ({len(idx._docs)} docs)") return idx except Exception as e: print(f"[NovelRAG] Failed to load cache: {e}") return None ``` The cache is automatically loaded by the context agent: ```python def _init_rag(self) -> None: """Lazy-init RAG index.""" if not self._use_rag or self._rag_index is not None: return idx = NovelRAGIndex.load(self.project_dir) if idx is None: idx = NovelRAGIndex(self.project_dir) idx.build() idx.save() self._rag_index = idx ``` ### Technical Analysis Python pickle data is executable serialization rather than a passive data format. During `pickle.loads`, specially constructed objects can invoke attacker-selected callables through methods such as `__reduce__`. Consequently, validating the resulting dictionary after deserialization cannot prevent code execution because the payload executes while it is being decoded. The cache file, `.rag_index.pkl`, resides inside the selected novel project directory. If a project originates from an untrusted archive, repository, shared directory, or collaborator, an attacker can include a malicious cache alongside otherw ...[truncated 1737 chars]- Remediation
View remediation
