Back to skill

Security audit

Category Selection

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate Amazon category-analysis purpose, but it needs review because it automatically uses Sorftime credentials and can generate report files where third-party data may execute as browser script or spreadsheet formulas.

Install only if you are comfortable giving the skill access to a Sorftime API key and sending category/product queries to Sorftime. Treat generated HTML, CSV, and Excel reports as untrusted files until the publisher fixes HTML/script escaping, innerHTML rendering, spreadsheet formula neutralization, and safer credential handling.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_reports.py:583
Finding
Stored HTML and JavaScript Injection in Generated Dashboard<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_reports.py:583-597`; `assets/dashboard_template.html:481-488`; `assets/dashboard_template.html:602-616` **Vulnerability Type**: Stored cross-site scripting through unsafe JavaScript serialization and DOM insertion **Risk Level**: High ### Vulnerable Code ```python # scripts/generate_reports.py:583-597 chart_data = self._prepare_chart_data() content = content.replace('{{SALES_TREND_DATA}}', json.dumps(chart_data['sales_trend'], ensure_ascii=False)) content = content.replace('{{PRICE_TREND_DATA}}', json.dumps(chart_data['price_trend'], ensure_ascii=False)) content = content.replace('{{PRICE_DIST_DATA}}', json.dumps(chart_data['price_dist'], ensure_ascii=False)) content = content.replace('{{RATING_DIST_DATA}}', json.dumps(chart_data['rating_dist'], ensure_ascii=False)) content = content.replace('{{BRAND_SHARE_DATA}}', json.dumps(chart_data['brand_share'], ensure_ascii=False)) content = content.replace('{{SELLER_SOURCE_DATA}}', json.dumps(chart_data['seller_source'], ensure_ascii=False)) content = content.replace('{{BRAND_RATING_TREND_DATA}}', json.dumps(chart_data['brand_rating_trend'], ensure_ascii=False)) content = content.replace('{{TOP50_PRODUCTS}}', json.dumps(chart_data['top50_products'], ensure_ascii=False)) # Compatibility with legacy template variables content = content.replace('{{STATISTICS_JSON}}', json.dumps(self.statistics, ensure_ascii=False)) content = content.replace('{{PRODUCTS_JSON}}', json.dumps(self.products[:50], ensure_ascii=False)) content = content.replace('{{SCORES_JSON}}', json.dumps(self.scores, ensure_ascii=False)) ``` ```html <!-- assets/dashboard_template.html:481-488 --> <script> const salesTrendData = {{SALES_TREND_DATA}}; const priceTrendData = {{PRICE_TREND_DATA}}; const priceDistData = {{PRICE_DIST_DATA}}; const ratingDistData = {{RATING_DIST_DATA}}; const brandShareData = {{BRAND_SHARE_DATA}}; const sellerSourceData = {{SELLER_SO ...[truncated 3029 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not insert untrusted values through `innerHTML`. Create cells explicitly and assign their values with `textContent`: ```javascript function appendTextCell(row, value) { const cell = row.insertCell(); cell.textContent = String(value ?? ''); } top50Products.forEach((product, index) => { const row = tableBody.insertRow(); appendTextCell(row, index + 1); appendTextCell(row, product.asin); appendTextCell(row, product.title); appendTextCell(row, product.brand); appendTextCell(row, `$${product.price}`); appendTextCell(row, product.rating); appendTextCell(row, product.sales); appendTextCell(row, `${product.marketShare}%`); }); ``` 2. Store serialized data in a non-executable element: ```html <script id="report-data" type="application/json">{{REPORT_DATA}}</script> ``` Parse it using: ```javascript const reportData = JSON.parse( document.getElementById('report-data').textContent ); ``` 3. Before embedding JSON in HTML, escape HTML-significant and script-sensitive characters. At minimum, transform `<`, `>`, `&`, U+2028, and U+2029 into Unicode escape sequences. This prevents literal `</script>` sequences from appearing in the generated file. 4. Apply contextual HTML escaping to direct replacements such as category names, site names, ratings, and seller summaries. 5. Validate API response fields against a strict schema, including expected types and maximum lengths. 6. Add a restrictive Content Security Policy. Prefer a locally bundled script and avoid inline JavaScript so that `script-src 'self'` can be enforced without `unsafe-inline`. 7. Add regression tests using payloads containing `</script>`, event-handler attributes, HTML entities, quotes, backticks, and Unicode line separators. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_reports.py:155
Finding
CSV and Excel Formula Injection Through API-Controlled Product Fields<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_reports.py:155-176`; `scripts/generate_reports.py:943-960` **Vulnerability Type**: Spreadsheet formula injection **Risk Level**: Medium ### Vulnerable Code ```python # scripts/generate_reports.py:155-176 stats_file = self.data_dir / "statistics.csv" with open(stats_file, 'w', newline='', encoding='utf-8-sig') as f: writer = csv.writer(f) writer.writerow(['Metric', 'Value']) for key, value in self.statistics.items(): writer.writerow([key, value]) products_file = self.data_dir / "products.csv" with open(products_file, 'w', newline='', encoding='utf-8-sig') as f: fieldnames = ['ASIN', 'Title', 'Brand', 'Price', 'Monthly Sales', 'Rating', 'Rank'] writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() for i, p in enumerate(self.products, 1): writer.writerow({ 'ASIN': p.get('ASIN', ''), 'Title': p.get('Title', '')[:100], 'Brand': p.get('Brand', ''), 'Price': p.get('Price', 0), 'Monthly Sales': p.get('Monthly Sales', 0), 'Rating': p.get('Rating', 0), 'Rank': i }) ``` The original source uses localized field-name literals; the security-relevant behavior is that values from product and statistics dictionaries are written without formula neutralization. ```python # scripts/generate_reports.py:943-960 ws = wb.create_sheet("Product List") headers = ['Rank', 'ASIN', 'Brand', 'Title', 'Price', 'Monthly Sales', 'Rating'] for col, header in enumerate(headers, 1): cell = ws.cell(row=1, column=col, value=header) cell.font = Font(bold=True) cell.fill = PatternFill( start_color="4472C4", end_color="4472C4", fill_type="solid" ) for row_idx, product in enumerate(self.products, 2): ws.cell(row=row_idx, column=1, value=row_idx - 1) ws.cell(row=row_idx, column=2, value=product.get('ASIN', '')) ws.cell(row=ro ...[truncated 2062 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Introduce a single spreadsheet-sanitization function and apply it to every untrusted textual cell: ```python def sanitize_spreadsheet_text(value): text = str(value or '') normalized = text.lstrip('\t\r\n ') if normalized.startswith(('=', '+', '-', '@')): return "'" + text return text ``` 2. Apply the function to all product fields, statistics keys, statistics values, category names, seller names, and other API-derived strings before writing CSV or XLSX files. 3. For XLSX output, explicitly force untrusted textual cells to string type: ```python cell = ws.cell(row=row_idx, column=4) cell.value = sanitize_spreadsheet_text(product.get('Title', '')) cell.data_type = 's' ``` 4. Keep numeric fields numeric only after strict numeric parsing. Do not write an unvalidated API string into a cell expected to contain a number. 5. Add tests for values beginning with `=`, `+`, `-`, `@`, tabs, carriage returns, newlines, and leading spaces. 6. Document that generated spreadsheets contain third-party marketplace data and should not be treated as trusted input. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/workflow.py:615
Finding
Sorftime API Key Exposed Through URL Query Strings and Process Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/workflow.py:44-78`; `scripts/workflow.py:615-626`; `scripts/analyze_category.py:31-60` **Vulnerability Type**: Sensitive credential exposure **Risk Level**: Low ### Vulnerable Code ```python # scripts/workflow.py:44-78 def get_api_key(): """ Retrieve the Sorftime API key. Priority: environment variable, then .mcp.json. """ api_key = os.environ.get('SORFTIME_API_KEY', '') if api_key: return api_key project_root = get_project_root_early() mcp_config_path = os.path.join(project_root, '.mcp.json') if os.path.exists(mcp_config_path): try: with open( mcp_config_path, 'r', encoding='utf-8', errors='ignore' ) as f: content = f.read() config = json.loads(content) sorftime_url = ( config.get('mcpServers', {}) .get('sorftime', {}) .get('url', '') ) if 'key=' in sorftime_url: api_key = sorftime_url.split('key=')[-1] if api_key: return api_key except Exception as e: print(f"Failed to read .mcp.json: {e}") return '' API_KEY = get_api_key() API_URL = f'https://mcp.sorftime.com?key={API_KEY}' ``` ```python # scripts/workflow.py:615-626 def _curl_request(self, tool_name: str, arguments: dict) -> dict: self.request_id += 1 args_str = json.dumps(arguments, ensure_ascii=False) cmd = [ 'curl', '-s', '-X', 'POST', API_URL, '-H', 'Content-Type: application/json', '-d', f'{{"jsonrpc":"2.0","id":{self.request_id},' f'"method":"tools/call","params":{{"name":"{tool_name}",' f'"arguments":{args_str}}}}}' ] result = subprocess.run( cmd, capture_output=True, text=True, timeout=120, encoding= ...[truncated 2533 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use a provider-supported authorization header instead of a URL query parameter, for example: ```python headers = { 'Content-Type': 'application/json', 'Authorization': f'Bearer {api_key}', } requests.post(base_url, json=payload, headers=headers, timeout=120) ``` 2. If Sorftime supports only query-string authentication, request or adopt a header-based authentication mechanism. Until then, ensure URL-bearing diagnostics and access logs are redacted. 3. Replace the curl subprocess with the Python HTTP client so the secret is not exposed through process arguments. 4. Parse `.mcp.json` URLs using `urllib.parse` rather than `split('key=')`, and reject unexpected hosts or schemes. 5. Restrict `.mcp.json` file permissions to the owning user and ensure it is excluded from version control and report archives. 6. Add centralized secret-redaction logic for errors, logs, telemetry, and debug output. 7. Rotate the API key if there is evidence that process arguments or request URLs have already been logged in an exposed environment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (61)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This mismatch explicitly matters because the skill is framed as benign category-analysis logic while depending on and processing external Sorftime SSE/API data through offline scripts. Hidden external dependencies and data ingestion paths increase risk: users may not expect third-party transmission, local artifact creation, or parser exposure when invoking what appears to be a simple analysis skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This mismatch explicitly matters because the skill is framed as benign category-analysis logic while depending on and processing external Sorftime SSE/API data through offline scripts. Hidden external dependencies and data ingestion paths increase risk: users may not expect third-party transmission, local artifact creation, or parser exposure when invoking what appears to be a simple analysis skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This mismatch explicitly matters because the skill is framed as benign category-analysis logic while depending on and processing external Sorftime SSE/API data through offline scripts. Hidden external dependencies and data ingestion paths increase risk: users may not expect third-party transmission, local artifact creation, or parser exposure when invoking what appears to be a simple analysis skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This mismatch explicitly matters because the skill is framed as benign category-analysis logic while depending on and processing external Sorftime SSE/API data through offline scripts. Hidden external dependencies and data ingestion paths increase risk: users may not expect third-party transmission, local artifact creation, or parser exposure when invoking what appears to be a simple analysis skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This mismatch explicitly matters because the skill is framed as benign category-analysis logic while depending on and processing external Sorftime SSE/API data through offline scripts. Hidden external dependencies and data ingestion paths increase risk: users may not expect third-party transmission, local artifact creation, or parser exposure when invoking what appears to be a simple analysis skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This mismatch explicitly matters because the skill is framed as benign category-analysis logic while depending on and processing external Sorftime SSE/API data through offline scripts. Hidden external dependencies and data ingestion paths increase risk: users may not expect third-party transmission, local artifact creation, or parser exposure when invoking what appears to be a simple analysis skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
This mismatch explicitly matters because the skill is framed as benign category-analysis logic while depending on and processing external Sorftime SSE/API data through offline scripts. Hidden external dependencies and data ingestion paths increase risk: users may not expect third-party transmission, local artifact creation, or parser exposure when invoking what appears to be a simple analysis skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
This mismatch explicitly matters because the skill is framed as benign category-analysis logic while depending on and processing external Sorftime SSE/API data through offline scripts. Hidden external dependencies and data ingestion paths increase risk: users may not expect third-party transmission, local artifact creation, or parser exposure when invoking what appears to be a simple analysis skill.

Ae1

High
Category
analysis-evasion
Content
| `workflow.py` | 一键执行完整分析流程 | **v4.0** |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `workflow.py` | 一键执行完整分析流程 | **v4.0** |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

External Script Fetching

High
Category
Supply Chain
Content
**用途**: 根据品类名称查找对应的类目nodeid

```bash
curl -s -X POST "https://mcp.sorftime.com?key=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"category_name_search","arguments":{"site":"US","searchName":"sofas"}}}'
```
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

MCP Config Access

High
Category
Agent Snooping
Content
def _load_api_key(self) -> str:
        """从配置文件加载 API Key"""
        config_file = Path(".mcp.json")
        if config_file.exists():
            with open(config_file, 'r', encoding='utf-8') as f:
                config = json.load(f)
Confidence
90% confidence
Finding
The code unconditionally reads sensitive credentials from a local .mcp.json file in the current working directory, which gives the skill access to secrets the user may not expect it to consume. In an agent skill context, this is more dangerous because skills often run with ambient file access, so a seemingly harmless analysis command can silently harvest local credentials and immediately use them for outbound requests.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The changelog describes logic that automatically reads an API key from `.mcp.json`, which is a credential-bearing local configuration file, without indicating user consent, scope restriction, or masking/handling safeguards. In an agent skill context, implicit credential discovery expands the trust boundary and can lead to unintended secret use or later leakage through logs, errors, or downstream requests.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill documents and encourages capabilities including shell execution, network access, local file read/write, and environment/config access, but it declares no explicit tool scope or permissions boundaries. In an agent setting, this can cause over-broad execution authority and makes it easier for the skill to access credentials, write files, and call external services without clear user awareness or enforcement.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The natural-language triggers are broad enough that ordinary requests about category analysis may invoke a skill with shell, filesystem, credential-read, and network behaviors. Overbroad activation is dangerous in agent environments because it can cause sensitive actions to run in contexts where the user intended only generic discussion or lightweight advice.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill explicitly states it will automatically read API keys from `.mcp.json` without an explicit user warning or consent step. Automatic credential discovery is sensitive because it normalizes secret access and can expose or misuse tokens in workflows that also perform external network requests to third-party services.

External Transmission

Medium
Category
Data Exfiltration
Content
### 调用格式
```bash
curl -s -X POST "https://mcp.sorftime.com?key=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":N,"method":"tools/call","params":{"name":"TOOL_NAME","arguments":{"amzSite":"US","nodeId":"NODE_ID"}}}'
```
Confidence
94% confidence
Finding
This documents outbound transmission to an external Sorftime endpoint using an API key in the URL and request body data. External transmission is expected for this skill's purpose, but it is still security-sensitive because category inputs, identifiers, and credentials are sent to a third party, and URL-based API keys can be leaked via logs, shell history, proxies, or monitoring systems.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The trigger section defines ambiguous activation conditions without clear boundaries, which can lead to accidental execution of an operational workflow. Given that the workflow includes external API calls and local file generation, accidental triggering raises the risk of unapproved data transmission and unintended side effects.

External Transmission

Medium
Category
Data Exfiltration
Content
#### 步骤 1: 搜索类目获取 nodeId

```bash
curl -s -X POST "https://mcp.sorftime.com?key={API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"category_name_search","arguments":{"amzSite":"US","searchName":"品类关键词"}}}'
```
Confidence
94% confidence
Finding
This is another documented external API call to the Sorftime service, again including the API key in the URL. The main risk is credential leakage and unreviewed third-party data sharing, especially since the skill also encourages automated execution and key retrieval from local configuration.

External Transmission

Medium
Category
Data Exfiltration
Content
**获取 NodeID 的方法**:
```bash
# 先用大类目搜索,查看返回的子类目列表
curl -s -X POST "https://mcp.sorftime.com?key={API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"category_name_search","arguments":{"amzSite":"US","searchName":"Laptop"}}}'
```
Confidence
93% confidence
Finding
The skill continues to instruct outbound requests to the external Sorftime API with credentials embedded in the URL. Repetition increases the likelihood that insecure patterns are copied into real use, propagating secret exposure through logs and command history.

External Transmission

Medium
Category
Data Exfiltration
Content
5. **测试 API 连接**:
```bash
curl -s -X POST "https://mcp.sorftime.com?key={YOUR_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"category_name_search","arguments":{"amzSite":"US","searchName":"Kitchen"}}}'
```
Confidence
93% confidence
Finding
The API connectivity test again sends requests to a third-party endpoint with the key in the query string. Even if intended for troubleshooting, diagnostic commands are often pasted into terminals and CI logs, making credential leakage and unauthorized external transmission more likely.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The HTML document is explicitly fixed to `lang="zh-CN"`, and the visible report text throughout the template is in Chinese. This creates a natural-language locale constraint with no indication that users can opt into another language or that the template is intentionally limited to a China-specific or Chinese-only use case.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This markdown template is entirely written in Chinese, including the title and all section headings, which indicates the skill will produce reports in a fixed language. The file does not mention any user opt-in, language selection, or region-specific justification, so it appears to impose a locale choice unconditionally.

External Transmission

Medium
Category
Data Exfiltration
Content
### 1. category_name_search - 搜索类目

```bash
curl -s -X POST "https://mcp.sorftime.com?key={API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"category_name_search","arguments":{"amzSite":"US","searchName":"Sofas"}}}'
```
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation shows API authentication via a query-string parameter (`?key={API_KEY}`), which is commonly exposed through browser history, shell history, logs, proxies, monitoring systems, and referrer-like telemetry. Even though this is only a reference document, publishing the pattern without any warning normalizes an unsafe credential-handling practice and can lead users to leak real API keys.

Static analysis

No suspicious patterns detected.