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
•Technically reviewed by Anupam Mediratta•Published September 7, 2026•Reviewed 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

Prevention and further reading

Frequently Asked Questions

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.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #224

Related Articles

high

picomatch 2.3.1 ReDoS: Extglob Pattern Catastrophic Backtracking

picomatch versions below 2.3.2, 3.0.2, or 4.0.4 contain a Regular Expression Denial of Service vulnerability in extglob pattern parsing. An attacker can cause catastrophic backtracking with patterns containing nested alternations and quantifiers, freezing any Node.js process that evaluates untrusted glob expressions.

critical

pet-window.js Dynamic Code Evaluation: CWE-94 Hardening via Number

The pet-window module constructed dynamic JavaScript by embedding raw configuration values into code strings. An attacker with local access could inject arbitrary JavaScript by modifying stored configuration. The fix replaces string interpolation with explicit Number() coercion and NaN validation for all numeric configuration parameters.

high

sanitizeUnicodeInput(): Fullwidth U+ Bypasses Codepoint Validation

The `sanitizeUnicodeInput()` helper used by the project character-range settings screen rewrote `U+` prefixes to `0x` and called `parseInt()`, but never normalized its argument first. Compatibility-equivalent forms such as fullwidth `U+`, superscript digits, or mathematical alphanumerics never matched the `/U\+/gi` regex, fell through to the `else return inputString` branch, and were handed back to callers verbatim as "sanitized" values. The fix inserts a `String.prototype.normalize('NFKC')` pas

high

brace-expansion Stack Exhaustion: CVE-2026-102276 Patched

A critical stack exhaustion vulnerability in brace-expansion allows attackers to crash Node.js applications by supplying specially crafted brace patterns that trigger unbounded recursion. The fix upgrades the library across all maintained version lines to enforce depth limits on recursive expansion. This vulnerability affects any service that expands user-controlled brace patterns without input validation.

critical

BFF Proxy QR Code Endpoint Prototype Pollution via Unvalidated JSON

A critical prototype pollution vulnerability in a backend-for-frontend (BFF) proxy endpoint allowed attackers to inject malicious properties into the JavaScript Object prototype by crafting JSON requests with forbidden keys. This could compromise application behavior across all objects. The fix adds explicit key validation to reject payloads containing `__proto__`, `constructor`, or `prototype`.

high

smol-toml 1.7.0 DoS: Malformed TOML Documents Crash Parser

A denial-of-service vulnerability in smol-toml 1.7.0 allows attackers to crash the parser by supplying malformed TOML documents. The vulnerability affects any application that parses untrusted TOML input. The fix, available in smol-toml 1.7.1, hardens input validation and error recovery.