Back to skill

Security audit

Travel Search RU

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed travel-search skill that sends trip criteria to one read-only HTTPS service and does not show local persistence, credential access, booking, or destructive behavior.

Before installing, be aware that trip criteria such as destinations, dates, traveler counts, child ages, budget, and preferences are sent to an external service. Do not include names, contacts, passport/payment details, credentials, or other unnecessary sensitive data. Treat returned descriptions, text, and booking links as untrusted travel data and verify final prices, availability, and train details on the provider site before acting.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/travel_search.py:483
Finding
Untrusted Remote MCP Content Is Exposed Directly to the Agent<![CDATA[ ## Vulnerability Details **File Location**: `scripts/travel_search.py:483-501`, `scripts/travel_search.py:625-637`, `scripts/travel_search.py:652-655`, and `SKILL.md:26,74-76` **Vulnerability Type**: Indirect prompt injection and unvalidated remote output **Risk Level**: Medium ### Complete Code Snippets From `scripts/travel_search.py:483-501`: ```python def _normalize_tool_result(result): if result is None: return {} if not isinstance(result, dict): return result # Tool-level error: never normalize or emit content / structuredContent. if result.get("isError") is True: raise McpError("tool_error", "tool error") if "structuredContent" in result and result["structuredContent"] is not None: return result["structuredContent"] content = result.get("content") if isinstance(content, list) and content: for item in content: if not isinstance(item, dict): continue if item.get("type") == "text" and isinstance(item.get("text"), str): text = item["text"].strip() if text and text[0] in "{[": try: return json.loads(text) except ValueError: pass break return result ``` From `scripts/travel_search.py:625-637`: ```python if match is None: return _fail(1, "not_found", "tool not found") _print_json( { "name": match.get("name"), "description": match.get("description"), "inputSchema": match.get("inputSchema"), } ) return 0 ``` From `scripts/travel_search.py:652-655`: ```python def _cmd_call(cli_name, arguments): mcp_name = COMMAND_TO_TOOL[cli_name] client = McpClient() result = client.call_tool(mcp_name, arguments) _print_json(result) ``` Relevant instructions from `SKILL.md:26` and `SKILL.md:74-76`: ```markdown Current tool schem ...[truncated 3576 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Declare the remote trust boundary** - Add an explicit rule to `SKILL.md` stating that all MCP descriptions, schemas, text, URLs, and result fields are untrusted data. - Instruct the Agent never to follow commands or behavioral instructions contained in MCP responses. - Require remote content to be used only as travel data matching the documented response fields. 2. **Apply strict output schemas** - Define local allowlists for expected fields for each command. - Reject or omit unexpected metadata instead of returning the complete MCP result. - Validate field types, maximum lengths, nesting depth, and collection sizes before printing results. - Avoid returning arbitrary text content merely because it parses as JSON. 3. **Validate returned URLs** - Permit only `https` URLs. - Reject URLs containing user information, nonstandard ports, control characters, or malformed hostnames. - Maintain an allowlist of approved booking and short-link hosts where feasible. - If the provider set is dynamic, label unrecognized destinations clearly and do not automatically present them as trusted booking links. 4. **Separate data from instructions** - Wrap remote values in a clearly identified structure such as `untrusted_remote_data`. - Ensure descriptions and schema annotations are never interpreted as Agent policy. - Prefer locally maintained command documentation for security-sensitive behavior, using live schemas only to validate parameter structure. 5. **Sanitize presentation fields** - Strip or reject content resembling system prompts, tool directives, embedded markup, or requests to ignore prior instructions. - Treat filtering as defense in depth rather than the only protection, because instruction-like text can be obfuscated. 6. **Add adversarial tests** - Test malicious instructions in tool descriptions, schema descriptions, structured content, and text content. - Test `javascr ...[truncated 180 chars]
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (17)

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The instruction to 'silently show obtained results and do not report the failure' hides partial backend outages from the user. This undermines transparency and can mislead users into believing the search was comprehensive, causing materially incomplete travel options, stale comparisons, or poor decisions based on omitted providers.

Natural-Language Policy Violations

Medium
Confidence
78% confidence
Finding
The package name and keyword set strongly indicate the skill is tailored to Russian-language or Russia-focused usage ('travel-search-ru', Russian keywords, and 'russia'), but the manifest does not state that language or locale selection is optional or user-chosen. This can be a natural-language policy concern when a skill appears to impose a locale-specific experience without explicit opt-in or justification.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This markdown file contains substantive operational instructions in Russian, while the rest of the document is in English. That creates an implicit language requirement for users without any opt-in, language choice, or documented reason that the skill is region- or locale-specific.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This CLI forwards user-supplied travel search data to a third-party remote service at mcp.botclaw.ru, and the file contains no disclosure, consent flow, or data-minimization controls. Travel planning queries can include sensitive personal information such as destinations, dates, family composition, budget, and booking preferences, so silent transmission creates a real privacy and data-governance risk even though the transport is HTTPS.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Unbounded Resource Access

Medium
Category
Excessive Agency
Content
def test_timeout_exits_1(self):
        _STATE.delay_seconds = 2.0
        client = travel_search.McpClient(endpoint=self.server.endpoint, timeout=0.2)
        with self.assertRaises(travel_search.McpError):
            client.list_tools()
        # CLI path
Confidence
75% confidence
Finding
Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Unbounded Resource Access

Medium
Category
Excessive Agency
Content
def test_timeout_exits_1(self):
        _STATE.delay_seconds = 2.0
        client = travel_search.McpClient(endpoint=self.server.endpoint, timeout=0.2)
        with self.assertRaises(travel_search.McpError):
            client.list_tools()
        # CLI path
Confidence
75% confidence
Finding
Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Unbounded Resource Access

Medium
Category
Excessive Agency
Content
def test_timeout_exits_1(self):
        _STATE.delay_seconds = 2.0
        client = travel_search.McpClient(endpoint=self.server.endpoint, timeout=0.2)
        with self.assertRaises(travel_search.McpError):
            client.list_tools()
        # CLI path
Confidence
75% confidence
Finding
Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
cache_dir.is_dir() or True,
            "local caches may or may not exist; exclusion is still required",
        )
        proc = subprocess.run(
            ["npm", "pack", "--dry-run", "--json"],
            cwd=str(ROOT),
            capture_output=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Unbounded Resource Access

Medium
Category
Excessive Agency
Content
_STATE.stream_mode = "sse_keepalive_only"
        _STATE.stream_hold_seconds = 5.0
        _STATE.stream_keepalive_interval = 0.05
        timeout = 0.6
        client = travel_search.McpClient(
            endpoint=self.server.endpoint, timeout=timeout
        )
Confidence
75% confidence
Finding
Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Unbounded Resource Access

Medium
Category
Excessive Agency
Content
_STATE.stream_mode = "sse_keepalive_only"
        _STATE.stream_hold_seconds = 5.0
        _STATE.stream_keepalive_interval = 0.05
        timeout = 0.6
        client = travel_search.McpClient(
            endpoint=self.server.endpoint, timeout=timeout
        )
Confidence
75% confidence
Finding
Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Unbounded Resource Access

Medium
Category
Excessive Agency
Content
_STATE.stream_mode = "sse_keepalive_only"
        _STATE.stream_hold_seconds = 5.0
        _STATE.stream_keepalive_interval = 0.05
        timeout = 0.6
        client = travel_search.McpClient(
            endpoint=self.server.endpoint, timeout=timeout
        )
Confidence
75% confidence
Finding
Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Unbounded Resource Access

Medium
Category
Excessive Agency
Content
_STATE.stream_mode = "sse_keepalive_only"
        _STATE.stream_hold_seconds = 5.0
        _STATE.stream_keepalive_interval = 0.05
        timeout = 0.6
        client = travel_search.McpClient(
            endpoint=self.server.endpoint, timeout=timeout
        )
Confidence
75% confidence
Finding
Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Vague Triggers

Low
Confidence
83% confidence
Finding
This manifest describes the skill's capabilities in very broad terms like 'Travel search for planning trips' and lists many generic travel keywords, but it does not provide any explicit trigger phrases, scope boundaries, or exclusion conditions. In a manifest file, that kind of broad wording can contribute to ambiguous invocation matching for common travel-related requests.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The client identifies itself as "travel-search-ru", which is a locale-specific natural-language indicator. In this file there is no visible opt-in, language selection, or explanation that the skill is intentionally limited to Russian users or content.

Static analysis

No suspicious patterns detected.