T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/verify_matrix_device_sdk.mjs:200
- Finding
- Unrestricted homeserver destination can expose Matrix access tokens or passwords<![CDATA[ ## Vulnerability Details **File Location**: `scripts/verify_matrix_device.mjs:207-228`, `scripts/verify_matrix_device_sdk.mjs:200-248` **Vulnerability Type**: Credentials transmitted to an unvalidated, user-controlled network destination **Risk Level**: High ### Vulnerable Code From `scripts/verify_matrix_device.mjs:207-228`: ```js const homeserver = await promptRequiredText("Homeserver URL", args.homeserver); let account; if (args["access-token"]) { const userId = await promptRequiredText("Matrix user ID", args["user-id"] || args.username); const targetAccessToken = await promptRequiredSecret("Access token (hidden)"); account = { accountId: null, userId, targetAccessToken }; console.log(`[+] Access-token mode target: ${account.userId}`); } else if (args.password) { const userId = await promptRequiredText("Matrix user ID", args["user-id"] || args.username); const password = await promptRequiredSecret("Password (hidden)"); const targetDeviceId = await promptRequiredText("Target device ID", args["device-id"]); account = { accountId: null, userId, password, targetDeviceId }; console.log(`[+] Password mode target: ${account.userId} / ${account.targetDeviceId}`); } else { const openclawJsonPath = args["openclaw-json"] || DEFAULT_OPENCLAW_JSON; if (!fs.existsSync(openclawJsonPath)) { throw new Error(`OpenClaw config not found at ${openclawJsonPath}. Use --access-token or --password to test without openclaw.json.`); } const oc = readJson(openclawJsonPath); const username = await promptRequiredText("Username", args.username); account = resolveOpenClawAccount(oc, username, openclawJsonPath); ``` From `scripts/verify_matrix_device_sdk.mjs:200-248`: ```js async function matrixRequest({ homeserver, accessToken, path, method = "GET", body }) { const headers = {}; if (accessToken) { headers.Authorization = `Bearer ${accessToken}`; } if (body !== undefined) { headers["Content-Type"] = "application/json"; } ...[truncated 3916 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse the homeserver with `new URL()` before handling any credential. 2. Require the `https:` protocol in normal operation. Permit HTTP only through an explicit development-only option with a prominent warning. 3. Reject URLs containing embedded usernames or passwords, fragments, or otherwise unexpected URL components. 4. Obtain the expected homeserver from trusted per-account configuration or trusted Matrix discovery and bind the selected account to that origin. 5. If a caller supplies an override that differs from the configured origin, require explicit confirmation that clearly identifies the destination and warns that credentials will be sent there. 6. Reject loopback, link-local, private, and other non-public destinations by default, with a narrowly scoped opt-in for legitimate local testing. 7. Configure requests with `redirect: "manual"` and reject redirects for authenticated requests, or explicitly verify that any redirect remains on the previously approved origin. 8. Separate credential-bearing requests from unauthenticated discovery. Do not attach an access token until the destination has been validated. 9. Prefer an existing access token over password authentication, and clearly document that password mode submits the password to the selected homeserver. 10. Add automated tests covering HTTP URLs, attacker-controlled domains, embedded credentials, private IP addresses, mismatched account origins, and cross-origin redirects. ]]>
