Back to skill

Security audit

Zero to One Startup

Security checks for vulnerabilities and agentic risk

Overview

This skill is related to startup assistance, but its package has serious review-worthy server, widget, credential, and packaging risks before installation.

Treat this as requiring review before installation. Do not expose the server to a network or embed the widget on a real site until the path traversal, default binding, feedback authentication, XSS, plaintext/query API key handling, request-size limits, and missing packaged scripts are fixed. External LLM and Supabase credentials are purpose-aligned, but should only be configured for trusted endpoints and accounts intended for this skill.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/server.py:690
Finding
Unauthenticated Arbitrary Local File Disclosure Through the Widget Route<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.py:690-701` and duplicated at `clawhub-raon-os/scripts/server.py:690-701` **Vulnerability Type**: Path traversal and absolute-path injection **Risk Level**: Critical ### Vulnerable Code ```python elif path.startswith("/widget/"): widget_dir = Path(__file__).resolve().parent.parent / "widget" file_path = widget_dir / path[8:] if file_path.exists() and file_path.is_file(): body = file_path.read_bytes() ct = "application/javascript" if str(file_path).endswith(".js") else "text/plain" self.send_response(200) self.send_header("Content-Type", ct) self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) else: self._send_error(404, "not_found") ``` ### Technical Analysis The route takes the portion of the request path after `/widget/` and appends it directly to the intended widget directory. It does not reject absolute paths, `..` path components, symbolic-link escapes, or resolved paths outside the widget directory. With `pathlib`, joining a base path with an absolute second path discards the base path. For example, if `path[8:]` is `/etc/passwd`, `file_path` becomes `/etc/passwd`, not a file below the widget directory. The route is also outside the `/v1/` authentication middleware. Consequently, any client that can connect to the HTTP server can attempt to read files using the server process's operating-system privileges. ### Attack Path 1. A user starts the HTTP server using its default configuration. 2. The server listens on all network interfaces. 3. An attacker sends a path-preserving request such as: ```text GET /widget//etc/passwd HTTP/1.1 Host: target.example ``` Alternatively, the attacker can use traversal components, for example `/widget/../SKILL.md`, where intermediary infrastructure does not normalize the path. 4. `path[8:]` is interpreted as an absol ...[truncated 829 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Resolve and validate the requested path before reading it: ```python widget_dir = (Path(__file__).resolve().parent.parent / "widget").resolve() relative_name = path.removeprefix("/widget/") if not relative_name or Path(relative_name).is_absolute(): self._send_error(400, "invalid_path") return candidate = (widget_dir / relative_name).resolve() try: candidate.relative_to(widget_dir) except ValueError: self._send_error(403, "forbidden") return if not candidate.is_file(): self._send_error(404, "not_found") return ``` Additional hardening should include: - Serve only an explicit allowlist such as `raon-chat.js` and `raon-chat.min.js`. - Reject paths containing `..`, backslashes, null bytes, or encoded separators. - Avoid following symbolic links, or verify the resolved target after link resolution. - Apply authentication if arbitrary widget assets are not intended to be public. - Add regression tests for absolute paths, traversal paths, encoded traversal, and symbolic-link escapes. - Apply the same fix to the duplicated `clawhub-raon-os/scripts/server.py`. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
widget/raon-chat.js:116
Finding
DOM-Based Cross-Site Scripting in the Embeddable Chat Widget<![CDATA[ ## Vulnerability Details **File Location**: `widget/raon-chat.js:116-119`, `widget/raon-chat.js:123-126`, and `widget/raon-chat.js:151-152`; duplicated in `clawhub-raon-os/widget/raon-chat.js` and both minified widget files **Vulnerability Type**: DOM-based cross-site scripting through unsafe `innerHTML` use **Risk Level**: High ### Vulnerable Code ```javascript function addMsg(html, cls) { const d = document.createElement('div'); d.className = 'raon-msg ' + cls; d.innerHTML = html; msgs.appendChild(d); msgs.scrollTop = msgs.scrollHeight; return d; } async function send() { const t = inp.value.trim(); if (!t && !pendingPdfBase64) return; const displayText = pendingPdfBase64 ? '📄 ' + fileNameEl.textContent + (t ? ' — ' + t : '') : t; addMsg(displayText, 'user'); inp.value = ''; sendBtn.disabled = true; const ld = addMsg('분석 중...', 'bot loading'); // ... if (d.status === 'ok') { const res = (d.result||d.response||'').replace(/\n/g,'<br>').replace(/\*\*(.*?)\*\*/g,'<strong>$1</strong>'); addMsg((d.score ? '<div style="font-size:20px;margin-bottom:8px">📊 '+d.score+'점</div>' : '') + res, 'bot'); } else addMsg('⚠️ ' + (d.error||'오류'), 'bot'); } ``` ### Technical Analysis The `addMsg` function treats its argument as trusted HTML and assigns it directly to `innerHTML`. However, it receives two untrusted data sources: 1. Raw text entered by the browser user through `displayText`. 2. LLM or remote API output through `d.result` or `d.response`. The newline and bold-text replacements are formatting transformations, not HTML sanitization. They do not remove event handlers, dangerous elements, `javascript:` URLs, SVG payloads, or other active markup. Because the widget is designed to be embedded in third-party websites, injected JavaScript executes in the embedding page's origin rather than in an isolated Raon OS origin. ### Attack Path A direct user-input exploitation path is: 1. A victim opens a website containin ...[truncated 1297 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Render untrusted data as text rather than HTML: ```javascript function addTextMsg(text, cls) { const d = document.createElement('div'); d.className = 'raon-msg ' + cls; d.textContent = String(text); msgs.appendChild(d); msgs.scrollTop = msgs.scrollHeight; return d; } ``` If Markdown formatting is required: - Use a maintained Markdown parser configured to reject raw HTML. - Sanitize the generated markup with a strict allowlist-based sanitizer such as DOMPurify. - Permit only necessary elements such as `strong`, `br`, and plain text. - Prohibit event-handler attributes, SVG, MathML, scripts, iframes, styles, and dangerous URL schemes. - Construct score and layout elements with DOM APIs rather than string concatenation. - Deploy a restrictive Content Security Policy that disallows inline scripts. - Add automated tests using event-handler, SVG, malformed-tag, and `javascript:` payloads. - Rebuild both minified widget files from the corrected source. - Apply the correction to both the root and duplicated `clawhub-raon-os` copies. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/server.py:1069
Finding
Local HTTP Service Exposed on All Network Interfaces by Default<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.py:1069-1076` and duplicated at `clawhub-raon-os/scripts/server.py:1069-1076` **Vulnerability Type**: Insecure default network binding **Risk Level**: High ### Vulnerable Code ```python def main(): parser = argparse.ArgumentParser(description="🌅 Raon OS API Server") parser.add_argument("--port", "-p", type=int, default=DEFAULT_PORT) parser.add_argument("--model", "-m", default=DEFAULT_MODEL) parser.add_argument("--host", default="0.0.0.0") # nosec B104 args = parser.parse_args() RaonHandler.model = args.model server = HTTPServer((args.host, args.port), RaonHandler) print(f"🌅 Raon OS API Server running on http://{args.host}:{args.port}") ``` ### Technical Analysis The project describes this component as a local HTTP server, but the default bind address is `0.0.0.0`. This exposes the service on every available IPv4 interface, including LAN, VPN, container, and potentially public interfaces. The broad bind is not required for local business-plan analysis. It materially increases the attack surface of all public and insufficiently protected routes, including the vulnerable widget file route, health endpoint, Kakao endpoint, and feedback endpoint. The `# nosec B104` comment suppresses a static-analysis warning but does not mitigate the exposure. ### Attack Path 1. A user launches the server with the documented command and does not specify `--host`. 2. The process binds to `0.0.0.0:8400`. 3. A remote host on a reachable network scans or directly connects to TCP port 8400. 4. The attacker accesses unauthenticated routes or attempts API-key guessing and application-layer attacks. 5. The attacker can combine this exposure with the widget path vulnerability to retrieve local files remotely. ### Impact Assessment The insecure default changes vulnerabilities that might otherwise require local access into remotely reachable attack surfaces. Potential effects i ...[truncated 515 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Change the default bind address to loopback: ```python parser.add_argument("--host", default="127.0.0.1") ``` Additional safeguards should include: - Require an explicit `--public` or `--allow-remote` option before accepting non-loopback bind addresses. - Print a prominent warning when binding to a non-loopback interface. - Require authentication on every non-static endpoint before supporting remote deployment. - Recommend a TLS-terminating reverse proxy, firewall allowlist, and request-rate controls for production use. - Do not rely on source IP alone for administrative authorization. - Document the difference between local and production deployment. - Apply the same secure default to the duplicated server. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/server.py:839
Finding
Authentication Bypass on the Feedback Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.py:839-853` and duplicated at `clawhub-raon-os/scripts/server.py:839-853` **Vulnerability Type**: Missing authentication caused by fail-open conditional logic **Risk Level**: Medium ### Vulnerable Code ```python # ── /v1/feedback — 인증 없이 로컬호스트 허용 ───────────────────────── if mode == "feedback": # 외부 요청은 API 키 검증 (로컬은 자동 통과) if not _is_localhost(self): api_key = self._get_api_key() if api_key: with _data_lock: keys = _load_api_keys() if not keys.get(api_key, {}).get("active", False): self._send_error(401, "invalid api key") return self._handle_feedback() return ``` The accepted data can subsequently be routed externally: ```python if RAON_API_URL and RAON_API_KEY: payload = json.dumps({ "evaluation_id": evaluation_id, "rating": rating, "comment": comment or "", **eval_ctx, }, ensure_ascii=False).encode("utf-8") req = _ur.Request( "{}/v1/feedback".format(RAON_API_URL), data=payload, headers={ "Content-Type": "application/json", "X-API-Key": RAON_API_KEY, }, method="POST", ) _ur.urlopen(req, timeout=5) ``` ### Technical Analysis For non-local requests, the code validates an API key only when a key is supplied. If the attacker omits the key entirely, the `if api_key:` block is skipped and `_handle_feedback()` is called. This is a fail-open authentication condition. It contradicts the comment that external requests are subject to API-key verification. Accepted feedback may be written to a local history file, inserted into a configured Supabase project, or relayed to a configured managed service using the server owner's `RAON_API_KEY`. ### Attack Path 1. The HTTP server is reachable remotely, which occurs by default because it binds to `0.0.0.0`. 2. The attacker sends a req ...[truncated 1035 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Use the existing authentication middleware and fail closed: ```python if mode == "feedback": if not _is_localhost(self) and not self._authenticate(None): return self._handle_feedback() return ``` For stronger protection: - Require authentication for local requests as well unless an explicit development mode is enabled. - Apply per-key and per-source rate limits to feedback. - Set strict maximum lengths for `evaluation_id` and `comment`. - Validate that the referenced evaluation exists and belongs to the authenticated principal. - Add replay and duplicate-submission controls. - Do not use client source IP as the sole trust signal, particularly behind reverse proxies. - Add tests proving that missing, invalid, disabled, and expired keys are rejected. - Correct both copies of `server.py`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/server.py:124
Finding
Reusable API Keys Stored in Plaintext and Accepted in URL Query Parameters<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.py:124-172`, `scripts/server.py:239-241`, `scripts/server.py:543-551`, and `scripts/server.py:824-835`; duplicated in `clawhub-raon-os/scripts/server.py` **Vulnerability Type**: Plaintext credential storage and insecure credential transport **Risk Level**: Medium ### Vulnerable Code ```python DATA_DIR = Path(SCRIPT_DIR) / "data" DATA_DIR.mkdir(parents=True, exist_ok=True) API_KEYS_FILE = DATA_DIR / "api_keys.json" def _save_json(path, data): with open(str(path), "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) def _save_api_keys(keys_dict): _save_json(API_KEYS_FILE, keys_dict) ``` ```python def _generate_api_key(): return "rk_" + secrets.token_hex(24) ``` ```python def _get_api_key(self): """Extract API key from header or query param.""" key = self.headers.get("X-API-Key", "") if key: return key parsed = urlparse(self.path) qs = parse_qs(parsed.query) keys = qs.get("api_key", []) if keys: return keys[0] return None ``` ```python new_key = _generate_api_key() key_obj = { "key": new_key, "user_id": body.get("user_id", ""), "plan": body.get("plan", "free"), "created_at": datetime.now(timezone.utc).isoformat(), "active": True, } with _data_lock: keys = _load_api_keys() keys[new_key] = key_obj _save_api_keys(keys) ``` ### Technical Analysis Generated API keys are bearer credentials: possession is sufficient to authenticate. The server stores each key verbatim both as the dictionary key and in the serialized object. The file is created using the process's ordinary umask. The implementation does not enforce owner-only permissions, use atomic secure creation, encrypt the data, or store a non-reversible verifier. On hosts with a permissive umask, other local users or processes may be able to read all active credentials. The server additionally accepts API keys in t ...[truncated 1476 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Store only a cryptographic verifier: - Generate a high-entropy bearer token and display it once. - Store a keyed HMAC or password-hash-style verifier rather than the plaintext token. - Associate a short non-secret identifier with each key for lookup. - Compare verifiers using constant-time comparison. - Support expiration, rotation, revocation, and last-used metadata. Secure the storage file: - Create it atomically with mode `0600`. - Ensure the data directory is mode `0700`. - Refuse to start or warn prominently if permissions are broader. - Avoid storing the plaintext key inside the value object. - Protect backups and exclude the file from source-control and artifact packaging. Secure transport: - Remove query-string API-key support. - Accept credentials only through an authorization header over HTTPS. - Redact authorization data from all logs and errors. - Add migration logic that invalidates or securely transforms existing plaintext key files. - Apply all changes to both project copies. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/server.py:375
Finding
Unbounded HTTP Bodies and PDF Processing Permit Resource-Exhaustion Attacks<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.py:375-399` and `scripts/server.py:903-933`; duplicated at the corresponding locations in `clawhub-raon-os/scripts/server.py` **Vulnerability Type**: Unrestricted upload size and unsafe resource consumption **Risk Level**: Medium ### Vulnerable Code ```python def extract_text_from_pdf(b64data: str) -> str: """Base64 PDF → text using pypdf (fallback: PyPDF2, pdfplumber).""" raw = base64.b64decode(b64data) with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as f: f.write(raw) tmp = f.name try: try: from pypdf import PdfReader reader = PdfReader(tmp) return "\n".join(p.extract_text() or "" for p in reader.pages).strip() except ImportError: pass try: from PyPDF2 import PdfReader as PdfReader2 reader = PdfReader2(tmp) return "\n".join(p.extract_text() or "" for p in reader.pages).strip() except ImportError: pass import pdfplumber with pdfplumber.open(tmp) as pdf: return "\n".join(p.extract_text() or "" for p in pdf.pages).strip() finally: os.unlink(tmp) ``` ```python # Read body content_length = int(self.headers.get("Content-Length", 0)) if content_length == 0: self._send_error(400, "empty_body") return try: body = json.loads(self.rfile.read(content_length)) except json.JSONDecodeError: self._send_error(400, "invalid_json") return text = body.get("text", "").strip() pdf_b64 = body.get("pdf_base64", "").strip() if pdf_b64 and not text: try: text = extract_text_from_pdf(pdf_b64) except Exception as e: self._send_error(400, f"pdf_parse_error: {e}") return ``` ### Technical Analysis The server trusts the request's `Content-Length` and reads the complete body into memory without enforcing an upper bound. It then parses the complete JSON d ...[truncated 1645 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Enforce limits before reading the request: ```python MAX_BODY_BYTES = 12 * 1024 * 1024 raw_length = self.headers.get("Content-Length") try: content_length = int(raw_length or "0") except ValueError: self._send_error(400, "invalid_content_length") return if content_length <= 0 or content_length > MAX_BODY_BYTES: self._send_error(413, "payload_too_large") return ``` Also implement the following controls: - Stream large bodies rather than reading them into one allocation. - Use strict Base64 validation and estimate decoded size before decoding. - Set a lower decoded-PDF size limit. - Limit PDF page count, object count, and extracted-text length. - Parse PDFs in a separate, sandboxed worker process with CPU, memory, file-size, and wall-clock limits. - Terminate the worker on timeout. - Use per-key and per-source request-rate limits. - Configure reverse-proxy body-size and timeout limits. - Move from the single-threaded development server to a production server with bounded worker concurrency. - Keep temporary files in a restricted directory and verify cleanup. - Add malformed, oversized, and decompression-heavy PDF tests. - Apply equivalent protections to the duplicated server. ]]>
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 (188)

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

Critical
Category
Data Flow
Content
def _ollama_available() -> bool:
    """Ollama 서버 응답 여부 확인 (3초 타임아웃)."""
    try:
        urllib.request.urlopen(f"{OLLAMA_URL}/api/tags", timeout=3)
        return True
    except Exception:
        return False
Confidence
91% confidence
Finding
The code uses OLLAMA_URL directly from the environment to make outbound HTTP requests without validating the scheme, host, or destination. In a hostile or multi-tenant environment, an attacker who can influence environment variables can redirect requests to arbitrary internal or external endpoints, creating an SSRF-style primitive and potentially leaking prompts or probing internal services.

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

Critical
Category
Data Flow
Content
body = json.dumps(data, ensure_ascii=False).encode("utf-8") if data is not None else None
    req = urllib.request.Request(url, data=body, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req, timeout=5) as resp:
            resp_body = resp.read()
            if resp_body:
                return json.loads(resp_body.decode("utf-8"))
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
def _ollama_available() -> bool:
    """Ollama 서버 응답 여부 확인 (3초 타임아웃)."""
    try:
        urllib.request.urlopen(f"{OLLAMA_URL}/api/tags", timeout=3)  # nosec B310
        return True
    except Exception:
        return False
Confidence
92% confidence
Finding
The module trusts OLLAMA_URL from the environment and uses it directly in urllib.request.urlopen, allowing requests to be redirected to an arbitrary host instead of the expected local Ollama service. In an agent setting, a manipulated environment could turn local-only model traffic and availability checks into outbound SSRF-style requests or cause prompts and embeddings to be sent to an attacker-controlled endpoint.

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

Critical
Category
Data Flow
Content
body = json.dumps(data, ensure_ascii=False).encode("utf-8") if data is not None else None
    req = urllib.request.Request(url, data=body, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req, timeout=5) as resp:  # nosec B310
            resp_body = resp.read()
            if resp_body:
                return json.loads(resp_body.decode("utf-8"))
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
98% confidence
Finding
The declared description presents a feature-rich AI platform for startup support, including analysis, matching, integrations, and retrieval-based AI capabilities. The supplied code chunk does not implement or expose any of those domain-specific behaviors. Instead, it merely acts as a launcher that synchronously executes a bash script with forwarded arguments. While this could be a small wrapper around a larger implementation, based on the provided code alone the observable behavior is materially different from the declared purpose and includes an undeclared CLI delegation mechanism.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a feature-rich AI assistant for startup support, but the supplied code chunk does not implement or expose any of those behaviors. It simply delegates execution to a bash script via spawnSync. While this may be a thin wrapper around a larger implementation, based on the provided chunk alone the actual behavior is a command-line launcher for a shell script, which is materially different from the declared end-user functionality and introduces execution of a local script not mentioned in the description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
The supplied code substantially supports the declared 'Agentic RAG' portion and some government program matching/eligibility behavior. It includes HyDE, Multi-Query RAG Fusion, CRAG-like critique, recursive retrieval, structured filtering over local evaluation data, and realtime fetching from approved government-related domains. However, several prominent declared capabilities are not represented in this code chunk: there is no implementation for connecting with a database/network of 3,972+ TIPS-selected startups, no investor recommendation logic, no Kakao i OpenBuilder integration, and no clear business-plan evaluation module. 'Track B financial matching' is also not specifically implemented; only generic budget/deadline filtering appears. Therefore the description overstates the skill's behavior relative to this code.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The code’s actual scope is much narrower than the declared description. It implements a static financial-product matcher focused on Korean financing/support products, with simple scoring by track, keywords, and loan preference, plus optional LLM-generated explanatory text. This partially aligns with the description’s mention of Track B financial matching and startup funding support, but it does not implement most of the headline capabilities: no business plan analysis, no startup-network connection feature, no investor matching, no OpenBuilder integration, and no evidence of Agentic RAG or structured extraction pipelines. The primary behavior is therefore a simple financial recommendation utility rather than the broader AI startup companion described.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The supplied code does not implement business-plan evaluation, funding-program matching, investor recommendations, RAG, structured extraction, startup-network connection, or Kakao integration. Instead, it is a gamification subsystem that tracks actions like evaluate/match/draft by awarding XP, levels, badges, and streaks, and saves this state to a local JSON profile. While some action names loosely relate to the declared product domain, the code chunk’s actual primary purpose is unrelated support functionality and includes undeclared capabilities such as local profile persistence and CLI reset/view operations. Therefore the description does not accurately represent what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description promises a broad AI-powered startup support system focused on Korean founders, including business plan assessment, policy funding matching, startup network connections, investor recommendations, Kakao integration, and advanced retrieval/extraction features. The supplied code does none of those things. It reads a local markdown reference file, parses sections into categories, exposes CLI commands to list/detail categories, and recommends ideas using straightforward keyword and synonym matching. This is a materially different primary purpose and omits nearly all advertised capabilities. No suspicious undeclared sensitive behavior is present, but the description substantially misrepresents the code's actual functionality.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The code chunk does not implement an AI startup companion or any user-facing advisory, matching, recommendation, networking, or chatbot integration features described in the declaration. Its actual purpose is much narrower and different: offline ingestion/parsing of a specific government PDF into JSONL. While this could be a supporting data-preparation component for a larger funding-matching system, the declared description presents broad product capabilities that are not represented in this code. Therefore the description does not accurately reflect what this supplied code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The supplied code is a retrieval infrastructure component, not a full startup companion product. It supports ingesting JSONL documents, embedding them, performing hybrid search, and optional LLM reranking. While this could support a broader startup-advice system, the chunk shown does not implement several major capabilities emphasized in the description: business plan evaluation, funding-program matching as a dedicated decision engine, startup network connection, investor recommendations, Kakao integration, structured extraction, Track B financial matching, or specific Agentic RAG methods like HyDE/Multi-Query/CRAG. The actual primary purpose is a command-line RAG/search pipeline over local evaluation data, which is materially narrower than the declared end-user functionality.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description presents a domain-specific startup advisory product with funding-program matching, startup/investor discovery, Kakao integration, and advanced RAG/extraction features. The supplied code does not implement any of those product behaviors. Instead, it is an infrastructure/helper module for generic LLM chat and embedding access across several providers, with provider detection, environment loading, cosine similarity, and a test CLI. While such a module could support a larger AI application, this code chunk by itself materially differs from the declared purpose and lacks the claimed domain-specific capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The code broadly supports part of the declared purpose: it is clearly a startup-focused API with business-plan evaluation, improvement, government-program matching, investor-related mode names, Kakao webhook support, and optional Agentic RAG. However, the description does not accurately represent the full behavior of this chunk. The server includes significant undeclared operational capabilities: admin endpoints for API key lifecycle management, authentication/rate limiting, usage accounting, feedback ingestion, local history logging, and optional Supabase storage. It also exposes additional product features not mentioned in the description, including valuation, idea suggestion, gamification, and profile handling. In the other direction, some strongly advertised claims are not substantiated by this code chunk, especially the specific 'connect with 3,972+ TIPS-selected startups' functionality and explicit structured extraction / Track B financial matching logic. So the description is directionally related but materially incomplete and partially overstated versus the supplied code.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The supplied code does not implement business-plan evaluation, funding-program matching, startup/investor connection, Kakao integration, Agentic RAG, structured extraction, or financial matching. Instead, it is a utility module for local persistence/telemetry: it reads Supabase credentials from environment files, validates the configured Supabase domain, and inserts records into raon_evaluations and raon_feedback tables over HTTP. This is a materially different function from the declared end-user startup companion capabilities, and the external resource access to Supabase is not reflected in the description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The description presents a broad startup-assistant platform with funding-program matching, startup network connections, investor recommendations, Kakao integration, and advanced RAG-based retrieval features. The supplied code chunk does something much narrower: it classifies input text into startup evaluation tracks (A/B/AB) and selects a corresponding system prompt. While this may be a supporting component for a larger founder-support product, its actual behavior is materially different from the declared primary purpose and does not implement the listed flagship capabilities. The track-based evaluation behavior is only loosely related to the description's mention of 'Track B financial matching,' but the code itself performs text classification rather than funding matching or financial analysis. Therefore this chunk does not accurately match the declared description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description presents a broad AI startup companion with funding-program matching, networking, investor recommendations, document understanding, and platform integration. The supplied code does none of those things. It only performs valuation calculations and report generation based on manual numeric inputs, with a placeholder CLI argument for a business-plan PDF marked for future LLM integration. While TIPS and government R&D are referenced, they are used merely as valuation adjustment factors, not as actual funding-program matching or eligibility analysis. This is a materially different primary purpose, so the description does not accurately represent the code.

Tp4

High
Category
MCP Tool Poisoning
Confidence
87% confidence
Finding
The declared description presents a broad AI startup assistant with specific domain capabilities and integrations. The supplied code chunk, however, only implements a front-end chat widget: it injects UI elements into a webpage, supports text input and PDF upload, and posts requests to generic backend endpoints for four modes. While 'business plan evaluation' and possibly 'matching' are loosely consistent with the mode names, the code does not itself implement or demonstrate the more specific declared capabilities such as government funding program matching details, investor recommendations, TIPS startup connections, Kakao i OpenBuilder integration, advanced RAG techniques, or financial matching. Because the actual code's primary purpose is an embeddable chat interface rather than the richer startup-companion behavior described, this is a description-to-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description presents a broad founder-facing assistant with retrieval, recommendation, matching, and platform integration features. The supplied code instead implements a command-line evaluation/benchmarking utility. It stores actual review outcomes in JSONL files, invokes another script to score a plan, compares LLM outputs with ground truth, and reports accuracy metrics such as confusion matrices, precision, recall, and F1. While this loosely relates to 'evaluate business plans,' the primary behavior is offline model evaluation and performance tracking, not delivering the described startup companion capabilities. This is a material description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding
The declared description presents a broad AI-powered startup platform with business-plan evaluation, startup/investor discovery, Kakao integration, and advanced RAG/extraction capabilities. The supplied code chunk is much narrower: it is a standalone financial mapping utility that matches predefined Korean financing products using simple scoring based on track, keywords, and loan preference, then formats recommendations. While this partially overlaps with the declared 'Track B financial matching' and funding-program recommendation theme, the main advertised capabilities are absent from this code. There are no undeclared risky behaviors, but the description materially overstates what this code actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The code chunk’s primary purpose is gamification and profile management, not an AI-powered startup advisory or matching system. It reads and writes a local JSON profile, awards XP for actions, computes levels, tracks streaks, grants badges, and exposes CLI commands for viewing/resetting the profile. None of the core declared capabilities—business plan evaluation, government funding matching, TIPS startup connection, investor recommendations, Agentic RAG, structured extraction, Track B financial matching, or Kakao i OpenBuilder integration—are implemented in this code. While some action names like 'evaluate', 'match', and 'draft' suggest integration points with a larger startup tool, this chunk itself is materially different in purpose and performs undeclared gamification and profile persistence functionality.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description promises a broad AI startup assistant focused on Korean founders, including business plan evaluation, government grant/program matching, startup network connections, investor recommendations, Kakao integration, and advanced retrieval features. The provided code does none of those things. It only loads a local reference markdown file (yc-rfs.md), parses idea categories with regex, exposes CLI commands to list/detail categories, and recommends top idea categories using lightweight keyword and synonym matching. This is a materially different primary purpose and lacks the core declared capabilities. There is no evidence of external integrations, RAG pipelines, financial matching, networking, or investor/program databases.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a broad AI assistant product with founder-facing analysis, recommendation, matching, networking, and integration capabilities. The supplied code chunk does not implement any of those user-facing functions. Instead, it performs a narrow data-ingestion/preprocessing task: reading a specific PDF, extracting program information via tables/regex, and saving JSONL output. While this could support a later funding-matching system, the code itself is only a parser for government announcement data. That is a materially different primary purpose from the declared startup companion functionality, so this is a clear description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The description presents a broad end-user startup assistant with multiple specialized product capabilities and integrations. The supplied code chunk only provides a backend retrieval/indexing component for document search: chunking JSONL records, embedding them, hybrid BM25/vector search, optional LLM reranking, and evaluation. While this could support part of a larger RAG-based assistant related to government programs and startup information, it does not itself implement most of the specifically claimed functions. The primary purpose of the code is narrower and infrastructural than the declared description, so the description materially overstates what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The description claims a domain-specific startup-assistant product with funding-program matching, startup/investor discovery, Kakao integration, and advanced RAG/extraction features. The supplied code does not implement any of those product behaviors. Instead, it is an infrastructure/helper library for generic LLM chat and embedding operations, with provider auto-detection and a small CLI test harness. This is a materially different primary purpose. Additionally, the code accesses external AI provider APIs and local Ollama, which are not reflected in the declared purpose. Therefore the description does not accurately represent the code chunk.