Back to skill

Security audit

Trip Planner 0→1

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent travel-planning helper, but several optional sync and setup templates could expose private trip data or user credentials if followed as written.

Install only if you are comfortable treating it as a travel-planning template that needs security edits before cloud sharing. Prefer the default localStorage mode, do not put GitHub PATs or JSONBin master keys into static browser code, use private authenticated sync with high-entropy room IDs, review any Docker or compose files before running them, and remove confirmation numbers or personal travel details before publishing or sharing generated pages.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (6)

T09 · Insecure Skill Coding Practices

Error
Location
references/templates/todo-sync.js:29
Finding

Cloud synchronization credentials are embedded in client-side JavaScript

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/cloudflare-workers-sync.md:47
Finding

Cloudflare Workers synchronization service permits unauthenticated room reads and writes

Content
View full analysis
const m = url.pathname.match(/^\/room\/([a-zA-Z0-9_-]{1,40})$/); if (!m) { return new Response('Not Found', { status: 404, headers: cors }); } const roomId = m[1]; const key = 'room:' + roomId; if (request.method === 'GET') { const data = await env.TRIP_KV.get(key, 'json'); return new Response(JSON.stringify(data || { state: null, ts: 0 }), { headers: { 'Content-Type': 'application/json', ...cors }, }); } if (request.method === 'POST') { const body = await request.json(); if (JSON.stringify(body).length > 256 * 1024) { return new Response('Payload too large', { status: 413, headers: cors }); } await env.TRIP_KV.put(key, JSON.stringify(body)); return new Response(JSON.stringify({ ok: true, ts: body.ts || Date.now() }), { headers: { 'Content-Type': 'application/json', ...cors }, }); } ``` ### Technical Analysis The recommended Worker treats knowledge of a room ID as sufficient permission to read and overwrite its data. There is no authentication, per-room authorization, ownership check, integrity protection, rate limiting, or replay protection. The main Skill recommends room IDs with the pattern `--`. Such identifiers are likely predictable and may disclose names or travel destinations. The route permits identifiers as short as one character, making enumeration and guessing practical. Wildcard CORS allows any website to issue browser-based read and write requests. The optional hardening section only demon ...[truncated 1875 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/self-host-sync.md:19
Finding

Self-hosted synchronization service exposes unauthenticated data through the reverse proxy

Content
View full analysis
{ if (req.method === 'OPTIONS') { res.writeHead(204, cors); return res.end(); } const m = req.url.match(/^\/room\/([^\/\?]+)/); if (!m) { res.writeHead(404, cors); return res.end('Not Found'); } const roomId = m[1]; if (!ROOM_RE.test(roomId)) { res.writeHead(400, cors); return res.end('Bad Room ID'); } const file = path.join(DATA_DIR, roomId + '.json'); if (req.method === 'GET') { res.writeHead(200, { 'Content-Type': 'application/json', ...cors }); if (!fs.existsSync(file)) return res.end(JSON.stringify({ state: null, ts: 0 })); return res.end(fs.readFileSync(file, 'utf-8')); } if (req.method === 'POST') { let body = ''; req.on('data', chunk => { body += chunk; if (body.length > MAX_BODY) { req.destroy(); } }); req.on('end', () => { try { const obj = JSON.parse(body); obj.ts = obj.ts || Date.now(); fs.writeFileSync(file, JSON.stringify(obj)); res.writeHead(200, { 'Content-Type': 'application/json', ...cors }); res.end(JSON.stringify({ ok: true, ts: obj.ts })); } catch (e) { res.writeHead(400, cors); res.end('Invalid JSON'); } }); return; } ``` ### Technical Analysis Binding the Node process to `127.0.0.1` appropriately prevents direct external access to port 3200. However, the documented nginx configuration intentionally publishes the same API over HTTPS, and the API has no authentication by default. Any Internet user who knows or guesses a room ID can read or overwrite its JSON file. The “optional” token enhancement protects ...[truncated 1935 chars]
Remediation
View remediation

T03 · Remote Payload Retrieval and Execution

Error
Location
SKILL.md:57
Finding

Mutable external MCP deployment artifacts are downloaded and executed without integrity verification

Content
View full analysis
Remediation
View remediation
`. - Download the Compose file from a reviewed commit hash or signed release rather than `main`. - Publish and verify cryptographic checksums or signatures before execution. - Include the reviewed Compose configuration directly in the Skill package where feasible. - Require users to inspect mounts, capabilities, ports, environment variables, and Docker socket access before startup. - Run the container as a non-root user with a read-only root filesystem, dropped Linux capabilities, no privileged mode, and no Docker socket mount. - Bind the MCP endpoint only to localhost unless remote access is explicitly required. - Use a version update process that performs a new security review before changing the pinned digest or commit. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/templates/index-skeleton.html:152
Finding

Untrusted marker content is concatenated into Leaflet popup HTML

Content
View full analysis
{ L.marker([m.lat, m.lng]).addTo(map).bindPopup('' + m.title + '
' + m.desc); }); ``` ### Technical Analysis `m.title` and `m.desc` are inserted into an HTML string without escaping or sanitization. Leaflet popup string content is interpreted as HTML. If either field contains attacker-controlled markup, event handlers or other executable HTML can run in the page origin when the popup is opened. The workflow constructs travel content from Web search, map data, social-media posts, hotel names, and other external sources. If externally sourced text is copied into the marker array without strict encoding, malicious content can cross the data-to-code boundary. Static-generation placeholders such as trip title and subtitle should be treated similarly when their source is not trusted. Successful exploitation becomes especially serious when cloud credentials have been embedded into the same page, because injected script can read and transmit those credentials. ### Attack Path 1. An attacker causes a hotel, attraction, imported dataset, or fetched research result to contain crafted HTML, such as an image with an error handler. 2. That text is copied into a marker's `title` or `desc`. 3. The generated page is deployed without output encoding. 4. A victim opens the map and clicks the affected marker. 5. Leaflet parses the concatenated string as HTML. 6. The attacker's script executes in the static page's origin and can read page data, alter Todos, access origin storage, and extract any credentials embedded in JavaScrip ...[truncated 557 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
references/templates/index-skeleton.html:141
Finding

Third-party Leaflet script is loaded without Subresource Integrity protection

Content
View full analysis
``` The associated stylesheet is also loaded from the same CDN without integrity metadata: ```html ``` ### Technical Analysis Although the Leaflet version is explicitly selected, the browser trusts executable JavaScript returned by the third-party CDN on every uncached load. No `integrity` hash is provided, so compromise of the package content, CDN, publishing account, or delivery path would allow modified JavaScript to execute in every deployed trip page. A compromised dependency would run with the same origin privileges as application code. It could read Todo state, inspect trip details, intercept synchronization operations, and steal browser-embedded provider credentials. ### Attack Path 1. An attacker compromises the CDN, package publication process, or upstream artifact associated with the requested URL. 2. The CDN serves modified `leaflet.js`. 3. A victim opens a deployed trip page and the browser downloads the modified script. 4. Because no integrity hash is present, the browser executes it. 5. The script accesses page state, storage, and any synchronization credentials, then sends them to the attacker. ### Impact Assessment The attacker obtains arbitrary JavaScript execution in the context of every affected trip page load. This permits confidentiality and integrity attacks against page content and browser storage and can amplify the credential-exposure vulnerability into access to external provider accounts. ]]>
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (39)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

声明描述的是完整旅行攻略生成与规划系统,而该代码片段仅覆盖其中很小的一部分:交互式 Todo 清单的前端状态管理与可选云同步。它不包含旅行信息采集、搜索整合、路线规划、行程文档生成或部署工作流等核心功能,因此主目的与实际行为存在明显差异。此外,代码确实会访问外部服务接口(GitHub、JSONBin、Cloudflare Workers)进行数据同步,而声明中虽提到“可选云端同步”,但未具体披露这些第三方资源;这属于次要但真实的未明示外部访问能力。综合来看,应判定为描述与行为不匹配。

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

The skill explicitly instructs use of a GitHub Personal Access Token and other cloud service keys for sync. Even though no literal secret is embedded, normalizing credential insertion into client-side or shared trip-planning workflows raises risk of token leakage through browser code, public repositories, shared pages, or accidental exposure to other participants.

Content

Scanner excerpt · SKILL.md (reported line 260)May include surrounding context.

md
### 方案 B:GitHub Gist(零服务器,需 GitHub 账号)
- 把勾选状态 POST 到一个 public Gist
- 用 Personal Access Token(只需 `gist` 权限)
- 代码模板见 `references/templates/todo-sync.js` 的 Gist 变体

### 方案 C:JSONBin.io / jsonbin.io(零服务器,免费 1 万次/月)

Chaining Abuse

High
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Content

Scanner excerpt · references/self-host-sync.md (reported line 114)May include surrounding context.

重载 nginx:

bash
sudo nginx -t && sudo systemctl reload nginx

4. 前端配置

Credential Access

High
Category
Privilege Escalation
Confidence
99% confidence
Finding

The code template explicitly uses a GitHub Personal Access Token in front-end JavaScript. Any real token placed here becomes immediately accessible to all site visitors, enabling unauthorized API calls as the token owner, including reading or modifying private gists used for sync and potentially exhausting rate limits or causing account misuse.

Content

Scanner excerpt · references/templates/todo-sync.js (reported line 31)May include surrounding context.

js
// GitHub Gist 配置(仅 SYNC_BACKEND='gist' 时)
const GIST_CONFIG = {
  token: 'ghp_xxx',            // Personal Access Token(scope: gist)
  gistId: '',                  // 首次留空,自动创建;后续填写 Gist ID
  filename: SYNC_ROOM + '.json',
};

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README promotes third-party research sources, maps, web search, and optional cloud sync, but does not clearly warn users what travel data may be transmitted externally. Because itinerary planning often includes sensitive personal details such as dates, budgets, locations, and accommodations, silent or poorly disclosed transmission increases privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger list is very broad and overlaps with common travel-planning language, which can cause the skill to activate in ordinary conversations without clear user intent. In a skill that performs multi-source research, optional cloud sync, and deployment-oriented workflows, unintended activation can expose user data to external services or cause unnecessary tool use.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding

The README instructs users to pull a Docker image by mutable name only, without a pinned tag or digest. This creates a supply-chain risk because a later-published or compromised image could be fetched instead of the one the author originally intended, leading users to run unreviewed code on their systems.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill instructs use of multiple network-capable components (MCP server, web search, map APIs, cloud sync, deployment) but does not declare any tool scope or allowed-tools boundary. This creates an authorization and transparency gap: a host agent may invoke broader network actions than users or reviewers expect, increasing risk of unintended outbound requests or data disclosure.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The description lists many generic trigger phrases such as “自由行”, “做攻略”, “旅游计划”, “travel plan”, and “itinerary”, which are broad terms a user might say in ordinary conversation rather than explicit skill invocations. The file does not provide narrowing constraints, negative examples, or a specific activation context, increasing the chance of unintended invocation.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding

Pulling xpzouying/xiaohongshu-mcp without a tag or digest fetches a mutable latest image, exposing users to image replacement or upstream compromise. Because this image is used to run an MCP server with network-facing behavior, a malicious update could execute arbitrary code or exfiltrate accessed data.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The MCP configuration is described as supporting content retrieval and publication to Xiaohongshu, but publication is not necessary for trip planning. Including an unnecessary publish-capable integration expands the action surface and could enable accidental or unauthorized posting if connected tools expose write operations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The cloud sync guidance encourages use of public Gists, JSONBin, Workers, and custom backends for storing trip checklist state, but does not provide strong privacy warnings or data-minimization guidance. Trip data can reveal travel dates, locations, companions, and other sensitive logistics, and public or weakly protected storage increases exposure risk.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding

Using npx serve without pinning a version causes execution of whatever package version is current at runtime, which can introduce supply-chain risk or breaking behavior. If the package is compromised or a malicious version is published, users may run attacker-controlled code locally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The example Worker exposes a world-readable GET endpoint and accepts unauthenticated POST writes for any guessed room ID, while the documentation presents it as a recommended sync service without a prominent warning. In this trip-planning context, stored room data can include itinerary details and collaborative task state, so attackers can overwrite or read travel-related data if IDs are discovered or guessed.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/cloudflare-workers-sync.md (reported line 108)May include surrounding context.

bash
# 写
curl -X POST https://trip-sync.your-subdomain.workers.dev/room/test123 \
  -H "Content-Type: application/json" \
  -d '{"state":{"u0":true},"ts":1714000000000}'

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The service allows any client who knows or guesses a room ID to read and overwrite stored trip data because GET and POST are unauthenticated and CORS permits any origin. In the trip-planning context this is more dangerous because itineraries can contain personal travel dates, destinations, and lodging details, enabling privacy loss and tampering.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · references/self-host-sync.md (reported line 95)May include surrounding context.

text

```bash
sudo systemctl enable --now trip-sync
sudo systemctl status trip-sync

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · references/self-host-sync.md (reported line 96)May include surrounding context.

text

```bash
sudo systemctl enable --now trip-sync
sudo systemctl status trip-sync

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · references/self-host-sync.md (reported line 114)May include surrounding context.

text

```bash
sudo systemctl enable --now trip-sync
sudo systemctl status trip-sync

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · references/self-host-sync.md (reported line 95)May include surrounding context.

text

```bash
sudo systemctl enable --now trip-sync
sudo systemctl status trip-sync

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/self-host-sync.md (reported line 133)May include surrounding context.

bash
# 读空房间(应返回 {state:null,ts:0})
curl https://your-domain.com/trip-sync/room/test123

# 写
curl -X POST https://your-domain.com/trip-sync/room/test123 \

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · references/self-host-sync.md (reported line 157)May include surrounding context.

自动备份

bash
# crontab -e
0 3 * * * tar czf /backup/trip-sync-$(date +\%Y\%m\%d).tar.gz /home/user/trip-sync/data

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The HTML document hard-codes lang="zh-CN", and the visible interface text throughout the template is also in Chinese. This creates a natural-language locale constraint without any opt-in, fallback, or documentation that the template is intended only for a China-specific audience.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The template explicitly instructs users to place GitHub PATs and JSONBin master keys directly into client-side JavaScript. In a browser-delivered static page, those secrets are exposed to every visitor via page source or developer tools, allowing anyone to steal the credentials and read, modify, or delete synced data and potentially abuse the linked third-party account.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/templates/todo-sync.js (reported line 61)May include surrounding context.

js
const GistBackend = {
  async load() {
    if (!GIST_CONFIG.gistId) return null;
    const r = await fetch('https://api.github.com/gists/' + GIST_CONFIG.gistId, {
      headers: { 'Authorization': 'token ' + GIST_CONFIG.token }
    });
    if (!r.ok) return null;

Static analysis

No suspicious patterns detected.