Back to skill

Security audit

12306 Backup

Security checks for vulnerabilities and agentic risk

Overview

This skill is purpose-aligned for querying China Railway 12306, with disclosed network use and local output files, but it has install and HTML-report hardening issues.

Reasonable to install if you are comfortable with a Node-based skill contacting 12306 and storing station cache/report files locally. Prefer pinned installation sources when available, use Markdown or JSON output for lower-risk display, and treat generated HTML reports as local files containing your travel query details.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T08 · Insecure Dependencies

Warning
Location
README.md:7
Finding

Unpinned executable installation chain

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/query.mjs:181
Finding

Remote API fields are incompletely escaped in generated HTML

Content
View full analysis
\u2014'; if (val === '无') return '\u65E0'; if (val === '有') return '\u6709'; return `${val}`; } function buildHTML(tickets, from, to, travelDate, filterDesc) { const e = (s) => s.replace(/&/g, '&').replace(/ { const swz = t.swz !== '--' ? t.swz : t.tz !== '--' ? t.tz : '--'; const rw = t.rw !== '--' ? t.rw : t.dw !== '--' ? t.dw : '--'; const typeClass = t.trainCode[0]?.toLowerCase() || ''; const buyClass = t.canBuy === 'Y' ? 'yes' : 'no'; return ` ${e(t.trainCode)} ${e(t.departTime)}\u2192${e(t.arriveTime)} ${formatDuration(t.duration)} ${seatCell(swz)}${seatCell(t.zy)}${seatCell(t.ze)}${seatCell(rw)}${seatCell(t.yw)}${seatCell(t.yz)}${seatCell(t.wz)} ${t.canBuy === 'Y' ? '\u53EF\u8D2D' : '\u552E\u7F44'} `; }).join('\n'); ``` ### Technical Analysis Ticket fields are derived from a remote, pipe-delimited API response and are treated as trusted when constructing the generated HTML report. The following output paths are not safely encoded: - `seatCell()` inserts `val` directly into an HTML text context. - `formatDuration(t.duration)` can return the original remote value when it cannot parse the duration, after which that value is inserted directly into HTML. - `typeClass` is de ...[truncated 2184 chars]
Remediation
View remediation
/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } ``` 2. Encode seat and duration values explicitly: ```js return `${escapeHtml(val)}`; ``` ```js ${escapeHtml(formatDuration(t.duration))} ``` 3. Do not derive CSS classes directly from remote text. Use a strict allowlist: ```js const candidate = String(t.trainCode || '').charAt(0).toLowerCase(); const typeClass = /^[gdz tk]$/.test(candidate) ? candidate : 'other'; ``` The allowlist should be adjusted to contain only the exact train types supported by the application, without spaces. 4. Validate the remote response schema before rendering. Seat fields should accept only expected values such as numeric counts and explicitly supported status strings. Time and duration fields should match strict formats. 5. Prefer DOM construction with `textContent` or a mature templating library that performs context-aware escaping by default instead of assembling HTML with string interpolation. 6. Add a restrictive Content Security Policy to the generated document, for example: ```html ``` 7. Add regression tests using malicious values containing tags, quotes, event handlers, and malformed duration strings, then verify that the resulting document contains only encoded text. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (6)

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding

The README instructs users to install and run a remote skill via npx skills add kirorab/12306-skill without pinning a specific immutable version. That creates a supply-chain risk: future upstream changes, account compromise, or a malicious republish could cause users to fetch different code than expected.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill invokes Node.js scripts that query the official 12306 API, which implies network access, but it does not declare any explicit tool scope such as permissions or allowed-tools. That creates a mismatch between the documented contract and the actual capability, making review, sandboxing, and policy enforcement weaker and increasing the chance of unintended outbound requests.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

This is a markdown file, so SQP-2 applies to omissions in user-facing safety disclosures. The README states that the skill directly calls the 12306 official API, but it does not warn users that origin, destination, date, and related query parameters will be sent over the network to a third-party service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The skill defaults to HTML mode that writes query results to disk, but this side effect is not clearly surfaced as a warning near the primary usage. Hidden file writes can surprise users and agents, potentially leaving travel-related data on disk or causing downstream components to treat the behavior as read-only when it is not.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The generated HTML sets lang="zh-CN" and uses Chinese-only labels and toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }), which forces a specific language/locale in user-facing output. The file does not offer any language selection or document that the tool is intentionally limited to a Chinese-speaking audience as a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The station resolution logic strips Chinese suffixes like “市” and “站”, and the script is tied to 12306 Chinese station data, indicating a Chinese-locale assumption. There is no natural-language indication or user opt-in for this locale constraint, so the skill implicitly forces a specific language/locale behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.