Back to skill

Security audit

star-search

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent search service, but it bundles under-secured public API, account, payment, URL-fetching, and credential-handling behavior that needs review before use.

Use only in an isolated development environment unless you are prepared to harden it. Before public deployment, remove the Hermes auth-file fallback, require a dedicated LLM key, enforce HTTPS allowlisted LLM/search egress, block private/internal URL fetches, require authentication and quotas on costly endpoints, remove or gate mock payments, verify real payment callbacks, replace the hard-coded token secret and password hashing, and avoid granting Cloudflare WAF/Bot permissions to an agent automatically.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (10)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/api_server.py:824
Finding

Unauthenticated Payment Activation and Unsigned Payment Callback

Content
View full analysis
Dict: data = _load_orders() order = data['orders'].get(order_id) if not order: return {'error': 'order not found'} if order['status'] != 'pending': return {'error': f'order status: {order["status"]}'} now = int(time.time()) order['status'] = 'active' order['paid_at'] = now order['activated_at'] = now order['expire_at'] = now + order['tier_info']['period_days'] * 86400 # Updates the user's membership tier. for u in ud.get('users', {}).values(): if u['user_id'] == order['user_id']: u['tier'] = order['tier'] u['tier_expire_at'] = order['expire_at'] ``` ```python def alipay_callback(params: Dict) -> Dict: order_id = params.get('out_trade_no', '') trade_status = params.get('trade_status', '') trade_no = params.get('trade_no', '') if trade_status in ['TRADE_SUCCESS', 'TRADE_FINISHED']: result = mark_paid(order_id, trade_no) return {'code': 'success', 'message': 'paid', 'result': result} ``` ### Technical Analysis The mock-payment endpoint is ...[truncated 1896 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/answer.py:758
Finding

Server-Side Request Forgery Through URLs Embedded in Search Queries

Content
View full analysis
1: import asyncio as _aio multi_results = _aio.run(_async_fetch_multi_urls(all_urls[:5])) multi_text = [] for i, mr in enumerate(multi_results, 1): if mr.get("ok") and mr.get("text"): multi_text.append( f"[URL {i}] {mr.get('title', mr.get('url',''))}\n" f"{mr['text'][:1500]}" ) if multi_text: formatted = "\n\n".join(multi_text) + \ f"\n\n===其他搜索结果===\n{formatted}" # Single-URL special handling. special = _detect_special_intent(query) if special: if special["type"] == "url": url_data = special["data"] url_text = f"[URL全文] {url_data['title']}\n{url_data['text']}" formatted = f"{url_text}\n\n===其他搜索结果===\n{formatted}" ``` The underlying fetch performs a server-side request: ```python req = _ur.Request(url, headers={ 'User-Agent': 'Mozilla/5.0 ...', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', }) with _ur.urlopen(req, timeout=timeout) as resp: raw = resp.read() ``` The single-URL filter only performs incomplete string checks: ```python if not any(s in url for s in ['localhost', '', '']): data = fetch_url(url) ``` ### Technical Analysis User-controlled URLs embedded in `query` are fetched by the server. There is no robust destination validation, DNS resolution check, IP-address classification, port restriction, redirect validation, or network egress policy. The multi-URL path has no localhost/private-address check at all. The single-URL path only rejects the literal string `localhost` and placeholder values. It does not reject loopback address ...[truncated 1561 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/user_auth.py:21
Finding

Hard-Coded Token Signing Secret Allows Authentication Token Forgery

Content
View full analysis
-2026' # v20.55: HMAC key TOKEN_TTL = 12 * 3600 def _make_token(user_id: str) -> str: payload = { 'uid': user_id, 'exp': int(time.time()) + TOKEN_TTL, 'jti': secrets.token_hex(8), } payload_b64 = base64.urlsafe_b64encode( json.dumps(payload).encode() ).decode() sig = hmac.new( SECRET, payload_b64.encode(), hashlib.sha256 ).hexdigest()[:32] return f"{payload_b64}.{sig}" def _verify_token(token: str) -> Optional[Dict]: payload_b64, sig = token.split('.', 1) expected_sig = hmac.new( SECRET, payload_b64.encode(), hashlib.sha256 ).hexdigest()[:32] if not hmac.compare_digest(sig, expected_sig): return None payload = json.loads(base64.urlsafe_b64decode(payload_b64).decode()) if payload.get('exp', 0) < time.time(): return None return payload ``` ### Technical Analysis The HMAC signing secret is embedded in source code distributed to every installation. Anyone with access to the package knows the key used by all deployments that have not modified the source. The token payload is only Base64-encoded and contains the user ID and expiration time. Because the signing algorithm is fully documented in the code and the key is known, an attacker can generate a valid token for any known user ID and choose an arbitrary future expiration. The signature is also truncated to 128 bits. While 128 bits is not practically brute-forceable, truncation provides no benefit and is unnecessary. The primary flaw is disclosure and reuse of the signing key. ### Attack Path 1. The attacker obtains the public project files and reads `SECRET`. 2. The attacker learns a target `user_id` ...[truncated 738 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/user_auth.py:25
Finding

Weak Custom Password Hashing Instead of a Password KDF

Content
View full analysis
str: if salt is None: salt = secrets.token_hex(16) h = hashlib.sha256((salt + password).encode()).hexdigest() for _ in range(1000): h = hashlib.sha256((h + salt).encode()).hexdigest() return f"{salt}${h}" def _verify_password(password: str, stored: str) -> bool: try: salt, _ = stored.split('$', 1) return _hash_password(password, salt) == stored except Exception: return False ``` ### Technical Analysis The implementation uses 1,001 fast SHA-256 operations. This is not “bcrypt-like”: SHA-256 is optimized for high throughput and does not provide the memory hardness required to resist GPU and ASIC cracking. The account system permits passwords as short as six characters. If `users.json` is copied through a backup, server compromise, file-permission error, or other disclosure, an attacker can perform efficient offline dictionary attacks against every account. The comparison should also use a constant-time primitive, although the dominant risk is the weak password derivation algorithm. ### Attack Path 1. An attacker obtains `users.json` or a backup containing salted password hashes. 2. The attacker runs common passwords and six-character candidates through the documented 1,001-round SHA-256 loop. 3. Recovered passwords can be used against this application. 4. Reused passwords may also enable credential-stuffing attacks against unrelated services. ### Impact Assessment This flaw increases the probability of account compromise after any password database disclosure. It does not independently expose the database, but materially weakens protection of stored user credentials. ]]>
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/api_server.py:652
Finding

CAPTCHA Is Collected by the Frontend but Never Enforced During Registration

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/api_server.py:190
Finding

Authentication Is Optional on Resource-Intensive Search and LLM Endpoints

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
index.html:1311
Finding

Bearer Tokens Are Placed in URLs and Persisted in Browser Local Storage

Content
View full analysis
r.json()) .then(u => { // Update account display. }); } } function clearAuth() { __authToken = ''; localStorage.removeItem('star_token'); } function getAuthHeader() { return __authToken ? { 'Authorization': 'Bearer ' + __authToken } : {}; } ``` ```python @app.get("/v1/auth/me") async def auth_me(token: str = ''): """query: ?token=xxx""" if not token: return {'error': 'token is required'} u = _ua.get_user(token) if not u: return {'error': 'invalid or expired token'} u.pop('password', None) return u ``` ### Technical Analysis Putting bearer credentials in query strings exposes them to web-server access logs, reverse-proxy logs, browser history, monitoring tools, error reports, and potentially referrer propagation. Query strings are commonly retained much more broadly than Authorization headers. Storing a bearer token in `localStorage` makes it readable by any JavaScript executing in the origin. A cross-site scripting issue, compromised third-party script, browser extension, or same-origin content compromise can therefore steal the token. ### Attack Path 1. A user logs in, and the bearer token is stored in `localStorage`. 2. `updateAuthChip()` calls `/v1/auth/me?token=...`. 3. The full URL may be recorded by reverse proxies, application servers, observability products, or browser history. 4. A party with access to one of those records can replay the token during its 12-hour lifet ...[truncated 399 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/answer.py:22
Finding

LLM Module Reads Credentials from Another Application's Authentication Store

Content
View full analysis
:8080/v1" ) LLM_API_KEY = os.environ.get( "LLM_API_KEY", _NEWAPI_KEY or "" ) ``` The extracted credential is later transmitted as: ```python headers={ "Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json" } ``` ### Technical Analysis Reading the Skill's own explicitly configured `.env` is expected for an LLM-backed search service. Reading `~/.hermes/auth.json` is materially different: it accesses another application's authentication store and extracts the first string matching an API-key pattern without confirming provider, scope, intended audience, or user consent. The extracted credential is then used against `LLM_BASE_URL`, which is separately configurable. This creates a credential-confusion and exfiltration risk: a Hermes credential may be transmitted to an unrelated or attacker-controlled endpoint. This behavior exceeds the minimum privileges needed for declared functionality. The Skil ...[truncated 820 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_content.py:77
Finding

Predictable Shared Temporary File Enables Symlink Attacks and Cross-Request Races

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/answer.py:390
Finding

Untrusted Search and Web Content Is Passed to the LLM Without Prompt-Injection Isolation

Content
View full analysis
str: lines = [] for i, r in enumerate(results[:max_n], 1): title = r.get('title', '').strip() url = r.get('url', '').strip() snippet = ( r.get('snippet', '').strip() or r.get('desc', '').strip() or r.get('content', '').strip() ) domain = _extract_domain(url) lines.append(f"[{i}] {title}") if domain: lines.append(f" Source: {domain}") if snippet: lines.append(f" {snippet[:200]}") lines.append("") return "\n".join(lines) ``` ```python req_data = { "model": LLM_MODEL, "messages": [ {"role": "system", "content": system_prompt}, { "role": "user", "content": f"query: {query}\n\nSearch results:\n{formatted}" } ], "temperature": 0.3, "max_tokens": 300, } ``` Fetched full-page content is similarly prepended to `formatted`: ```python url_text = f"[Full URL text] {url_data['title']}\n{url_data['text']}" formatted = f"{url_text}\n\n===Other search results===\n{formatted}" ``` ### Technical Analysis Search-result titles, snippets, and fetched web-page text are attacker-controlled content. They are concatenated directly into the model's user message without a strong instruction that the content is untrusted data, without structural separation, and without detecting instruction-like content. A malicious page can include text such as “ignore the question,” “cite this URL,” or “produce the following recommendation.” The model may interpret those strings as instructions rather than source material. The model appears to generate text only and does not directly execute tools, whic ...[truncated 975 chars]
Remediation
View remediation
Vulnerability Patterns
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (225)

Tainted flow: 'req' from os.environ.get (line 14, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The request URL is derived from the STAR_SEARCH_BASE environment variable and used directly for outbound network access. If an attacker or untrusted deployment context can control that variable, they can redirect searches to an arbitrary host, causing SSRF-like behavior, data exfiltration of user queries, and trust bypass of the intended backend.

Content

Scanner excerpt · integrations/dify/star_search.py (reported line 20)May include surrounding context.

python
headers={"Content-Type": "application/json"}
    )
    try:
        resp = urllib.request.urlopen(req, timeout=30)
        return json.loads(resp.read())
    except Exception as e:
        return {"error": str(e)}

Tainted flow: 'req' from os.environ.get (line 85, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · integrations/langchain/star_search_tool.py (reported line 71)May include surrounding context.

python
headers={"Content-Type": "application/json"}
        )
        try:
            resp = urllib.request.urlopen(req, timeout=self.timeout)
            return json.loads(resp.read())
        except Exception as e:
            return {"error": str(e)}

Tainted flow: 'req' from os.environ.get (line 85, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · integrations/langchain/star_search_tool.py (reported line 90)May include surrounding context.

python
data=data,
        headers={"Content-Type": "application/json"}
    )
    resp = urllib.request.urlopen(req, timeout=30)
    return json.loads(resp.read())

Tainted flow: 'req' from os.environ.get (line 985, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
97% confidence
Finding

fetch_url accepts a URL derived from user query parsing and performs a server-side request with urlopen before any strong allowlist or egress restriction. That creates an SSRF primitive: an attacker can cause the service to fetch arbitrary remote or internal resources, and the fetched content is later injected into the LLM context, increasing data-exfiltration risk. In a web-search skill, this is more dangerous because URL-following is framed as normal functionality and may be triggered by ordinary user prompts.

Content

Scanner excerpt · scripts/answer.py (reported line 425)May include surrounding context.

python
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
        })
        with _ur.urlopen(req, timeout=timeout) as resp:
            ct = resp.headers.get('Content-Type', '')
            # 非 HTML 直接返回 (PDF/图片等不抓)
            if 'text/html' not in ct and 'application/xhtml' not in ct:

Tainted flow: 'req' from os.environ.get (line 985, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/answer.py (reported line 489)May include surrounding context.

python
# 1. repo info
        api_url = f"https://api.github.com/repos/{owner}/{repo}"
        req = _ur.Request(api_url, headers={'Accept': 'application/vnd.github+json', 'User-Agent': 'star-search/18.0'})
        with _ur.urlopen(req, timeout=8) as resp:
            data = _json.loads(resp.read())
        out["description"] = data.get("description", "")
        out["language"] = data.get("language", "")

Tainted flow: 'req' from os.environ.get (line 985, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
97% confidence
Finding

fetch_pdf downloads arbitrary PDFs from URLs extracted from the user query without meaningful origin restrictions. This is another SSRF path and also introduces denial-of-service risk from large or malformed PDFs; downloaded content may then be processed and summarized by the LLM, potentially leaking fetched internal data. The skill context increases risk because users are encouraged to provide links for analysis.

Content

Scanner excerpt · scripts/answer.py (reported line 532)May include surrounding context.

python
import zlib as _zlib
    try:
        req = _ur.Request(url, headers={'User-Agent': 'star-search/18.0'})
        with _ur.urlopen(req, timeout=timeout) as resp:
            data = resp.read()
        if not data.startswith(b'%PDF'):
            return {"ok": False, "error": "not a PDF file", "url": url}

Tainted flow: 'req' from os.environ.get (line 985, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/answer.py (reported line 586)May include surrounding context.

python
})
            url = f"http://export.arxiv.org/api/query?{params}"
        req = _ur.Request(url, headers={'User-Agent': 'star-search/18.0'})
        with _ur.urlopen(req, timeout=10) as resp:
            xml_data = resp.read().decode('utf-8', errors='ignore')
        # 解析 atom xml
        ns = {'atom': 'http://www.w3.org/2005/Atom'}

Tainted flow: 'req' from os.environ.get (line 985, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The module sends search results, prior conversation history, and potentially fetched page/PDF content to an external LLM endpoint defined by LLM_BASE_URL, with credentials loaded from local files or environment. If the endpoint is untrusted, misconfigured, or intercepted, sensitive user data can be disclosed externally; the default uses plain HTTP, which worsens the risk. In this skill, the danger is elevated because the code silently aggregates multiple data sources and forwards them off-box.

Content

Scanner excerpt · scripts/answer.py (reported line 884)May include surrounding context.

python
"Content-Type": "application/json"
            }
        )
        with urllib.request.urlopen(req, timeout=LLM_TIMEOUT) as resp:
            return json.loads(resp.read())

    try:

Tainted flow: 'req' from os.environ.get (line 985, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
93% confidence
Finding

The follow-up generation path also transmits user query and answer content to the external LLM service, again without disclosure and using the same configurable endpoint. Although the data volume is smaller than the main summarization path, it still creates unnecessary secondary exfiltration of user content.

Content

Scanner excerpt · scripts/answer.py (reported line 990)May include surrounding context.

python
data=json.dumps(req_data).encode(),
            headers={"Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json"}
        )
        with urllib.request.urlopen(req, timeout=10) as resp:
            return json.loads(resp.read())

    try:

Tainted flow: 'req' from os.environ.get (line 52, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

This function sends user queries and search-derived content to an externally configured LLM endpoint using credentials loaded from environment or a local .env file. Because the destination base URL is configurable and there is no allowlist, sensitive research queries or retrieved content could be exfiltrated to an unintended third-party service or attacker-controlled endpoint if configuration is tampered with.

Content

Scanner excerpt · scripts/deep_research.py (reported line 61)May include surrounding context.

python
},
    )
    try:
        with urllib.request.urlopen(req, timeout=LLM_TIMEOUT) as resp:
            data = json.loads(resp.read())
            return data['choices'][0]['message']['content']
    except Exception as e:

Tainted flow: 'req' from os.environ.get (line 645, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
94% confidence
Finding

The code constructs an outbound request to an LLM endpoint whose base URL is sourced from environment variables and an on-disk .env file, then sends user-derived entity content to that remote service. If an attacker can influence configuration, this can redirect requests and bearer credentials to an attacker-controlled endpoint, causing secret exfiltration and unintended data disclosure.

Content

Scanner excerpt · scripts/entity_card.py (reported line 654)May include surrounding context.

python
},
    )
    try:
        with urllib.request.urlopen(req, timeout=LLM_TIMEOUT) as resp:
            data = json.loads(resp.read())
            return data['choices'][0]['message']['content']
    except Exception:

Tainted flow: 'req' from os.environ.get (line 139, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/query_rewrite.py (reported line 147)May include surrounding context.

python
'Authorization': f'Bearer {LLM_API_KEY}',
                },
            )
            with urllib.request.urlopen(req, timeout=LLM_TIMEOUT) as resp:
                data = json.loads(resp.read())
                content = data['choices'][0]['message']['content']
                for line in content.split('\n'):

Tainted flow: 'req' from os.environ.get (line 91, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
92% confidence
Finding

The code allows network egress to an LLM endpoint whose base URL is sourced from environment variables and can also be overridden by a fixed local .env file. That means user queries and the bearer token may be sent to an attacker-controlled host if deployment configuration is tampered with, creating SSRF/data-exfiltration risk rather than a harmless configuration pattern.

Content

Scanner excerpt · scripts/super_brain.py (reported line 100)May include surrounding context.

python
},
    )
    try:
        with urllib.request.urlopen(req, timeout=LLM_TIMEOUT) as resp:
            data = json.loads(resp.read())
            return data['choices'][0]['message']['content']
    except Exception:

Tainted flow: 'req' from os.environ.get (line 56, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/verify.py (reported line 61)May include surrounding context.

python
data=body,
            headers={'Content-Type': 'application/x-www-form-urlencoded'},
        )
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read())
        return {
            'success': data.get('success', False),

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

Cloudflare Turnstile verification and exposing a site key are not inherently malicious, but embedding CAPTCHA verification behavior inside a purported search skill is a meaningful trust-boundary expansion. It introduces third-party verification flows and token transmission that users may not expect, with privacy and misuse implications if not disclosed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

Cloudflare Turnstile verification and exposing a site key are not inherently malicious, but embedding CAPTCHA verification behavior inside a purported search skill is a meaningful trust-boundary expansion. It introduces third-party verification flows and token transmission that users may not expect, with privacy and misuse implications if not disclosed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Cloudflare Turnstile verification and exposing a site key are not inherently malicious, but embedding CAPTCHA verification behavior inside a purported search skill is a meaningful trust-boundary expansion. It introduces third-party verification flows and token transmission that users may not expect, with privacy and misuse implications if not disclosed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

Cloudflare Turnstile verification and exposing a site key are not inherently malicious, but embedding CAPTCHA verification behavior inside a purported search skill is a meaningful trust-boundary expansion. It introduces third-party verification flows and token transmission that users may not expect, with privacy and misuse implications if not disclosed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Cloudflare Turnstile verification and exposing a site key are not inherently malicious, but embedding CAPTCHA verification behavior inside a purported search skill is a meaningful trust-boundary expansion. It introduces third-party verification flows and token transmission that users may not expect, with privacy and misuse implications if not disclosed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

Cloudflare Turnstile verification and exposing a site key are not inherently malicious, but embedding CAPTCHA verification behavior inside a purported search skill is a meaningful trust-boundary expansion. It introduces third-party verification flows and token transmission that users may not expect, with privacy and misuse implications if not disclosed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Cloudflare Turnstile verification and exposing a site key are not inherently malicious, but embedding CAPTCHA verification behavior inside a purported search skill is a meaningful trust-boundary expansion. It introduces third-party verification flows and token transmission that users may not expect, with privacy and misuse implications if not disclosed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Cloudflare Turnstile verification and exposing a site key are not inherently malicious, but embedding CAPTCHA verification behavior inside a purported search skill is a meaningful trust-boundary expansion. It introduces third-party verification flows and token transmission that users may not expect, with privacy and misuse implications if not disclosed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Cloudflare Turnstile verification and exposing a site key are not inherently malicious, but embedding CAPTCHA verification behavior inside a purported search skill is a meaningful trust-boundary expansion. It introduces third-party verification flows and token transmission that users may not expect, with privacy and misuse implications if not disclosed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Cloudflare Turnstile verification and exposing a site key are not inherently malicious, but embedding CAPTCHA verification behavior inside a purported search skill is a meaningful trust-boundary expansion. It introduces third-party verification flows and token transmission that users may not expect, with privacy and misuse implications if not disclosed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

Cloudflare Turnstile verification and exposing a site key are not inherently malicious, but embedding CAPTCHA verification behavior inside a purported search skill is a meaningful trust-boundary expansion. It introduces third-party verification flows and token transmission that users may not expect, with privacy and misuse implications if not disclosed.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
index.html:1311

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/verify.py:6