Affected Versions
| Affected | unknown (first-party code) |
| Fixed in | unknown (see PR commit) |
| Ecosystem | not applicable (first-party code) |
| CVE / GHSA | not assigned |
| CWE | CWE-601: URL Redirection to Untrusted Site |
The Vulnerability Explained
The withProxy() utility wraps native fetch() calls to route traffic through configurable proxies. When fetching random background images, it constructed options like this:
const fetchOptions = withProxy(
{ method: 'GET', signal: AbortSignal.timeout(5000) },
pluginConfig,
pluginConfig.proxy?.randomBackground,
// ...
);
The problem: fetch() defaults to redirect: 'follow', meaning any 301/302/307 response is automatically followed without giving the application code a chance to inspect the destination. The existing validation checked that the original URL used HTTPS and matched the expected hostname—but this happens before the redirect, not after.
Here's the attack chain:
1. Application calls withProxy() to fetch from https://api.tomys.top/random-bg
2. Compromised API returns 302 Found with Location: https://attacker-phishing.com/malware
3. fetch() automatically follows to the attacker's site
4. The response returns to the application, which has no visibility into the redirect chain
The HTTPS and hostname checks provided false confidence. They validated the initial request, not where the client actually ended up.
The Fix
The change adds explicit redirect control to disable automatic following:
const fetchOptions = withProxy(
{ method: 'GET', signal: AbortSignal.timeout(5000), redirect: 'manual' },
pluginConfig,
pluginConfig.proxy?.randomBackground,
// ...
);
With redirect: 'manual', fetch() returns the 3xx response instead of following it. The application can now:
- Inspect response.headers.get('location')
- Validate the destination against an allowlist
- Reject suspicious redirects before any subsequent request
This shifts security control from the external API's integrity back to the application. The signal: AbortSignal.timeout(5000) timeout remains, protecting against slow responses, but now the redirect chain is equally protected.
Key Takeaways
-
redirect: 'follow'(the default) is dangerous for external API calls: Never assume HTTPS on the original URL guarantees HTTPS on every hop in a redirect chain. -
Proxy wrappers need redirect awareness: The
withProxy()abstraction obscuredfetch()behavior; security-critical options must be explicit, not inherited from defaults. -
Hostname validation must cover final destinations: Checking
api.tomys.topon request start missesattacker.comon response completion. -
Manual redirects enable defense in depth: Even if
api.tomys.topis later compromised, the application can reject unexpected destinations rather than blindly following them.
How Orbis AppSec Detected This
Source: The pluginConfig.proxy?.randomBackground configuration value controlling proxy routing for background image fetches.
Sink: The withProxy() utility's internal fetch() call, invoked with default redirect: 'follow' behavior.
Missing control: No validation of response.url after automatic redirect following; the application had no mechanism to inspect or reject redirect destinations from api.tomys.top.
CWE: CWE-601 — URL Redirection to Untrusted Site
Fix: Added redirect: 'manual' to fetch options, converting automatic redirects into inspectable responses that require explicit application-level validation before following.
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
This fix demonstrates how default fetch() behavior can undermine application security when calling external APIs. The withProxy() utility's partial HTTPS and hostname validation created a dangerous blind spot: it checked the front door but ignored where the redirect chain led. By switching to redirect: 'manual', the code regains control over the entire request lifecycle—essential when any external domain, even trusted ones like api.tomys.top, might return redirect responses.