critical · 9.6Jul 14, 2026

TidGi Desktop Remote Code Execution via Malicious TiddlyWiki Repository Import

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Importing a crafted TiddlyWiki Git repository into TidGi Desktop automatically executes attacker-supplied JavaScript with full Node.js access, giving an attacker complete control over the victim's…

Packagetidgi
Ecosystemnpm
Affected<= 0.13.0

The problem

TidGi Desktop through 0.13.0 boots every imported wiki by calling $tw.boot.startup(), which loads all .tid files from the wiki's tiddlers/ directory into the wiki store unconditionally.

Any tiddler carrying type: application/javascript and module-type: startup is then registered as an executable module via defineTiddlerModules() and its exports.startup() function is called automatically during the boot sequence. The Wiki Worker process runs full Node.js, so the attacker's code has access to require('child_process'), require('fs'), and everything else.

No authentication, no prompt, and no sandbox stand between the import action and code execution.

Proof of concept

A working proof-of-concept for this issue in tidgi, with the exact payload below.

text
title: $:/plugins/poc/startup.js
type: application/javascript
module-type: startup

exports.startup = function() {
  require('child_process').execSync('touch /tmp/TidGi-RCE-PoC.txt');
  console.log('STARTUP_EXECUTED');
};

The root cause is the absence of any trust boundary between user-supplied tiddler files and TiddlyWiki's module system. loadWikiTiddlers reads every .tid file from disk and passes it straight to wiki.addTiddlers(). Immediately after, defineTiddlerModules() iterates the full wiki store: any tiddler with module-type: startup and type: application/javascript is handed to $tw.modules.define() and scheduled for execution.

The platform guard in doesTaskMatchPlatform() returns true by default when no platforms field is set, so the startup function runs unconditionally.

The advisory recommends stripping module-type from non-system tiddlers (titles not starting with $:/), sandboxing user module execution with vm.runInNewContext, or allowlisting safe module types such as widget, macro, filter, and parser. Any of these mitigations breaks the exploit chain.

CWE-94 (Improper Control of Code Generation) applies directly: attacker-controlled text is promoted to executable code with no sanitization or isolation step.

The fix

No patched release was publicly available at the time of writing. The advisory recommends: (1) filtering out module-type fields on tiddlers whose titles do not start with $:/, so user-repository tiddlers can never register executable modules; (2) running user modules inside vm.runInNewContext with a restricted sandbox; or (3) allowlisting safe module-type values (widget, macro, filter, parser) and rejecting startup, library, saver, etc. for user tiddlers.

Monitor the TidGi-Desktop releases page at https://github.com/tiddly-gittly/TidGi-Desktop/releases for a patched build. Until a fix ships, do not import wiki repositories from untrusted sources.

Reported by nuii.

References: [1][2]

Related research