T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/publish.mjs:31
- Finding
- Unvalidated API Base URL Allows Credential and Project Data Exfiltration## Vulnerability Details **File Location**: `scripts/publish.mjs`, lines 31, 61–67, 81–83, 116–127, and 136–161 **Vulnerability Type**: Unvalidated network endpoint override and sensitive-data disclosure **Risk Level**: High ### Vulnerable Code ```js const baseUrl = (process.env.YEZHOU_BASE_URL || "https://yz.gbfeng.com").replace(/\/$/, ""); ``` ```js async function api(path, options = {}) { let response; try { response = await fetch(`${baseUrl}${path}`, options); } catch (error) { fail(`Unable to connect to 页舟: ${error instanceof Error ? error.message : String(error)}`); } const body = await response.json().catch(() => ({})); return { response, body }; } ``` ```js async function listSites(accessToken){ return api("/api/agent/sites",{headers:{Authorization:`Bearer ${accessToken}`}}); } ``` ```js async function publish(accessToken, payload, siteId) { const project=inputKind!=="html",basePath=project?"/api/agent/projects":"/api/agent/sites"; return api(siteId ? `${basePath}/${encodeURIComponent(siteId)}` : basePath, { method:siteId ? "PUT" : "POST", headers:{Authorization:`Bearer ${accessToken}`,...(project?{}:{"Content-Type":"application/json; charset=utf-8"})}, body:project?payload:JSON.stringify(payload), }); } ``` ```js let accessToken = await savedCredential(); if (!accessToken) accessToken = await authorize(); if(listOnly){ let result=await listSites(accessToken); }else{ let payload; if(inputKind==="html"){ let html;try{html=await readFile(inputPath,"utf8");}catch(error){fail(`Unable to read ${inputPath}: ${error instanceof Error?error.message:String(error)}`);} payload={html,title}; }else payload=await projectPayload(); // ... let result = await publish(accessToken,payload,selectedSiteId); } ``` ### Technical Analysis The script permits `YEZHOU_BASE_URL` to replace the documented 页舟 service origin. The s ...[truncated 3147 chars]
- Remediation
- ## Remediation Suggestions 1. **Remove the production endpoint override.** Use a fixed constant for the documented service: ```js const baseUrl = "https://yz.gbfeng.com"; ``` 2. **If an override is operationally necessary, require explicit development mode and validate it before reading credentials or project files.** Parse the value with `URL` and enforce: - The `https:` protocol. - An exact allowlisted hostname. - An expected port. - No embedded username or password. - No unexpected path, query string, or fragment. 3. **Bind credentials to an origin.** Store the authorized service origin with the credential and refuse to send the credential to any different origin. Credentials obtained for production must never be reused with testing or custom endpoints. 4. **Separate development credentials and configuration.** A development endpoint should use an isolated credential file and require a conspicuous command-line option rather than an ambient environment variable. 5. **Validate before sensitive operations.** Complete endpoint validation before calling `savedCredential()`, reading upload files, constructing `FormData`, or initiating any authenticated request. 6. **Provide explicit destination visibility.** Before authentication or publication, display the validated destination origin. If a non-production mode is supported, require explicit user confirmation. 7. **Limit token privileges and lifetime server-side.** Use narrowly scoped, revocable, short-lived tokens where possible, and provide a documented revocation procedure for credentials potentially exposed through endpoint redirection. 8. **Add security regression tests.** Verify that HTTP URLs, alternate hosts, credential-bearing URLs, unexpected ports, path-based origins, and malformed endpoint values are rejected before any credential or project content is accessed.
