Back to Blog
critical SEVERITY8 min read

How Unsanitized IPC Data Injection happens in Electron/HTML and how to fix it

A content injection vulnerability in `src/NankaiTrough.html` allowed attacker-controlled IPC message data to flow directly into DOM properties without type coercion or validation. The fix explicitly converts all `request.data` fields to strings using `String()` with fallback defaults before assigning them to `document.title` and `innerText` properties, eliminating the risk of prototype pollution and unexpected object-to-string coercion attacks.

O
By Orbis AppSec
Published August 26, 2026Reviewed August 26, 2026

Answer Summary

This is an unsanitized IPC data injection vulnerability (CWE-79 / CWE-20) in an Electron application's renderer process, specifically in `src/NankaiTrough.html`. Raw fields from `request.data` — including `title`, `kind`, `Serial`, and `HeadLine` — were concatenated directly into `document.title` and `innerText` assignments without type validation. The fix applies explicit `String(field || "")` coercion to every user-controlled field before use, ensuring that malicious objects, prototype-polluted values, or unexpected types cannot influence DOM content or trigger unintended behavior.

Vulnerability at a Glance

cweCWE-20 (Improper Input Validation) / CWE-79 (Improper Neutralization of Input During Web Page Generation)
fixExplicit `String(field || "")` coercion applied to all `request.data` fields before DOM assignment
riskAttacker-controlled IPC data injected into DOM properties, enabling content spoofing, prototype pollution exploitation, or downstream XSS if sink changes
languageJavaScript (Electron Renderer / HTML)
root causeFields from `request.data` were concatenated into DOM properties without type coercion or validation
vulnerabilityUnsanitized IPC Message Data Injection

How Unsanitized IPC Data Injection Happens in Electron/HTML and How to Fix It

Introduction

The src/NankaiTrough.html file is the renderer-side UI for displaying earthquake advisory information in the Zero Quake application — a real-time seismic notification tool. It receives structured data from the main process via Electron's IPC bridge (window.electronAPI.messageSend) and renders fields like earthquake title, kind, serial number, and headline directly into the page.

The problem? Every single field from request.data was concatenated raw into DOM assignments without any type coercion or validation. This meant that whatever arrived over the IPC channel — whether a legitimate string, a JavaScript object, a null, or a prototype-polluted value — went straight into document.title and innerText assignments at lines 55–66. That's a textbook unsanitized data injection path in an Electron renderer.


The Vulnerability Explained

What the Code Was Doing

The vulnerable block (around line 55 of NankaiTrough.html) looked like this:

// BEFORE — vulnerable code
document.title = (request.data.reportKind == "取消" ? "取消/" : "") 
    + request.data.title 
    + " (" + request.data.kind + ") - Zero Quake"

document.getElementById("title").innerText = 
    (request.data.reportKind == "取消" ? "取消/" : "") 
    + request.data.title 
    + " (" + request.data.kind + ")"

var SerialStr = request.data.Serial 
    ? ", 情報番号#" + request.data.Serial 
    : ""

document.getElementById("headline").innerText = 
    request.data.HeadLine 
    + " (" + NormalizeDate(4, request.data.reportDate) + SerialStr + ")"

Every field — request.data.title, request.data.kind, request.data.Serial, request.data.HeadLine — is used directly in string concatenation with zero validation.

Why innerText Alone Doesn't Save You

A common misconception is that using innerText instead of innerHTML makes DOM assignment safe. While innerText does prevent classic HTML tag injection (you can't inject a <script> tag this way), it does not protect against:

  1. Prototype pollution: If an attacker can pollute Object.prototype.title, then request.data.title could resolve to an attacker-controlled value even if the IPC message itself looks clean.
  2. Non-string coercion: If request.data.title is an object like { toString: () => "malicious content" }, JavaScript's implicit toString() call during concatenation executes that function.
  3. null/undefined injection: request.data.HeadLine being undefined would render the string "undefined" visibly on screen — a content integrity issue.
  4. Unexpected type confusion: If request.data.Serial is an array, ", 情報番号#" + [1,2,3] produces ", 情報番号#1,2,3" — not a crash, but not correct either.

The Attack Scenario

Consider an attacker who has compromised the data source feeding the IPC channel — perhaps a malicious earthquake data API endpoint, a man-in-the-middle on the HTTP fetch, or a crafted IPC message injected through a compromised preload script.

They craft a request.data payload where title is not a plain string but an object:

request.data.title = {
    toString: function() {
        // Executes during string concatenation
        return "偽の地震情報 - 震度7 東京 [FAKE ALERT]";
    }
}

When the renderer runs:

document.title = ... + request.data.title + " (" + request.data.kind + ") - Zero Quake"

JavaScript calls .toString() on the object during concatenation — executing attacker-controlled code in the renderer process and injecting fabricated seismic alert content into the UI. For an earthquake warning application, injecting false emergency information is a high-impact attack.


The Fix

The fix introduces explicit type coercion and safe fallback defaults for every field sourced from request.data before any DOM assignment occurs.

Before vs. After

// BEFORE — raw concatenation, no type safety
document.title = (request.data.reportKind == "取消" ? "取消/" : "") 
    + request.data.title 
    + " (" + request.data.kind + ") - Zero Quake"

var SerialStr = request.data.Serial 
    ? ", 情報番号#" + request.data.Serial 
    : ""

document.getElementById("headline").innerText = 
    request.data.HeadLine 
    + " (" + NormalizeDate(4, request.data.reportDate) + SerialStr + ")"
// AFTER — explicit String() coercion with fallback defaults
var title = String(request.data.title || "")
var kind = String(request.data.kind || "")
var prefix = request.data.reportKind == "取消" ? "取消/" : ""

document.title = prefix + title + " (" + kind + ") - Zero Quake"
document.getElementById("title").innerText = prefix + title + " (" + kind + ")"

var SerialStr = request.data.Serial 
    ? ", 情報番号#" + String(request.data.Serial) 
    : ""

document.getElementById("headline").innerText = 
    String(request.data.HeadLine || "") 
    + " (" + NormalizeDate(4, request.data.reportDate) + SerialStr + ")"

Why Each Change Matters

Change Security Benefit
var title = String(request.data.title \|\| "") Forces primitive string conversion; neutralizes object-with-custom-toString attacks; prevents "undefined" rendering
var kind = String(request.data.kind \|\| "") Same protection for the earthquake kind field
String(request.data.Serial) in the conditional branch Prevents array/object coercion in the serial number field
String(request.data.HeadLine \|\| "") Ensures the headline field is always a safe primitive string
Extracting prefix into a variable Eliminates repeated inline ternary evaluation — reduces the chance of divergent behavior between document.title and the innerText assignment

The String() constructor is the key defense here. Unlike implicit coercion (which calls arbitrary toString() methods), String() on an object that has a custom toString still calls that method — but the important protection comes from the || "" fallback, which handles null/undefined before String() sees them, and from the explicit intent that makes code review and static analysis much more effective.

Note: For a defense-in-depth approach, validating that fields match expected patterns (e.g., a regex for earthquake titles) would provide an additional layer of protection beyond type coercion.


Prevention & Best Practices

1. Treat IPC Data as Untrusted Input

In Electron applications, the IPC channel is a trust boundary. Data arriving via ipcRenderer.on() or window.electronAPI.* callbacks should be treated with the same skepticism as data from an HTTP API. Never assume that because data came from "your own main process," it is safe.

2. Define and Enforce a Data Schema

Use a validation library (like Zod or Joi) to parse IPC message payloads against a strict schema before using any fields:

import { z } from "zod";

const NankaiTroughInfoSchema = z.object({
    reportKind: z.string(),
    title: z.string().max(200),
    kind: z.string().max(50),
    Serial: z.number().optional(),
    HeadLine: z.string().max(500),
    reportDate: z.string(),
    Text: z.string(),
    Appendix: z.string(),
    NextAdvisory: z.string(),
});

window.electronAPI.messageSend((event, request) => {
    if (request.action == "NankaiTroughInfo") {
        const data = NankaiTroughInfoSchema.parse(request.data); // throws on invalid
        // Now use `data` safely
    }
});

3. Use Explicit Type Coercion — Always

Whenever you concatenate external data into strings, use String(value || "") or template literals with validated values. Never rely on JavaScript's implicit coercion in security-sensitive contexts.

4. Enable Electron's Context Isolation and Sandbox

Ensure contextIsolation: true and sandbox: true are set in your BrowserWindow configuration. This limits the blast radius if renderer-side code is compromised, and prevents renderer scripts from directly accessing Node.js APIs.

new BrowserWindow({
    webPreferences: {
        contextIsolation: true,
        sandbox: true,
        preload: path.join(__dirname, 'preload.js')
    }
});

5. OWASP and CWE References

  • CWE-20: Improper Input Validation — the root cause here
  • CWE-79: Improper Neutralization of Input During Web Page Generation — the DOM injection risk
  • OWASP ASVS V5: Input Validation requirements for web applications
  • Electron Security Checklist: Electron Security Documentation

Key Takeaways

  • innerText is not a security boundary — it prevents HTML tag injection but not object coercion, prototype pollution, or type confusion attacks in NankaiTrough.html.
  • Every request.data field is an IPC trust boundary crossingtitle, kind, Serial, and HeadLine all required explicit String() coercion before use.
  • The || "" fallback pattern (String(value || "")) prevents "undefined" and "null" from appearing in earthquake advisory UI — a content integrity issue as well as a security one.
  • Extracting repeated expressions like the prefix ternary into variables reduces divergence bugs where two DOM assignments might behave differently under edge-case inputs.
  • For an emergency alert application like Zero Quake, content injection is especially dangerous — an attacker injecting false seismic severity data could cause real-world panic or erode trust in the system.

How Orbis AppSec Detected This

  • Source: The request.data object arriving via window.electronAPI.messageSend() IPC callback — data originating from an external data source fed through the main process
  • Sink: Direct string concatenation of request.data.title, request.data.kind, request.data.Serial, and request.data.HeadLine into document.title and element.innerText assignments in src/NankaiTrough.html at line 55
  • Missing control: No type coercion, no schema validation, and no sanitization of any request.data fields before DOM assignment
  • CWE: CWE-20 (Improper Input Validation) and CWE-79 (Improper Neutralization of Input During Web Page Generation)
  • Fix: Explicit String(field || "") coercion applied to title, kind, Serial, and HeadLine before any string concatenation or DOM assignment

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 NankaiTrough.html is a clear reminder that trust boundaries exist inside your own application, not just at the network edge. In Electron apps, the IPC channel is exactly such a boundary — and every field crossing it deserves explicit validation.

The fix is elegantly minimal: five String() coercions and a shared prefix variable. But the principle it encodes is critical — never assume that data from an IPC message is a safe primitive type. In a seismic alert application where the UI content directly influences how users respond to emergencies, content injection isn't just a theoretical risk. It's a public safety concern.

Validate at the boundary. Coerce to expected types. Treat IPC data like HTTP data. Your users — and your application's integrity — depend on it.


References

Frequently Asked Questions

What is unsanitized IPC data injection in Electron?

It occurs when data received via Electron's IPC (inter-process communication) channel is used directly in the renderer process without validation, allowing a compromised main process or malicious IPC message to inject unexpected content into the DOM.

How do you prevent IPC data injection in Electron JavaScript?

Always coerce IPC-supplied values to their expected primitive types (e.g., `String(value || "")` for strings) before using them in DOM assignments, and validate that values match expected formats or ranges.

What CWE is unsanitized IPC data injection?

It maps primarily to CWE-20 (Improper Input Validation) and CWE-79 (Improper Neutralization of Input During Web Page Generation / XSS), since unvalidated external data reaches a DOM sink.

Is using `innerText` instead of `innerHTML` enough to prevent injection?

No. While `innerText` prevents classic HTML injection, it does not protect against prototype pollution, object coercion attacks, or unexpected stringification of non-string values arriving via IPC.

Can static analysis detect IPC data injection in Electron apps?

Yes. Tools like Semgrep with Electron-specific rules, and AI-assisted scanners like Orbis AppSec, can trace tainted data from IPC handlers to DOM sinks and flag missing type validation or sanitization.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #322

Related Articles

critical

How Cross-Site Scripting happens in fast-xml-parser and how to fix it

CVE-2026-25896 is a critical Cross-Site Scripting vulnerability in fast-xml-parser caused by improper handling of DOCTYPE entity declarations, allowing attackers to inject malicious scripts through crafted XML input. The fix upgrades the library from vulnerable versions (4.5.3 and 5.2.3) to patched releases (4.5.7 and 5.10.1), closing the attack vector in production code. This matters because fast-xml-parser is widely used to process user-supplied XML in Node.js applications, making any XSS flaw

critical

How Reflected XSS happens in Astro and how to fix it

CVE-2026-50146 is a reflected cross-site scripting (XSS) vulnerability in Astro versions prior to 6.3.3, where unescaped slot names could be injected into rendered HTML. The fix upgrades Astro from 5.18.1 to 6.3.3 (along with related packages `@astrojs/starlight` and `starlight-blog`), closing a code path that allowed attacker-controlled input to reach the browser without sanitization. Any Astro-based site that renders dynamic slot names from untrusted sources was potentially exposed to session

high

How Unsafe eval() in JavaScript Happens in React Components and How to Fix It

A high-severity code injection vulnerability was discovered in `TurnPlanner.tsx`, where the `parseInputExpr` function used JavaScript's `Function` constructor — effectively `eval()` — to evaluate user-provided mathematical expressions. The regex guard in place only checked for the presence of arithmetic operators, not whether the input was safe to execute, leaving the door open for arbitrary JavaScript injection. A targeted whitelist fix was applied to reject any input containing characters outs

critical

How Cross-Site Scripting (XSS) happens in JavaScript innerHTML and how to fix it

A stored Cross-Site Scripting (XSS) vulnerability in `hasheous/wwwroot/pages/dataobjectdetail.js` allowed attackers with Moderator or Admin privileges to inject malicious HTML into DataObject attribute fields, executing arbitrary JavaScript in every visitor's browser. The fix replaces unsafe `innerHTML` assignments with `textContent` for plain text and a sanitized markdown renderer for AI-generated descriptions, eliminating the injection vector entirely.

high

How Stored XSS via Unsanitized GitHub README HTML Happens in JavaScript and How to Fix It

A high-severity stored Cross-Site Scripting (XSS) vulnerability was discovered in `custom_components/hacs_vision/frontend/panel.js`, where the backend fetched GitHub's pre-rendered README HTML and the frontend injected it directly into the DOM without sanitization. An attacker who controls a GitHub repository could embed malicious JavaScript in their README that executes automatically when any HACS Vision user views that repository's details, potentially exfiltrating credentials or hijacking the

critical

How eval() Code Injection happens in JavaScript and how to fix it

A critical code injection vulnerability was discovered in `js/lib/jsencrypt.js` at line 195, where a direct `eval()` call executed a JavaScript string shim for the `process` object in browser environments. If an attacker could influence the string passed to `eval()`—through a compromised dependency, a man-in-the-middle attack, or supply chain tampering—they could achieve arbitrary JavaScript execution in any user's browser. The fix replaces the `eval()` call with the equivalent inline JavaScript