T09 · Insecure Skill Coding Practices
- Location
scripts/revolut.py:1341- Finding
JavaScript Injection Through Unvalidated Portfolio Identifiers
- Content
View full analysis
Any: """Execute fetch() inside the page context to get data directly.""" headers_json = json.dumps(extra_headers) method_json = json.dumps(method) body_json = json.dumps(body) try: print(f"[revolut] Fetching via JS: {method} {url}", file=sys.stderr) result = page.evaluate( f""" async () => {{ try {{ const extra = {headers_json}; const method = {method_json}; const body = {body_json}; const headers = {{ 'Accept': 'application/json, text/plain, */*', ...extra }}; const opts = {{ method, headers, credentials: 'include' }}; if (body !== null && body !== undefined) {{ headers['Content-Type'] = 'application/json'; opts.body = JSON.stringify(body); }} const res = await fetch("{url}", opts); if (!res.ok) return {{ error: String(res.status) + " " + (res.statusText || "") }}; return await res.json(); }} catch (e) {{ return {{ error: e.toString() }}; }} }} """ ) ``` User-controlled portfolio identifiers reach this function without validation: ```python portfolio_id = getattr(args, "id", None) if portfolio_id: raw = _fetch_via_api_call( page, f"/api/retail/trading-acces ...[truncated 3128 chars]- Remediation
View remediation
{ const headers = { "Accept": "application/json, text/plain, */*", ...extra, }; const opts = { method, headers, credentials: "include", }; if (body !== null && body !== undefined) { headers["Content-Type"] = "application/json"; opts.body = JSON.stringify(body); } const res = await fetch(url, opts); if (!res.ok) { return { error: `${res.status} ${res.statusText || ""}`, }; } return await res.json(); }""", { "url": url, "extra": extra_headers, "method": method, "body": body, }, ) ``` 2. Validate `--id` and `--portfolio-id` against the exact identifier syntax used by Revolut. If they are UUIDs, enforce a full UUID match and reject all other values. 3. URL-encode identifiers with `urllib.parse.quote(identifier, safe="")` before placing them into path components. 4. Restrict `_fetch_via_api_call()` to relative `/api/retail/` paths and reject absolute URLs, protocol-relative URLs, control characters, quotes, backslashes, and unexpected origins. 5. Add security tests using identifiers containing quotes, backticks, newlines, `${...}`, closing parentheses, and script statements to ensure they are treated strictly as data. 6. Prefer Playwright's request APIs or a fixed endpoint mapping over dynamically generated in-page JavaScript wherever practical. ]]>
