Back to skill

Security audit

Geo Brand Master

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real brand visibility monitoring skill, but it needs review because it sends business queries to external services and ships unsafe API and licensing controls.

Install only if you are comfortable sending brand names, competitor terms, result snippets, and reports to external AI/search services and to Feishu when configured. Do not expose the bundled Flask API publicly unless the hardcoded key, input validation, rate limits, and network binding are fixed. Review the local quota/report files and pin dependencies before production use.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
api/geo_api.py:21
Finding
Hardcoded API Key Grants Unauthorized Pro Service Access<![CDATA[ ## Vulnerability Details **File Location**: `api/geo_api.py`, lines 21-23 **Vulnerability Type**: Hardcoded authentication credential **Risk Level**: High ### Vulnerable Code ```python API_KEYS = { "pro_key_placeholder": {"tier": "pro", "brand_limit": float('inf')}, } ``` The credential is also used directly by the test client in `api/test_api.py`, lines 6-13: ```python API_URL = "http://localhost:8080/search" TEST_KEY = "pro_key_placeholder" def test_search(brand="91tokenhub"): """Test search""" resp = requests.post( API_URL, headers={"X-API-Key": TEST_KEY}, json={"brand": brand, "max_results": 5} ) ``` ### Technical Analysis The API authenticates clients by comparing the `X-API-Key` header against the static `API_KEYS` dictionary. The repository-visible value `pro_key_placeholder` is therefore an active credential rather than an inert example. Anyone with access to the source code can authenticate as a Pro user. The associated `brand_limit` is infinite, and the application does not implement effective per-key request quotas or rate limiting. If this service is exposed beyond localhost, the credential can be used by unauthorized clients to invoke searches that consume the server operator's Tavily API allowance. Static plaintext key comparison also prevents safe credential rotation, auditing, expiration, and revocation without modifying and redeploying the application. ### Attack Path 1. An attacker reads the public or otherwise accessible project source. 2. The attacker extracts `pro_key_placeholder` from `api/geo_api.py` or `api/test_api.py`. 3. The attacker identifies an exposed deployment of the Flask `/search` endpoint. 4. The attacker sends requests containing: ```http X-API-Key: pro_key_placeholder ``` 5. `verify_api_key()` accepts the credential and assigns the Pro tier. 6. Each authenticated request causes the service to invoke Tavily using the server-owned `TAVILY_API_KEY`. 7. The at ...[truncated 619 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the hardcoded credential from both production and test code. 2. Immediately rotate or revoke `pro_key_placeholder` in every deployed environment. 3. Load production credentials from a protected secret manager or deployment secret. 4. Store only cryptographic hashes of client API keys, using constant-time comparison where applicable. 5. Give test environments separate credentials that cannot authenticate to production. 6. Add credential expiration, rotation, revocation, and usage-auditing support. 7. Implement per-key rate limits, concurrency limits, and Tavily quota budgets. 8. Reject startup in production if known placeholder or example credentials are configured. 9. Add secret scanning to CI to prevent future plaintext credentials from being committed. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
api/geo_api.py:86
Finding
Unvalidated Search Parameters Permit Upstream Resource Abuse<![CDATA[ ## Vulnerability Details **File Location**: `api/geo_api.py`, lines 86-94 **Vulnerability Type**: Missing input validation and resource controls **Risk Level**: Medium ### Vulnerable Code ```python # Get request data data = request.get_json() if not data or "brand" not in data: return jsonify({"error": "Missing brand parameter"}), 400 brand = data["brand"] max_results = data.get("max_results", 5) # Invoke Tavily search result = search_brand_tavily(brand, max_results) ``` The values are passed to the upstream request in lines 34-44: ```python response = requests.post( TAVILY_API_URL, json={ "api_key": TAVILY_API_KEY, "query": f"{brand_name} Brand AI Service", "search_depth": "basic", "max_results": max_results, "include_answer": True, }, timeout=30 ) ``` ### Technical Analysis The `/search` endpoint only checks that a `brand` property exists. It does not verify that `brand` is a string, constrain its length, or reject control characters and complex JSON values. Similarly, `max_results` has no integer type check or upper and lower bounds. These caller-controlled values are forwarded to Tavily while the application supplies its own privileged API key. This creates a resource-abuse boundary: a minimally authenticated caller can influence the size and computational cost of upstream requests. The issue is amplified by the repository-visible API key and the absence of request throttling, body-size limits, per-client quotas, or concurrency controls. ### Attack Path 1. An attacker authenticates to `/search`, including by using the exposed static key. 2. The attacker supplies an oversized `brand` value, an invalid type, or an extreme `max_results` value. 3. The Flask application forwards the values to Tavily without local normalization or bounds checking. 4. The attacker submits many such requests concurrently or sequentially. 5. The application consumes outbound connections, worker time, me ...[truncated 650 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `brand` to be a string with an explicit length limit, such as 1-200 characters. 2. Normalize whitespace and reject control characters or unsupported input forms. 3. Require `max_results` to be an integer within a narrow range, such as 1-10. 4. Configure Flask or the reverse proxy with a maximum request-body size. 5. Apply per-key and per-IP rate limits. 6. Enforce daily and monthly upstream quota budgets for every client key. 7. Limit concurrent outbound searches and queue excess requests. 8. Return generic upstream failure messages rather than exposing raw exception text. 9. Record rejected and high-volume requests in security monitoring. 10. Add tests covering oversized strings, JSON objects, arrays, negative values, floating-point values, and extreme integers. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/geo_report.py:139
Finding
Predictable Shared Temporary File Enables Symlink-Based File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/geo_report.py`, lines 139-142 **Vulnerability Type**: Unsafe temporary-file creation **Risk Level**: Medium ### Vulnerable Code ```python timestamp = int(time.time()) report_file = "/tmp/geo_report_full_%d.md" % timestamp with open(report_file, "w") as f: f.write(full_report) ``` ### Technical Analysis The report filename is based only on the current Unix timestamp in seconds and is created in the shared `/tmp` directory. Another local user can predict the filename around the time a scan is run. The normal `open(..., "w")` operation follows symbolic links and truncates an existing target. The code does not use exclusive creation, verify ownership, reject links, or set restrictive permissions explicitly. This creates a time-of-check/time-of-use and symlink attack opportunity on systems where multiple users or processes share `/tmp`. The report may also contain brand names, snippets returned by AI platforms, errors, scores, and analysis. Default file permissions are determined by the process umask and may allow unintended local disclosure. ### Attack Path 1. A local attacker observes or predicts when `geo_report.py` will execute. 2. The attacker calculates the expected timestamp-based filename, for example: ```text /tmp/geo_report_full_1760000000.md ``` 3. Before report creation, the attacker places a symbolic link at that path pointing to a file writable by the victim process. 4. The Skill executes `open(report_file, "w")`. 5. Python follows the symbolic link and truncates or overwrites the target with report content. 6. Alternatively, the attacker pre-creates or monitors the predictable report file to access sensitive report data. ### Impact Assessment Exploitation requires local access to the same host and favorable timing. It does not inherently elevate the attacker to root, but it can abuse the privileges of the account running the Skill. The possible scope includes: - Ove ...[truncated 310 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use Python's secure temporary-file facilities: ```python import os import tempfile with tempfile.NamedTemporaryFile( mode="w", encoding="utf-8", prefix="geo_report_full_", suffix=".md", delete=False ) as report: os.chmod(report.name, 0o600) report.write(full_report) report_file = report.name ``` 2. Prefer a private per-user report directory with permissions `0700` when reports need to persist. 3. Create files atomically with exclusive creation and reject pre-existing paths. 4. Ensure report files use permissions no broader than `0600`. 5. Never run the report generator as root unless strictly required. 6. Define retention and deletion rules for reports containing potentially sensitive business data. ]]>

T08 · Insecure Dependencies

Warning
Location
api/requirements.txt:1
Finding
Open-Ended Dependency Versions Create Non-Reproducible Supply-Chain Exposure<![CDATA[ ## Vulnerability Details **File Location**: `api/requirements.txt`, lines 1-3 **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Vulnerable Code ```text flask>=2.0 requests>=2.25 gunicorn>=20.0 ``` The installation documentation also recommends an unpinned Playwright installation in `README.md`: ```bash pip install playwright && playwright install chromium ``` ### Technical Analysis Every API dependency uses only a lower version bound. A future installation can therefore select any newer package version available from the configured package index. The installed dependency graph is not reproducible and may change without source-code review. This is not evidence that Flask, Requests, Gunicorn, or Playwright are malicious. The security issue is that the project does not constrain installations to reviewed artifacts or verify package hashes. A compromised release, vulnerable future version, incompatible major release, or compromised package index could consequently introduce unreviewed code into the deployment. Python packages execute code during installation and application startup, while Playwright additionally downloads a browser binary. These components operate with the privileges of the installing or running account. ### Attack Path 1. An operator installs dependencies using the provided requirements or README command. 2. The package resolver selects the newest version satisfying each open-ended constraint. 3. A selected release or transitive dependency contains a vulnerability or has been compromised. 4. The package or installation logic executes under the operator's account. 5. The affected dependency gains access to the application's runtime environment, including environment variables, files, and network capabilities available to that account. ### Impact Assessment The exact impact depends on the behavior of a future compromised or vulnerable dependency; no currently malicious dependency was identified ...[truncated 419 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin all direct dependencies to reviewed versions. 2. Generate a lock file that also fixes transitive dependency versions. 3. Require package hashes, for example through `pip-compile --generate-hashes`. 4. Install packages with hash verification enabled: ```bash pip install --require-hashes -r requirements.txt ``` 5. Pin Playwright to a reviewed version and document the expected browser build. 6. Use a trusted internal mirror or controlled package index for production builds. 7. Run dependency vulnerability scanning and automated update review in CI. 8. Build dependencies in an isolated, non-privileged environment. 9. Rebuild and test intentionally when dependency versions change rather than accepting automatic upgrades. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (45)

Tainted flow: 'TAVILY_API_KEY' from os.environ.get (line 15, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
def search_brand_tavily(brand_name, max_results=5):
    """使用Tavily搜索品牌信息"""
    try:
        response = requests.post(
            TAVILY_API_URL,
            json={
                "api_key": TAVILY_API_KEY,
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The code partially overlaps with the description in that it performs brand-related searching and outputs a 0-100 score. However, the primary behavior is materially narrower and different from the declared product. It exposes a small authenticated Flask API that submits a single Tavily search query like '<brand> 品牌 AI 服务' and assigns a score via min(len(results)*15, 100). It does not integrate with or search Kimi/Xunfei/Zhipu/Wenxin/DeepSeek/etc., does not analyze recommendation failures, does not push to Feishu, and does not implement competitor monitoring. The use of API-key authentication is a supporting implementation detail, not itself a mismatch, but the major declared capabilities are absent or substituted with a generic web-search backend.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared description presents a broader end-to-end monitoring tool with platform searching, visibility detection, scoring, analysis, and notification support. The supplied code implements only a narrower analysis module that consumes already-provided search results and an already-provided GEM score. While the 'analyze why brand was not recommended' portion aligns with the description, the primary behavior of this specific code chunk does not include the other major declared capabilities. There is also an undeclared optional external network call to an AI endpoint via curl, but it is directly in service of the analysis function rather than an unrelated capability.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description presents an end-user monitoring product focused on searching AI platforms, evaluating brand visibility, generating scores, and producing analysis/pushes. The supplied code does not perform any of those monitoring or analysis functions. Instead, it handles monetization and access control: validating API keys with a remote service, limiting free-tier brands/platforms, persisting monthly quota state locally, and exposing upgrade/downgrade helpers. While quota enforcement could be a supporting implementation detail for the broader product, this chunk also introduces undeclared external verification and licensing behavior that is materially different from the declared functionality and not represented in the description or permissions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding
The core purpose mostly aligns: the code does search multiple AI platforms for a brand keyword and generates a GEM-style visibility score/report. However, several declared capabilities are not implemented. There is no logic for AI-generated causal analysis of why the brand was not recommended, and no Feishu push support appears anywhere in the code. Additionally, while the interface accepts multiple keywords, the search logic uses only self.keywords[0] for querying, so multi-keyword monitoring is overstated. The code also saves reports to the local Desktop, which is an undeclared resource access, though this is secondary to the main mismatch. Therefore the description overstates the implemented functionality enough to count as a mismatch.

Lp1

High
Category
MCP Least Privilege
Confidence
94% confidence
Finding
This service makes HTTP requests to an external API, but that network capability is not covered by declared permissions. In a skill context, undisclosed egress is security-relevant because user input and operational metadata may be transmitted to third parties without explicit approval.

Lp1

High
Category
MCP Least Privilege
Confidence
94% confidence
Finding
This service makes HTTP requests to an external API, but that network capability is not covered by declared permissions. In a skill context, undisclosed egress is security-relevant because user input and operational metadata may be transmitted to third parties without explicit approval.

Lp1

High
Category
MCP Least Privilege
Confidence
92% confidence
Finding
This skill contains shell-capable behavior by launching `curl` through `subprocess`, but the declared permissions do not cover that capability. In an agent-skill context, undeclared execution capabilities reduce transparency and can bypass operator expectations or policy controls, making the skill more dangerous than equivalent standalone code.

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding
The skill uses 'file_read' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding
The skill uses 'file_write' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The module includes undocumented local functions to upgrade a user to pro or enterprise by simply editing the local quota file. In a brand monitoring skill, this capability is unrelated to core business logic and effectively provides a built-in license-escalation mechanism that can be abused to unlock paid functionality without valid authorization.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README explicitly advertises that Pro requests are routed through the vendor's server, but it does not clearly warn users that brand queries and related monitoring data leave the local environment and are transmitted to a third party. This creates a real privacy and confidentiality risk, especially because monitored brand terms, competitive research, and visibility results may be commercially sensitive.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README tells users to configure a Feishu webhook for report delivery but does not warn that generated report contents will be sent to an external messaging endpoint outside the local execution context. If reports contain sensitive brand-monitoring results, prompts, or competitive intelligence, this can lead to unintended disclosure to third-party systems or overly broad internal audiences.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger phrases are very broad and overlap with common user requests about GEO, AI visibility, or competitor monitoring. In agent ecosystems, overly broad triggers can cause accidental invocation, leading the skill to process sensitive brand queries or initiate external lookups in contexts where the user did not intend to use this tool.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill advertises external API usage and Feishu webhook support, but it does not clearly warn that user-supplied brand names, queries, and potentially generated results may be transmitted to third-party services. This creates a privacy and data-governance risk, especially for confidential competitor analysis, unreleased brands, or internal monitoring terms.

External Transmission

Medium
Category
Data Exfiltration
Content
- ✅ Routed through our server — no local environment needed
- ✅ Stable and efficient, no IP blocking

> After purchasing Pro/Enterprise, get your API key at [https://yk-global.com](https://yk-global.com) and use `--api-key YOUR_KEY` to unlock all features. Verification: `POST https://api.yk-global.com/v1/verify`. On failure, auto-downgrades to Free — no disruption.

## Supported AI Platforms
Confidence
92% confidence
Finding
The skill explicitly routes functionality through an external server and performs API-key verification against api.yk-global.com. Any time user queries, telemetry, or keys are sent to a third-party endpoint, there is a real data exposure and trust-boundary risk; in this skill context, brand-monitoring queries may be commercially sensitive and the server-mediated design increases that sensitivity.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The module description and error message text are written in Chinese, and the query string is also hard-coded with Chinese terms, with no indication that users may choose another language or that the service is intentionally region-specific. This can violate language/locale policy where skills should not force a specific language without opt-in or clear justification.

External Transmission

Medium
Category
Data Exfiltration
Content
TAVILY_API_KEY = os.environ.get('TAVILY_API_KEY', '')
if not TAVILY_API_KEY:
    raise ValueError("请设置 TAVILY_API_KEY 环境变量")
TAVILY_API_URL = "https://api.tavily.com/search"

# API认证(简易版:检查X-API-Key头)
API_KEYS = {
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def search_brand_tavily(brand_name, max_results=5):
    """使用Tavily搜索品牌信息"""
    try:
        response = requests.post(
            TAVILY_API_URL,
            json={
                "api_key": TAVILY_API_KEY,
Confidence
84% confidence
Finding
The code sends user-provided brand queries to an external third-party endpoint. While external transmission is functionally required for this service, it is still a real security/privacy concern because business-sensitive queries leave the local trust boundary and are processed by another provider.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
User-supplied brand data is transmitted to a third-party service together with a service credential, yet the code provides no disclosure, consent, or data-handling notice. In this skill's brand-monitoring context, inputs may contain sensitive business terms or client identifiers, making silent external transmission more risky.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The docstring and printed messages are entirely in Chinese (for example at L02, L10, L16, and L21), which imposes a specific language on users. The policy for this category says to flag language or locale constraints unless the skill offers user opt-in or clearly documents a justified region-specific scope, neither of which appears here.

External Transmission

Medium
Category
Data Exfiltration
Content
def test_search(brand="91tokenhub"):
    """测试搜索"""
    resp = requests.post(
        API_URL,
        headers={"X-API-Key": TEST_KEY},
        json={"brand": brand, "max_results": 5}
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The config sets the report language to "zh-CN", which is a natural-language locale constraint. Because this file does not indicate that the user can choose another language or opt in to Chinese output, it may violate the policy against forcing a specific language without user consent.

External Transmission

Medium
Category
Data Exfiltration
Content
# ⚠️ 如需启用AI分析功能,请在此填入您的AI接口地址
        # 例如:https://your-api-server.com/v1/chat/completions
        # 当前默认使用本地分析框架(见 _get_fallback_analysis)
        AI_ENDPOINT = ""  # <-- 填入AI接口地址,如 https://api.minimax.chat/v1/chat/completions

        if not AI_ENDPOINT:
            return self._get_fallback_analysis()
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
94% confidence
Finding
If `AI_ENDPOINT` is configured, the code sends the brand name, GEM score, and summarized search-result snippets to an external AI service without any runtime disclosure or consent gate. In this skill's monitoring context, those snippets may contain sensitive business intelligence, customer-related text, or proprietary competitive-analysis data, so silent transmission creates a real privacy and data-governance risk.

Static analysis

No suspicious patterns detected.