Back to Blog
critical SEVERITY6 min read

How URL Parameter Injection Happens in JavaScript APIs and How to Fix It

A critical URL parameter injection vulnerability in the `getUserInfoByKeyword()` function allowed attackers to manipulate API requests by injecting special characters into the search keyword parameter. The fix applies `encodeURIComponent()` to properly encode the keyword before concatenating it into the URL, preventing parameter tampering and null byte injection attacks.

O
By Orbis AppSec
Published September 7, 2026Reviewed September 7, 2026

Answer Summary

This is a URL parameter injection vulnerability (CWE-95) in the Bilibili API client where the `getUserInfoByKeyword()` function directly concatenates user-supplied keywords into the API URL without URL encoding. Attackers could inject special characters like `&`, `%00`, or `=` to modify API request parameters or trigger parsing errors. The fix applies `encodeURIComponent()` to the keyword parameter, which properly escapes all special characters and prevents the injection.

Vulnerability at a Glance

cweCWE-95 (Improper Neutralization of Directives in Dynamically Evaluated Code), CWE-20 (Improper Input Validation)
fixApply `encodeURIComponent()` to encode the keyword parameter before URL concatenation
riskAttackers can manipulate API request parameters, bypass filters, inject null bytes, or trigger unexpected API behavior
languageJavaScript
root causeDirect string concatenation of untrusted user input into URL query parameters without encoding
vulnerabilityURL Parameter Injection / Improper Input Encoding

How URL Parameter Injection Happens in JavaScript APIs and How to Fix It

Introduction

The bilibili-api.js file handles search requests to the Bilibili video platform API, but a flaw in the getUserInfoByKeyword() function at line 215 created a critical security gap. The function was directly concatenating a user-supplied keyword parameter into the API URL without any URL encoding:

const response = await fetch(`${_BILIAPI.BILIBILI_API}/x/web-interface/wbi/search/type?search_type=bili_user&keyword=${keyword}`);

This seemingly innocent string concatenation meant that any special characters in the keyword would be interpreted as part of the URL structure itself, rather than as data. For developers working on similar API clients or search features, this is a critical reminder that user input should never be directly interpolated into URLs without encoding.

The Vulnerability Explained

The Problem: Direct String Concatenation

When you build a URL by directly concatenating user input, special characters in that input take on new meaning:

// VULNERABLE CODE (line 215 of bilibili-api.js)
const response = await fetch(`${_BILIAPI.BILIBILI_API}/x/web-interface/wbi/search/type?search_type=bili_user&keyword=${keyword}`);

The keyword variable comes from user input (via HTTP parameters, form submission, or API request). By concatenating it directly into the URL string, the code treats the string as literal URL syntax.

Attack Scenarios

Scenario 1: Parameter Injection

If an attacker supplies a keyword like test&limit=1, the resulting URL becomes:

https://api.bilibili.com/x/web-interface/wbi/search/type?search_type=bili_user&keyword=test&limit=1

Instead of searching for "test&limit=1", the API receives TWO parameters: keyword=test and limit=1. This could bypass rate limiting, pagination controls, or other filtering logic the API enforces.

Scenario 2: Null Byte Injection

A keyword like test%00admin (URL-encoded null byte) could inject a null terminator:

https://api.bilibili.com/x/web-interface/wbi/search/type?search_type=bili_user&keyword=test%00admin

Depending on how the backend parses URLs, this might truncate the keyword at the null byte, returning results for "test" instead of the intended search, or causing parsing errors.

Scenario 3: Fragment/Anchor Injection

A keyword containing # like test#anchor could inject a URL fragment:

https://api.bilibili.com/x/web-interface/wbi/search/type?search_type=bili_user&keyword=test#anchor

This could cause the API request to fail or be interpreted differently by caching layers, proxies, or the client itself.

Real-World Impact

For the Bilibili API client, an attacker could:
- Manipulate search results by injecting unexpected parameters
- Bypass pagination or filtering controls
- Cause API errors or unexpected behavior
- Potentially access unintended API endpoints or data
- Create denial-of-service conditions by injecting malicious parameters

The Fix

The pull request applied a single, critical change to line 215:

- const response = await fetch(`${_BILIAPI.BILIBILI_API}/x/web-interface/wbi/search/type?search_type=bili_user&keyword=${keyword}`);
+ const response = await fetch(`${_BILIAPI.BILIBILI_API}/x/web-interface/wbi/search/type?search_type=bili_user&keyword=${encodeURIComponent(keyword)}`);

What encodeURIComponent() Does

The built-in encodeURIComponent() function properly percent-encodes special characters according to RFC 3986:

Character Encoded As
& %26
= %3D
# %23
% %25
(space) %20
/ %2F
: %3A
? %3F

Now, if an attacker supplies test&limit=1, it becomes test%26limit%3D1:

https://api.bilibili.com/x/web-interface/wbi/search/type?search_type=bili_user&keyword=test%26limit%3D1

The Bilibili API receives a single parameter with the literal value test&limit=1—the & is part of the data, not a parameter separator.

How This Prevents the Attack

  • Parameter injection prevented: Special characters are encoded, so they can't create new parameters
  • Null bytes handled: The %00 is just data, no longer a terminator
  • Fragments blocked: # becomes %23, so it's treated as part of the keyword value
  • Encoding preserved: International characters and spaces are handled correctly

Prevention & Best Practices

To avoid URL parameter injection vulnerabilities in JavaScript API clients:

1. Always Use encodeURIComponent() for Query Parameters

// ✅ SECURE
const keyword = userInput;
const url = `https://api.example.com/search?q=${encodeURIComponent(keyword)}`;

// ✅ ALSO SECURE - Using URLSearchParams
const params = new URLSearchParams();
params.append('q', keyword);
const url = `https://api.example.com/search?${params.toString()}`;

2. Use the URL Constructor for Complex URLs

// ✅ SECURE - Most readable and maintainable
const url = new URL('https://api.example.com/search');
url.searchParams.set('keyword', userInput);
url.searchParams.set('type', 'user');
fetch(url.toString());

3. Never Trust Framework Defaults

Even modern frameworks may not automatically encode query parameters—explicitly verify:

// Check your framework's documentation
// In Next.js, for example:
// ✅ SECURE
fetch(new URL('/api/search', process.env.API_BASE).toString() + '?' + 
  new URLSearchParams({keyword: userInput}).toString());

4. Use Static Analysis to Catch This

  • Semgrep rule: Set up rules to detect string concatenation in fetch/request calls
  • ESLint plugins: Use eslint-plugin-security to flag direct template literals in URLs
  • SAST tools: Integrate tools like CodeQL or Snyk into your CI/CD pipeline

5. Apply Defense in Depth

While encoding is essential, also:
- Validate input type (ensure keyword is a string, has reasonable length)
- Use Content Security Policy headers
- Monitor API request patterns for anomalies
- Rate-limit endpoints to mitigate injection attempts

Key Takeaways

  • String concatenation in URL query parameters is dangerous: The getUserInfoByKeyword() function teaches us that even simple API client code can introduce security vulnerabilities when handling user input.

  • encodeURIComponent() is non-negotiable for dynamic URLs in JavaScript: This single function call prevents parameter injection, null byte injection, and fragment attacks—but it must be applied consistently.

  • URLSearchParams is the modern, safe alternative: Rather than manually encoding, using new URL() with .searchParams eliminates the class of vulnerability entirely.

  • This pattern appears throughout codebases: If you have one instance of direct URL concatenation without encoding, you likely have others—conduct a codebase-wide audit for fetch(), axios(), or other HTTP calls with dynamic parameters.

  • Static analysis tools can catch this at development time: Semgrep rules and ESLint security plugins can detect this pattern before code reaches production, preventing exploitation.

How Orbis AppSec Detected This

Source: The keyword parameter is sourced from user input (HTTP request parameters, form data, or API arguments passed to getUserInfoByKeyword()).

Sink: The dangerous sink is the template literal string interpolation directly into the fetch URL at line 215: fetch(`...&keyword=${keyword}`).

Missing Control: The code lacked URL encoding/normalization. No encodeURIComponent(), URLSearchParams, or URL constructor was used to sanitize the dynamic parameter.

CWE: CWE-95 (Improper Neutralization of Directives in Dynamically Evaluated Code) and CWE-20 (Improper Input Validation).

Fix: Wrap the keyword parameter with encodeURIComponent() to percent-encode all special characters before URL concatenation.

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

URL parameter injection is a subtle but critical vulnerability that developers often overlook when building API clients. The fix in bilibili-api.js demonstrates that a single line change—adding encodeURIComponent(keyword)—is all that's needed to eliminate the risk. However, the best defense is prevention through consistent practices: always use URLSearchParams, the URL constructor, or explicit encoding functions when building URLs with dynamic data. Audit your codebase for similar patterns, enable static analysis tools in your CI/CD pipeline, and remember that user input should never be trusted in URL construction. Secure coding practices like these are essential for building resilient API clients and preventing attackers from manipulating your application's interactions with third-party services.

References

Frequently Asked Questions

What is URL parameter injection?

It's a vulnerability where user-controlled input is directly concatenated into a URL query string without proper encoding, allowing attackers to inject special characters that modify the API's behavior or parameters.

How do you prevent URL parameter injection in JavaScript?

Always use `encodeURIComponent()` to encode dynamic query parameters, use URL constructor with URLSearchParams for proper parsing, or use template literal placeholders with proper escaping mechanisms.

What CWE covers URL parameter injection?

CWE-95 (Improper Neutralization of Directives in Dynamically Evaluated Code) and CWE-20 (Improper Input Validation) both apply to this class of vulnerability.

Is input validation enough to prevent URL parameter injection?

No—while validation helps, proper encoding is essential. Validation alone cannot prevent all special characters from having special meaning in URLs; encoding is the defense-in-depth solution.

Can static analysis detect URL parameter injection?

Yes—tools like Semgrep, ESLint with security plugins, and SAST scanners can detect string concatenation patterns in URL construction and flag missing `encodeURIComponent()` calls.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #224

Related Articles

high

How JavaScript Injection via String Interpolation Happens in Go Wails Applications and How to Fix It

A high-severity JavaScript injection vulnerability in `internal/clusterconfigs/input.go` allowed arbitrary code execution through malicious kubeconfig filenames. The `saveClusterConfigFile` function at line 20 constructed JavaScript code by directly interpolating unsanitized filenames into `window.ExecJS()` calls, enabling attackers to break out of string literals and execute arbitrary JavaScript in the Webview context.

high

How Denial of Service via Prototype Pollution happens in Axios and how to fix it

Axios versions prior to 1.15.1 merged untrusted configuration objects without guarding against the `__proto__` key, letting attacker-controlled input pollute `Object.prototype` and crash or destabilize applications. Upgrading axios (and its transitive dependencies `form-data`, `follow-redirects`, `proxy-from-env`) closes this Denial of Service and prototype-pollution attack surface without changing any application code.

critical

How Server-Side Request Forgery happens in Node.js and how to fix it

The order-flow service in a Node.js e-commerce backend built an outbound fetch() URL by directly concatenating a configurable `sendingOrder.url` value with a query string, with no validation of protocol or destination. This allowed order data—including customer and payment-adjacent information—to be silently redirected to an attacker-controlled endpoint simply by changing a config value or environment variable.

high

How Infinite Loop Denial of Service Happens in nanoid and How to Fix It

CVE-2026-67213 is a high-severity infinite loop vulnerability in nanoid's `customAlphabet` function that could cause Denial of Service through CPU exhaustion. The fix upgrades nanoid from 3.3.12 to patched versions 3.3.18 and 5.1.6, eliminating the loop condition that trapped ID generation when processing certain input patterns.

critical

How Message Corruption via Protocol Length Header Abuse Happens in WebSocket Implementations and How to Fix It

CVE-2026-54466 is a critical vulnerability in websocket-driver 0.7.4 that allows attackers to corrupt WebSocket messages by abusing protocol length headers. The fix upgrades the package to version 0.7.5, which implements proper validation of untrusted length header inputs. This vulnerability could allow attackers to modify or inject data into real-time communication channels used by frontend applications.

critical

How XML Entity Expansion happens in Node.js and how to fix it

A critical XML External Entity (XXE) vulnerability in `lib/xml2json.js` allowed attackers to trigger exponential memory consumption through nested entity expansion. The fix adds `strictEntities: true` to both SAX parser instances, disabling dangerous entity processing that could crash servers processing untrusted XML.