Back to skill

Security audit

SpecQ Intel Sales

Security checks for vulnerabilities and agentic risk

Overview

This sales-intelligence skill is purpose-aligned overall, but it needs review because it has broad local-data, network, memory, and external-processing paths that are not clearly scoped or disclosed.

Install only if you are comfortable with a local MCP server retaining customer history and, when API keys are configured, sending customer text or recordings to external providers. Use it in an isolated profile or workspace, avoid passing arbitrary database paths or sensitive media, and require review of the database/API and tenant-isolation behavior before use with real customer or business-confidential data.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (7)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
mcp_server.py:216
Finding

Arbitrary Local SQLite Database Access and LLM Disclosure

Content
View full analysis
str: collection = mem.get_chroma_collection() where_filter: dict = {"category": {"$in": ["visit", "feedback"]}} if customer_id: where_filter["customer_id"] = str(customer_id) results = collection.get(where=where_filter, limit=50) if not results or not results["ids"]: return "⚠️ 暂无拜访记录或丢单复盘。请先用 specq_log_visit 沉淀暗数据。" db_data = [] if db_path and db_query and os.path.exists(db_path): try: import sqlite3 conn = sqlite3.connect(db_path) conn.row_factory = sqlite3.Row rows = conn.execute(db_query).fetchall() db_data = [dict(r) for r in rows[:100]] conn.close() except Exception as e: _log.warning(f"本地数据库查询失败: {e}") records_text = json.dumps(records[:20], ensure_ascii=False, indent=2) extra_sections = "" if db_data: extra_sections += f"\n本地数据库数据:\n{json.dumps(db_data[:20], ensure_ascii=False, indent=2)}" user_prompt = f"暗数据记录:\n{records_text}\n{extra_sections}\n\n请提取结构化洞察(最多 {limit} 条):" try: result = await _ask_llm(system_prompt, user_prompt, max_tokens=2000) ``` ### Technical Analysis The MCP tool accepts a caller-controlled filesystem path and an arbitrary SQL statement. It opens any SQLite database readable by the server process and executes the supplied query without restricting the path, tables, columns, or permitted SQL operation. The resulting rows are serialized into `extra_s ...[truncated 1615 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
mcp_server.py:216
Finding

Server-Side Request Forgery Through Unrestricted API URL

Content
View full analysis
str: collection = mem.get_chroma_collection() where_filter: dict = {"category": {"$in": ["visit", "feedback"]}} if customer_id: where_filter["customer_id"] = str(customer_id) results = collection.get(where=where_filter, limit=50) if not results or not results["ids"]: return "⚠️ 暂无拜访记录或丢单复盘。请先用 specq_log_visit 沉淀暗数据。" api_data = [] if api_url: try: import httpx async with httpx.AsyncClient(timeout=10.0) as c: r = await c.get(api_url, params=api_params or {}) if r.status_code == 200: raw = r.json() api_data = raw if isinstance(raw, list) else [raw] except Exception as e: _log.warning(f"在线API数据拉取失败: {e}") extra_sections = "" if api_data: extra_sections += f"\n在线 API 数据:\n{json.dumps(api_data[:20], ensure_ascii=False, indent=2)}" user_prompt = f"暗数据记录:\n{records_text}\n{extra_sections}\n\n请提取结构化洞察(最多 {limit} 条):" try: result = await _ask_llm(system_prompt, user_prompt, max_tokens=2000) ``` ### Technical Analysis The tool performs an HTTP GET request to an arbitrary caller-supplied URL. It does not validate the scheme, hostname, resolved address, port, redirect destination, or whether the target is a loopback, link-local, private, reserved, or cloud metadata address. If the target returns JSON with HTTP status 200, the response is inserted into an LLM sampling pro ...[truncated 1118 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
memory.py:24
Finding

External Transmission of Customer Text Through Configurable Embedding API

Content
View full analysis
dict: """Generic Embedding API configuration.""" return { "url": os.getenv("EMBEDDING_API_URL", "https://api.deepseek.com/v1/embeddings"), "key": os.getenv("EMBEDDING_API_KEY", ""), "model": os.getenv("EMBEDDING_MODEL", "deepseek-embed"), } def embed_text(text: str) -> list[float]: """Vectorize text through an API when configured, otherwise use a local hash.""" cfg = get_embedding_config() if cfg["key"]: import httpx try: resp = httpx.post( cfg["url"], json={"model": cfg["model"], "input": text}, headers={"Authorization": f"Bearer {cfg['key']}", "Content-Type": "application/json"}, timeout=10, ) if resp.status_code == 200: data = resp.json() return data["data"][0]["embedding"] except Exception: pass return _hash_embed(text) ``` ### Technical Analysis When `EMBEDDING_API_KEY` is configured, `embed_text` sends the full plaintext input to `EMBEDDING_API_URL`. The function is used for visit notes, feedback, generated intelligence summaries, and search queries. These values can contain confidential customer requirements, competitor information, loss reviews, pricing sensitivity, and other sales intelligence. The endpoint is fully configurable and defaults to a third-party host. There is no destination allowlist, redaction, sensitivity check, or operation-level consent. This conflicts with the documentation's broad assertion that dark data remains local and is not uploaded to third parties. ### Attack Path 1. An embedding API key is configured, causing remote embedding mode to activate. 2. A user saves a v ...[truncated 847 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
multimodal.py:24
Finding

External Upload of Customer Audio Recordings

Content
View full analysis
str: """Convert speech to text.""" if not os.path.exists(audio_path): return "" try: api_key = os.getenv("LLM_API_KEY") or os.getenv("DEEPSEEK_API_KEY", "") if not api_key: return f"[audio: {os.path.basename(audio_path)}] (API key not configured)" import openai client = openai.OpenAI( api_key=api_key, base_url=os.getenv("LLM_BASE_URL", "https://api.deepseek.com") ) with open(audio_path, "rb") as f: resp = client.audio.transcriptions.create(model="whisper-1", file=f) return resp.text[:2000] except ImportError: return f"[audio: {os.path.basename(audio_path)}] (OpenAI package unavailable)" except Exception as e: return f"[audio transcription failed: {os.path.basename(audio_path)} - {e}]" ``` ### Technical Analysis When a supported API key is present, the complete file identified by `audio_path` is uploaded to the configured `LLM_BASE_URL`. The path is accepted through `specq_log_visit`, and processing occurs automatically when the file exists. There is no explicit per-file confirmation, file-size restriction, media validation, destination allowlist, redaction, or warning that the recording will leave the host. The behavior conflicts with the project's local-storage and no-third-party-upload assurances. ### Attack Path 1. A transcription API key is present in the environment. 2. A caller invokes `specq_log_visit` and supplies a readable local audio path. 3. `process_audio` opens that file. 4. The OpenAI-compatible client uploads the complete file to `LLM_BASE_URL`. 5. The remote provider receives the recording and returns a transcript. 6. Part of the transcript is added to the ...[truncated 448 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
mcp_server.py:33
Finding

Missing Tenant Isolation in Persistent Sales Memory

Content
View full analysis
str: if action == "recall": vec = mem.embed_text(query) collection = mem.get_chroma_collection() kwargs = {"query_embeddings": [vec], "n_results": limit} if uid != "default": kwargs["where"] = {"user_id": uid} results = collection.query(**kwargs) ``` ```python async def specq_generate_intel(product: str, application: str, scenario: str, output_format: str = "markdown", context_block: str | None = None, anonymize: bool = True) -> str: enhanced_scenario = scenario try: vec = mem.embed_text(f"{product} {application}") collection = mem.get_chroma_collection() results = collection.query(query_embeddings=[vec], n_results=5) context_parts = [] if results and results["ids"] and results["ids"][0]: for i, mid in enumerate(results["ids"][0]): doc = results["documents"][0][i] meta = results["metadatas"][0][i] if results["metadatas"] else {} context_parts.append( f"- [{meta.get('category','')}] " f"{meta.get('timestamp','')}: {doc[:200]}" ) ``` ```python collection2.add( ids=[f"mem_{uuid.uuid4().hex[:12]}"], embeddings=[vec2], documents=[summary], metadatas=[{ ...[truncated 2174 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
mcp_server.py:119
Finding

Indirect Prompt Injection Through Untrusted Search, Memory, Database, and API Content

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Unpinned and Unbounded Third-Party Dependencies

Content
View full analysis
=1.0.0 # Network search httpx>=0.28.0 # ChromaDB semantic memory chromadb>=0.5.0 # Multimodal processing Pillow>=12.0.0 pytesseract>=0.3.0 # Output processing python-docx>=1.2.0 python-pptx>=1.0.2 ``` ### Technical Analysis Every dependency uses a lower-bound-only constraint. Installation can therefore resolve to any future release satisfying the minimum version. There is no lock file, upper bound, or package hash to ensure that an installed artifact matches a reviewed build. The audit did not identify a known malicious package name or dependency-confusion namespace in this file. The confirmed issue is unsafe, non-reproducible dependency resolution rather than evidence that a current dependency is malicious. ### Attack Path 1. A user follows the documented instruction to run `pip install -r requirements.txt`. 2. The package index resolves the broad constraints at installation time. 3. A future compromised, vulnerable, or behaviorally incompatible release satisfies a listed minimum version. 4. That release is downloaded and installed without hash verification. 5. Its installation or runtime code executes with the privileges of the user running SpecQ. ### Impact Assessment The eventual impact depends on the affected dependency. A compromised package could execute code with the installing user's privileges, access SpecQ's customer-memory files, read environment API keys, or use network access. Broad constraints also increase the likelihood of availability failures caused by incompatible future releases. ]]>
Remediation
View remediation
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
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (77)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The design permits arbitrary local SQLite query execution and arbitrary outbound API requests from user-controlled parameters. That creates a powerful data-exfiltration primitive: a user can read unintended local data via SQL and transmit or enrich it through attacker-controlled endpoints, which is not justified for a sales-intelligence assistant.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documented v2.0 behavior substantially exceeds the skill's described purpose by adding persistent memory, web search, multimodal ingestion, database/API access, and analytics. This kind of capability creep is dangerous because users and reviewers may grant trust based on a narrow manifest while the implementation can collect, retrieve, and emit much broader classes of data.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The design ingests images, recordings, and videos, extracts their contents, merges them into visit content, and stores summaries, yet provides no user-facing warning that potentially sensitive multimedia data will be processed and retained. This is especially risky because such media can contain bystander PII, confidential documents, or unrelated local information not obvious from the original prompt.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

Allowing user-supplied db_path, db_query, api_url, and api_params creates a broad capability to read arbitrary local SQLite files and fetch arbitrary remote resources, far beyond the stated purpose of generating sales intelligence packages. This opens the door to unauthorized data access, SSRF-style internal probing, sensitive file exposure, and extraction of unrelated enterprise data into LLM context and outputs.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · docs/archived/SpecQ_主文档_v4.0.md (reported line 212)May include surrounding context.

│ └── crm_models.py # Customer / Visit / LossReview ├── data/ # SPECQ_DATA_DIR ├── tests/ ├── .env # 本地环境变量 └── deploy.sh # rsync 部署脚本

text

Ssd 3

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The insight extraction function combines visit records with optional database and API data into a single prompt and asks the model to derive structured insights, creating a broad semantic exfiltration path for collected data. This is especially risky in an enterprise sales setting because multiple sensitive sources are fused and transmitted together, increasing both exposure scope and downstream inference risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The insight extraction flow packages local database rows and remote API responses into the LLM prompt without an explicit disclosure or approval boundary. This is dangerous because sensitive internal data can be silently transmitted to the model layer, expanding exposure beyond the original storage location and making unintended third-party disclosure more likely.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README explicitly describes collecting and correlating customer visit history, lost-deal reviews, and competitor intelligence, but does not provide clear warnings or handling guidance for sensitive business data. In a sales-intelligence skill, this can lead users to input confidential customer, pricing, and competitive information into agent workflows without understanding retention, exposure, or policy implications.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
76% confidence
Finding

The privacy section states that real customer names in outputs are automatically desensitized and that dark data is not uploaded to third-party servers. However, the usage example explicitly asks for an intel package for the named customer "深南电路" and the workflow says the AI will recall that customer's history, showing the documentation mixes a privacy guarantee with behavior centered on handling real customer identifiers. Without supporting code here, the documented privacy claim is in tension with the rest of the documented behavior.

Content

No source excerpt is available for this finding.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding

Suspicious Unicode normalization or mixed-script content

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares broad operational capabilities via a local MCP server, local storage, environment-variable use, and optional network access, but it does not define an explicit permission boundary such as allowed tools or scoped capabilities. In practice this creates an over-privileged integration where an agent may invoke file, env, or network functionality without clear user-visible constraints, increasing the blast radius for data exfiltration or unintended local access.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The file presents the skill name, description, triggers, workflow, and example invocations entirely in Chinese, and the usage examples assume Chinese interaction without indicating that users may choose another language. This creates a natural-language locale constraint that is not documented as optional or justified as a region-specific limitation.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The trigger phrases are broad enough to match ordinary business conversation such as 'continue', 'what did we discuss before', or simple status updates, which can cause the skill to activate and pull customer memory unexpectedly. In a sales-intelligence context, accidental activation is risky because it may surface sensitive historical CRM-like data into the current conversation without deliberate user intent.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill requires persistent recall on every conversation and mandates front-loading historical customer interaction data into each generated output. In this context, that design materially increases the risk of unnecessary disclosure of sensitive commercial history, prior deal outcomes, pricing objections, and customer-specific preferences to the wrong conversation, user, or task.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The FAQ claims customer data is not uploaded to third-party servers, yet the skill also advertises use of external Brave Search and reuse of the agent's LLM through MCP Sampling. That creates a potentially misleading trust boundary: users may believe all data stays local even though prompts, search queries, or derived customer intelligence could be sent to external providers depending on configuration and implementation.

Content

No source excerpt is available for this finding.

Indirect Prompt Extraction

Medium
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains patterns that could indirectly extract system prompts through rephrasing, translation, summarization, or side-channel techniques.

Content

Scanner excerpt · docs/OpenCode_Prompt_v2.0.md (reported line 1)May include surrounding context.

md
# SpecQ v2.0 OpenCode Prompt

> 基准代码:https://github.com/daizehua-wq/Specq-mcp (v1.0, commit a4d968e)
> 设计文档:本地 `SpecQ_v2.0_设计规格.md` + 飞书 https://www.feishu.cn/docx/Us68dycZJosRIQxMQ5ccI3eInZb

Indirect Prompt Extraction

Medium
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains patterns that could indirectly extract system prompts through rephrasing, translation, summarization, or side-channel techniques.

Content

Scanner excerpt · docs/archived/SpecQ_主文档_v4.0.md (reported line 376)May include surrounding context.

md
# SpecQ v2.0 OpenCode Prompt

> 基准代码:https://github.com/daizehua-wq/Specq-mcp (v1.0, commit a4d968e)
> 设计文档:本地 `SpecQ_v2.0_设计规格.md` + 飞书 https://www.feishu.cn/docx/Us68dycZJosRIQxMQ5ccI3eInZb

Indirect Prompt Extraction

Medium
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains patterns that could indirectly extract system prompts through rephrasing, translation, summarization, or side-channel techniques.

Content

Scanner excerpt · docs/archived/SpecQ_主文档_v4.0.md (reported line 380)May include surrounding context.

md
# SpecQ v2.0 OpenCode Prompt

> 基准代码:https://github.com/daizehua-wq/Specq-mcp (v1.0, commit a4d968e)
> 设计文档:本地 `SpecQ_v2.0_设计规格.md` + 飞书 https://www.feishu.cn/docx/Us68dycZJosRIQxMQ5ccI3eInZb

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The prompt materially expands the skill from sales-intelligence generation into persistent memory, broad search, analytics logging, and multimodal ingestion. This increases the attack surface and data-handling scope well beyond the original purpose, making accidental overcollection and misuse of sensitive business information more likely.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The prompt instructs persistent storage of user content and working memory without a clear warning that sensitive commercial information will be retained. Because the skill handles visit notes, feedback, and customer context, silent persistence raises confidentiality, over-retention, and unauthorized reuse risks.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill adds outbound web search and scraping without any user-facing disclosure that queries and possibly contextual business data may be sent to external services. In a sales-intelligence context, search strings can contain confidential customer names, product issues, or competitive strategy, making silent transmission particularly risky.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The fallback from a structured search API to ad hoc Google scraping broadens the tool into uncontrolled web scraping. This bypasses normal governance around outbound requests and can leak sensitive query/context data to third parties while producing unpredictable content ingestion behavior.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

The Brave Search API integration is an explicit external transmission channel. In this skill, search queries may embed confidential customer names, product defects, or deal strategy, so sending them to a third-party service without strong minimization and disclosure can leak commercially sensitive information.

Content

Scanner excerpt · docs/OpenCode_Prompt_v2.0.md (reported line 84)May include surrounding context.

md
source: "web" | "news" | "scholar"
    
    使用 Brave Search API:
      https://api.search.brave.com/res/v1/web/search?q={query}&count={limit}
      Header: Accept: application/json, X-Subscription-Token: {SEARCH_API_KEY}
    
    如果 SEARCH_API_KEY 未配置,使用 requests + BeautifulSoup 做基础 Google 抓取作为 fallback

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The multimodal functions process local images, audio, and video, potentially containing highly sensitive customer or employee information, without clear notice, consent, retention rules, or isolation guarantees. If cloud OCR/transcription is used, this also creates hidden external transmission of raw media and extracted text.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The prompt declares anonymization as mandatory, but the implementation explicitly leaves anonymization unimplemented and relies only on prompt guidance. This creates a misleading safety boundary and can result in direct disclosure of customer names, visit notes, and other sensitive business data in generated outputs or downstream storage.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.