Introduction
The js/config.js file in this application handles dynamic configuration loading, fetching remote info.json files from GitHub raw content URLs to check for updates. However, a critical flaw at line 49 created a dangerous security gap: the code only verified that the HTTP response returned successfully (res.ok), but never validated that the returned data was actually a legitimate configuration object.
This oversight meant that an attacker who could intercept network traffic—through DNS hijacking, ARP spoofing, or compromising an upstream server—could inject arbitrary JSON payloads that the application would blindly trust and process. The remoteInfo variable would accept any valid JSON, including arrays, primitives, or objects with malicious properties that could corrupt application state or trigger unintended behavior.
The Vulnerability Explained
Let's examine the vulnerable code path:
const remoteInfo = await fetch(
'https://raw.githubusercontent.com/...'
).then(res => {
if (!res.ok) throw new Error('获取远程info.json失败');
return res.json();
});
const remoteTime = String(remoteInfo.lastEditTime || '');
const localTime = String(lib.extensionPack['活动武将'].lastEditTime || '');
The code fetches a remote JSON file and immediately accesses properties like lastEditTime without verifying that remoteInfo is actually a valid configuration object. Here's what's missing:
- No type validation: The response could be an array
[], a string"malicious", a number42, ornull - No schema validation: Even if it's an object, it might contain unexpected properties
- No integrity verification: There's no cryptographic signature or hash to verify the content hasn't been tampered with
Attack Scenario
Imagine an attacker performing DNS spoofing on a corporate network:
- The attacker intercepts DNS queries for
raw.githubusercontent.com - Requests are redirected to an attacker-controlled server
- The malicious server responds with HTTP 200 and a crafted payload:
["__proto__", {"polluted": "true"}]
Or even more dangerously:
{
"lastEditTime": "2099-12-31",
"constructor": {"prototype": {"admin": true}}
}
Since the application only checks res.ok, this malicious payload passes through. Depending on how the configuration is processed downstream, this could lead to:
- Prototype pollution attacks if object properties are merged unsafely
- Application state corruption causing crashes or undefined behavior
- Logic bypass if configuration values control security-sensitive features
- Denial of service if the application expects specific data types
The Fix
The fix adds a single but crucial validation line at js/config.js:53:
Before (Vulnerable)
const remoteInfo = await fetch(
'https://raw.githubusercontent.com/...'
).then(res => {
if (!res.ok) throw new Error('获取远程info.json失败');
return res.json();
});
const remoteTime = String(remoteInfo.lastEditTime || '');
After (Secure)
const remoteInfo = await fetch(
'https://raw.githubusercontent.com/...'
).then(res => {
if (!res.ok) throw new Error('获取远程info.json失败');
return res.json();
});
if (typeof remoteInfo !== 'object' || remoteInfo === null || Array.isArray(remoteInfo))
throw new Error('远程info.json数据无效');
const remoteTime = String(remoteInfo.lastEditTime || '');
This validation ensures:
| Check | Purpose |
|---|---|
typeof remoteInfo !== 'object' |
Rejects primitives (strings, numbers, booleans) |
remoteInfo === null |
Rejects null (which has typeof "object" in JavaScript) |
Array.isArray(remoteInfo) |
Rejects arrays (which are also objects in JavaScript) |
If any of these conditions are true, the application throws an error with the message "远程info.json数据无效" (Remote info.json data invalid), preventing malicious payloads from being processed.
Prevention & Best Practices
1. Always Validate External Data Structure
// Good: Validate before use
const data = await response.json();
if (!isValidConfigObject(data)) {
throw new Error('Invalid configuration format');
}
function isValidConfigObject(obj) {
return (
typeof obj === 'object' &&
obj !== null &&
!Array.isArray(obj) &&
typeof obj.lastEditTime === 'string'
);
}
2. Consider Schema Validation Libraries
For complex configurations, use libraries like Zod, Yup, or JSON Schema:
import { z } from 'zod';
const ConfigSchema = z.object({
lastEditTime: z.string(),
version: z.string().optional(),
});
const remoteInfo = ConfigSchema.parse(await response.json());
3. Implement Integrity Verification
For sensitive configurations, consider:
- Subresource Integrity (SRI): Hash-based verification
- Digital signatures: Sign configuration files and verify signatures
- Certificate pinning: Prevent MITM attacks at the TLS level
4. Use Content Security Policy
Restrict which domains can be fetched:
// In your CSP headers
connect-src 'self' https://raw.githubusercontent.com;
Key Takeaways
- Never trust
res.okalone: HTTP status codes don't guarantee content integrity - The
typeofoperator returns "object" for bothnulland arrays: Always check explicitly with=== nullandArray.isArray() - Remote configuration endpoints are high-value targets: They can affect application behavior without modifying code
- The
remoteInfo.lastEditTimeaccess assumed valid object structure: Defensive programming requires validating assumptions - One line of validation prevented a critical attack vector: Simple input validation is often the most effective defense
How Orbis AppSec Detected This
- Source: Remote JSON data fetched from
https://raw.githubusercontent.com/...viafetch()API - Sink: Direct property access on
remoteInfo.lastEditTimeatjs/config.js:53without type validation - Missing control: No validation that the parsed JSON response was a valid configuration object (non-null, non-array object type)
- CWE: CWE-20 (Improper Input Validation)
- Fix: Added type checking (
typeof remoteInfo !== 'object' || remoteInfo === null || Array.isArray(remoteInfo)) to reject malformed responses before processing
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 vulnerability demonstrates a common but dangerous pattern: trusting that external data will conform to expected formats. The fix—a single validation line—shows that effective security doesn't always require complex solutions. By validating that remoteInfo is a proper object before accessing its properties, the application now rejects malicious payloads that could have been injected through network-level attacks.
When working with remote data sources, always validate the structure and type of received data before processing. This is especially critical for configuration data that can influence application behavior. Remember: in JavaScript, typeof null === 'object' and arrays are objects too—explicit checks are essential.