T09 · Insecure Skill Coding Practices
- Location
scripts/client.js:40- Finding
Bearer Token Disclosure Through Unvalidated Configurable Endpoints
- Content
View full analysis
Vulnerability Details
File Location:
scripts/client.js:4-20,scripts/client.js:40-48,scripts/volcseed.js:50-57, andscripts/volcseed.js:63-68
Vulnerability Type: Unvalidated outbound request destination with sensitive authorization header
Risk Level: HighThe endpoint URLs are loaded from
AIZNT_PROXY_URLSand used without validating their protocol, hostname, or port. TheTS_TOKENcredential is then attached as a Bearer token to requests sent to those endpoints.Relevant endpoint-loading code from
scripts/client.js:4-20:js const token = (process.env.TS_TOKEN || '').trim(); if (!token) { throw new Error('缺少 TS_TOKEN(天树对话凭证 ts_xxx,对应 skills.entries 的 apiKey)'); } const raw = process.env.AIZNT_PROXY_URLS; if (!raw || !String(raw).trim()) { throw new Error('缺少 AIZNT_PROXY_URLS(JSON 字符串,与 GET /miniapp/ai/chat/credentials 返回的 aiznt_proxy_urls 一致)'); } let urls; try { urls = typeof raw === 'string' ? JSON.parse(raw) : raw; } catch { throw new Error('AIZNT_PROXY_URLS 不是合法 JSON'); } if (!urls || typeof urls !== 'object') { throw new Error('AIZNT_PROXY_URLS 必须是对象'); } return { token, urls };Relevant authorization and request code from
scripts/client.js:40-48:js function authHeaders(token, extra = {}) { return { Authorization: `Bearer ${token}`, ...extra, }; } async function fetchJson(url, options = {}) { const res = await fetch(url, options);The configured destinations are used directly in
scripts/volcseed.js:50-57:js const url = urls.volcseededit_submit; if (!url) throw new Error('AIZNT_PROXY_URLS 缺少 volcseededit_submit'); const data = await fetchJson(url, { method: 'POST', headers: authHeaders(token, { 'Content-Type': 'application/json' }), body: JSON.stringify({ prompt, image_urls: imageUrls }), });The same behavior occurs for result retrieval in
scripts/volcseed.js:63-68:` ...[truncated 2901 chars]
- Remediation
View remediation
Remediation Suggestions
- Parse every configured endpoint with
new URL()before issuing a request. - Require the
https:scheme and reject plaintext HTTP, embedded URL credentials, fragments, and unexpected ports. - Enforce an explicit allowlist of trusted Volcseed/Tianshu hostnames. Compare normalized hostnames exactly rather than using suffix checks that can accept domains such as
trusted.example.attacker.com. - Validate the destination immediately before adding the
Authorizationheader, so future callers cannot bypass validation. - Configure redirects conservatively. Prefer
redirect: 'manual', or revalidate every redirect target and never forward authorization headers across origins. - Use a service-specific, least-privileged, short-lived token instead of a broadly reusable credential where supported.
- Validate
task_idagainst the service's expected identifier syntax. At minimum, encode replacements withencodeURIComponent()when inserting them into path or query components. - Apply outbound network controls so the Skill can reach only approved service addresses and cannot access loopback, link-local, private, or cloud-metadata endpoints.
- Avoid logging credentials or complete sensitive responses, and redact signed URLs or other secrets before displaying errors and results.
- Add tests confirming rejection of HTTP URLs, unapproved domains, embedded credentials, deceptive subdomains, unexpected ports, internal addresses, and cross-origin redirects.
- Parse every configured endpoint with
