T09 · Insecure Skill Coding Practices
Error
- Location
- trifle-auth.mjs:39
- Finding
- Configurable backend permits JWT and SIWE credential disclosure to an arbitrary server## Vulnerability Details **File Location**: `trifle-auth.mjs`, lines 39-41 and 96-134 **Vulnerability Type**: Unrestricted credential destination and cleartext transport permitted **Risk Level**: High ```javascript const STATE_FILE = process.env.TRIFLE_AUTH_STATE || join(STATE_DIR, 'auth-state.json'); const SETTINGS_FILE = join(CONFIG_DIR, 'settings.json'); const BACKEND_URL = process.env.TRIFLE_BACKEND_URL || SERVERS.live; async function apiRequest(path, options = {}) { const url = `${BACKEND_URL}${path}`; const res = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', 'Origin': 'https://trifle.life', 'Referer': 'https://trifle.life/', ...options.headers, }, }); if (!res.ok) { const text = await res.text(); throw new Error(`API error ${res.status}: ${text}`); } return res.json(); } async function authenticatedRequest(path, options = {}) { const state = loadState(); if (!state.token) { throw new Error('Not authenticated. Run: trifle-auth.mjs login'); } return apiRequest(path, { ...options, headers: { 'Authorization': `Bearer ${state.token}`, ...options.headers, }, }); } ``` During login, the same unrestricted request function also transmits the signed SIWE message: ```javascript const result = await apiRequest('/auth/wallet/verify', { method: 'POST', body: JSON.stringify({ signature, message, chainId: mainnet.id, }), }); ``` ### Technical Analysis `TRIFLE_BACKEND_URL` completely controls the origin receiving authentication traffic. The value is not parsed, restricted to an approved hostname, or required to use HTTPS. Consequently, authenticated commands send the stored bearer JWT to any configured destination, including an attacker-controlled server or a cleartext HTTP endpoint. Sending a SIWE signature and JWT to ...[truncated 1743 chars]
- Remediation
- ## Remediation Suggestions - Remove `TRIFLE_BACKEND_URL` unless arbitrary backends are a documented and essential feature. - If staging support is required, accept only symbolic values such as `live` and `staging`, then map them internally to the hardcoded HTTPS URLs. - Parse the final URL with `new URL()` and require: - `protocol === 'https:'` - an exact allowlisted hostname - no embedded username or password - an expected port - Refuse redirects for requests carrying JWTs, or validate every redirect destination before forwarding credentials. - Maintain separate authentication state per approved server so a staging token cannot be sent to production or vice versa. - Never attach `Authorization` automatically to a request until the final destination has passed origin validation. - Add automated tests proving that HTTP URLs, lookalike domains, user-info URL tricks, unexpected ports, and redirects to unapproved hosts are rejected.
