Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jso-protector-electron

Electron V8 bytecode adapter for jso-protector. Pair JSO obfuscation with Electron's bytecode runtime to ship .jsc artefacts that combine JSO protection with version-bound V8 cached data. Public bytecode tools and an attached debugger still matter; this is a cost-raising layer, not an unrecoverability guarantee.

Pipeline

src/main.js
   |   jso-protector (HTTP API: obfuscate + sign + watermark)
   v
dist-protected/main.js
   |   jso-protector-electron (V8 cachedData -> .jsc)
   v
dist-bytecode/main.js.jsc        <-- ship this in your .asar

Install

npm install --save-dev jso-protector \
  https://javascriptobfuscator.com/download/jso-protector-electron/jso-protector-electron-0.1.0.tgz

The adapter is a versioned direct download and is not published in the npm registry. Pin the URL (or your mirrored copy) in the lockfile.

electron is a peer dependency. You need Electron in your build environment for mode: embedded (the only mode that emits real bytecode).

Embedded mode (real bytecode)

Run under Electron's Node:

electron $(npm bin)/jso-protector-electron \
  --input  dist-protected \
  --output dist-bytecode \
  --mode   embedded

This walks dist-protected/, runs vm.Script(src).createCachedData() for each .js file, and emits dist-bytecode/<rel>.jsc containing a small JSON header followed by the V8 cachedData blob. The header records the source bytes, jsc bytes, and Electron version so an auditor can validate coverage:

[ 8 bytes magic "JSOJSC\x01\x00" ]
[ 4 bytes LE uint32 header length  ]
[ N bytes JSON: { filename, electronVersion, srcBytes, jscBytes }  ]
[ remaining bytes: V8 cachedData                                   ]

The output directory also contains bytecode.manifest.json listing every processed + skipped file. Hand it to jso-protector compliance pci-dss-v4 as additional evidence.

Scaffold-only mode (preview without Electron)

jso-protector-electron \
  --input  dist-protected \
  --output dist-bytecode \
  --mode   scaffold-only

Useful in CI environments where you can't run Electron (e.g. a lint stage that just wants to confirm coverage). Output is the original .js files plus per-file <file>.bytecode-meta.json recording { sourceSha256, sourceBytes, generatedAt }. The top-level bytecode.manifest.json carries mode: "scaffold-only" so a downstream verifier can refuse to ship until the real bytecode build has run.

Loader on the Electron side

In your Electron main process (or preload):

const fs = require("node:fs");
const vm = require("node:vm");

function loadJsc(jscPath) {
    const buf = fs.readFileSync(jscPath);
    // Skip the 8-byte magic + 4-byte length + JSON header.
    const headerLen = buf.readUInt32LE(8);
    const cachedData = buf.subarray(12 + headerLen);
    const filename = JSON.parse(buf.subarray(12, 12 + headerLen).toString("utf8")).filename;
    const script = new vm.Script("", { filename: filename, cachedData: cachedData });
    return script.runInThisContext();
}

Wrap that in app.whenReady().then(...) and you've replaced your ordinary require for protected modules.

Threat model

Layer Defends against
JSO obfuscation Casual reverse-engineering of the JS bundle
V8 bytecode Skilled JS reverse-engineering; LLM-assisted decompilation
Ed25519 signed manifest Post-build tampering (CDN swaps, supply-chain attacks)
Watermark Anti-piracy / leak attribution
Beacon + SIEM Detection / personnel alerts when any of the above fires

None of these defeat a debugger attached to the running Electron process. For that you need OS-level mitigations: code-signing, notarization (macOS), hardened runtime, and Windows AppContainer.

API (programmatic)

const electron = require("jso-protector-electron");

const result = await electron.compileDirectory({
    input:      "dist-protected",
    output:     "dist-bytecode",
    mode:       "auto",             // "auto" | "embedded" | "scaffold-only"
    extensions: [".js", ".cjs", ".mjs"],
});
// result: { mode, processed, skipped, manifestPath }

The auto mode picks embedded when running under Electron and scaffold-only otherwise. Always pass an explicit mode in CI so the build fails loudly if Electron is missing.

Verify

npm test

14 tests covering both modes, the CLI surface, the .jsc header layout, the directory walk, and the actionable-error path when embedded mode is invoked without Electron.

Releases

Packages

Contributors

Languages