T05 · Unauthorized Access and Privilege Escalation
- Location
SKILL.local.md:20- Finding
Supabase Service-Role Credential Access Violates Least Privilege
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The main skill is a document search tool, but the package also includes local instructions for privileged Supabase access to private conversations and contacts, so it needs careful review before installation.
Review this before installing. Use only with documents you are comfortable storing in the generated index and sending to the configured embedding endpoint. Do not use the packaged local Supabase instructions unless you intentionally want an agent to access private conversations, contacts, and a service-role database credential; replace that with scoped read-only credentials first.
SKILL.local.md:20Supabase Service-Role Credential Access Violates Least Privilege
SKILL.local.md:24Skill Scope Includes Broad Access to Private Conversations and Contacts
scripts/build_index.py:52Document Contents Can Be Sent to an Arbitrary Embedding Endpoint
scripts/search.py:62Sensitive Search Queries Can Be Sent to an Arbitrary HTTP Endpoint
SKILL.md:40Unpinned Third-Party Dependencies Create Supply-Chain Exposure
The documentation shows how to extract a Supabase service-role key directly from a local .env file and use it in REST calls. Service-role keys are highly privileged secrets; exposing their file path and encouraging shell extraction increases the risk of credential disclosure, misuse, and full database compromise if the skill is invoked or copied in an unsafe environment.
# Full-text search
SERVICE_ROLE_KEY=$(grep SERVICE_ROLE_KEY /home/endar/docker-stack/supabase/.env | cut -d= -f2)
curl "http://host.docker.internal:8000/rest/v1/knowledge_entries?content=ilike.*GEM*&select=id,title,category" \
-H "apikey: $SERVICE_ROLE_KEY" \
-H "Authorization: Bearer $SERVICE_ROLE_KEY"
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
E = 'turbovec'
except ImportError:
print("ERROR: TurboVec not available. Install with: pip install turbovec")
print("BLAS library required. Install with: sudo apt install libblas3")
print("")
print("Run via the wrapper script (recommended):")
print(" ./scripts/librarian.sh build <input> <output>")
print("")
print("Or set LD_PRELOAD before Python starts:")
print(" LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libblas.so.3 python build_index.py ...")
sys.exit(1)
import numpy as np
import requests
# Optional: BM25 for hybrid search
try:
from rank_bm25 import BM25Okapi
BM25_AVAILABLE = True
except ImportError:
BM25_AVAILABLE = False
print("Note: BM25 not available. Install with: pip install rank-bm25")
# Configuration
DEFAULT_OLLAMA_API = "http://host.docker.internal:11434"
DEFAULT_MODEL = "nomic-embed-text:v1.5"
CHUNK_SIZE = 650 # Characters per chunk
CHUNK_OVERLAP = 100 # Characters overlap between chunks
BIT_WIDTH = 4 # 2, 3, or 4 bits
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
E = 'turbovec'
except ImportError:
print("ERROR: TurboVec not available. Install with: pip install turbovec")
print("BLAS library required. Install with: sudo apt install libblas3")
print("")
print("Run via the wrapper script (recommended):")
print(" ./scripts/librarian.sh search <query> <index>")
print("")
print("Or set LD_PRELOAD before Python starts:")
print(" LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libblas.so.3 python search.py ...")
sys.exit(1)
import numpy as np
import requests
# Optional: BM25 for hybrid search
try:
from rank_bm25 import BM25Okapi
BM25_AVAILABLE = True
except ImportError:
BM25_AVAILABLE = False
# Optional: Flashrank for reranking
try:
from flashrank import Ranker, RerankRequest
FLASHRANK_AVAILABLE = True
except ImportError:
FLASHRANK_AVAILABLE = False
# Configuration
DEFAULT_OLLAMA_API = "http://host.docker.internal:11434"
DEFAULT_MODEL = "nomic-embed-text:v1.5"
# Security: Warn if embedding API is
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
E = 'turbovec'
except ImportError:
print("ERROR: TurboVec not available. Install with: pip install turbovec")
print("BLAS library required. Install with: sudo apt install libblas3")
print("")
print("Run via the wrapper script (recommended):")
print(" ./scripts/librarian.sh search <query> <index>")
print("")
print("Or set LD_PRELOAD before Python starts:")
print(" LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libblas.so.3 python search.py ...")
sys.exit(1)
import numpy as np
import requests
# Optional: BM25 for hybrid search
try:
from rank_bm25 import BM25Okapi
BM25_AVAILABLE = True
except ImportError:
BM25_AVAILABLE = False
# Optional: Flashrank for reranking
try:
from flashrank import Ranker, RerankRequest
FLASHRANK_AVAILABLE = True
except ImportError:
FLASHRANK_AVAILABLE = False
# Configuration
DEFAULT_OLLAMA_API = "http://host.docker.internal:11434"
DEFAULT_MODEL = "nomic-embed-text:v1.5"
# Security: Warn if embedding API is
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
E = 'turbovec'
except ImportError:
print("ERROR: TurboVec not available. Install with: pip install turbovec")
print("BLAS library required. Install with: sudo apt install libblas3")
print("")
print("Run via the wrapper script (recommended):")
print(" ./scripts/librarian.sh search <query> <index>")
print("")
print("Or set LD_PRELOAD before Python starts:")
print(" LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libblas.so.3 python search.py ...")
sys.exit(1)
import numpy as np
import requests
# Optional: BM25 for hybrid search
try:
from rank_bm25 import BM25Okapi
BM25_AVAILABLE = True
except ImportError:
BM25_AVAILABLE = False
# Optional: Flashrank for reranking
try:
from flashrank import Ranker, RerankRequest
FLASHRANK_AVAILABLE = True
except ImportError:
FLASHRANK_AVAILABLE = False
# Configuration
DEFAULT_OLLAMA_API = "http://host.docker.internal:11434"
DEFAULT_MODEL = "nomic-embed-text:v1.5"
# Security: Warn if embedding API is
The skill metadata and opening description frame this as a lightweight document-indexing/search tool, but the body also exposes a broader Supabase-backed knowledge/conversation system with message ingestion, contacts, and scheduled review workflows. This scope expansion can cause an agent or user to invoke the skill in contexts where sensitive conversations and knowledge data are accessed or processed without clear expectation or consent.
The trigger phrases include broad language like 'search my knowledge', 'what do we know about', and 'remember this', which can match routine user requests outside the intended narrow use case. That over-breadth raises the likelihood that the skill will activate inappropriately and access sensitive knowledge stores or conversations without clear user intent.
The documentation introduces WhatsApp ingestion, contact handling, and weekly review/summary workflows that are materially broader than a document-indexing skill. In practice, this increases the chance of over-collection and automated processing of personal data, especially if the skill is triggered for ordinary knowledge-search requests.
Claiming 'Local, free, no API calls' while also documenting HTTP endpoints for embeddings and Supabase access is misleading. Even if the endpoints are local-network services, they are still API calls and may transmit sensitive document text or queries over HTTP, which can undermine user assumptions about privacy and data flow.
The skill describes automatic ingestion of WhatsApp conversations, contact population, and weekly summaries without an equally prominent user warning in the skill description. This creates a significant transparency and consent problem because users invoking a 'librarian' or document-search skill may not expect continual ingestion and summarization of personal communications.
The skill describes capabilities that read and write local files and send document/query content over the network, but it does not declare any explicit tool scope such as permissions or allowed-tools. That mismatch can cause the agent runtime or reviewer to underestimate what the skill can access, increasing the risk of unintended file exposure or outbound data transfer.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# Install BLAS library (required for TurboVec)
sudo apt install libblas3
# Create venv and install dependencies
cd /path/to/the-librarian
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# Install BLAS library (required for TurboVec)
sudo apt install libblas3
# Create venv and install dependencies
cd /path/to/the-librarian
Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.
### Vulnerability History
**v1.1.0 (July 2026):**
- **Critical fix:** `pickle.load` on `bm25_index.pkl` removed. Pickle files can execute arbitrary code when loaded. BM25 index now stored as JSON. Legacy pickle files are detected but refused to load.
- **High fix:** Embedding API URL now checked against localhost. If a non-local API endpoint is configured, a privacy warning is displayed before any data is transmitted.
- **Medium fix:** SKILL.md now documents that document text and search queries are transmitted to the Ollama HTTP API for embedding generation.
Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.
### Vulnerability History
**v1.1.0 (July 2026):**
- **Critical fix:** `pickle.load` on `bm25_index.pkl` removed. Pickle files can execute arbitrary code when loaded. BM25 index now stored as JSON. Legacy pickle files are detected but refused to load.
- **High fix:** Embedding API URL now checked against localhost. If a non-local API endpoint is configured, a privacy warning is displayed before any data is transmitted.
- **Medium fix:** SKILL.md now documents that document text and search queries are transmitted to the Ollama HTTP API for embedding generation.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
ENGINE = 'turbovec'
except ImportError:
print("ERROR: TurboVec not available. Install with: pip install turbovec")
print("BLAS library required. Install with: sudo apt install libblas3")
print("")
print("Run via the wrapper script (recommended):")
print(" ./scripts/librarian.sh build <input> <output>")
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
ENGINE = 'turbovec'
except ImportError:
print("ERROR: TurboVec not available. Install with: pip install turbovec")
print("BLAS library required. Install with: sudo apt install libblas3")
print("")
print("Run via the wrapper script (recommended):")
print(" ./scripts/librarian.sh build <input> <output>")
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
ENGINE = 'turbovec'
except ImportError:
print("ERROR: TurboVec not available. Install with: pip install turbovec")
print("BLAS library required. Install with: sudo apt install libblas3")
print("")
print("Run via the wrapper script (recommended):")
print(" ./scripts/librarian.sh build <input> <output>")
The script sends each document chunk's full text to the configured embedding API, which can expose sensitive local documents to another service. Although the code warns on non-local URLs, the default endpoint uses host.docker.internal and the transfer still occurs over HTTP without authentication or content minimization.
This POST request transmits raw document text in the prompt field to whatever API URL the user supplies, so secrets in indexed files may be disclosed to external systems. In the context of a local document indexing skill, that behavior is security-relevant because users may reasonably expect documents to stay local unless they explicitly choose otherwise.
def get_embedding(text: str, model: str, api_url: str) -> list[float]:
"""Get embedding from Ollama API."""
response = requests.post(
f"{api_url}/api/embeddings",
json={"model": model, "prompt": text},
timeout=120
The script sends the user's query text to an embedding API via HTTP, and the configured endpoint can be changed to a non-local host. Although the code warns on non-local destinations, it does not enforce localhost, HTTPS, authentication, or explicit user confirmation, so indexed content and search queries may be disclosed to remote services.
def get_embedding(text: str, model: str, api_url: str) -> list[float]:
"""Get embedding from Ollama API."""
response = requests.post(
f"{api_url}/api/embeddings",
json={"model": model, "prompt": text},
timeout=60