Native desktop Markdown viewer built with Tauri (Rust backend + modular frontend).
- Node.js 20+
- Rust (stable)
- macOS (primary target)
npm install
npm run devnpm install
npm run build- Flexible file loading: file dialog, drag-and-drop into app, and Finder open-with flow
- Multi-document reading workflow: sidebar navigation, reordering, and document management
- Fast rendering with code highlighting and automatic refresh on external file saves
- Reader controls: theme switching and zoom shortcuts
- Safe link behavior: in-document anchor navigation and controlled external link opening
src/main.js: orchestration only (wires controllers + app lifecycle)src/modules/: focused frontend modules by concernsrc-tauri/src/commands.rs: file IO, validation, watcher sync, Tauri commandssrc-tauri/src/parser.rs: markdown parsing + syntax highlighting + heading ID generationtests/: pure-logic unit tests for frontend behavior and ordering logic
npm install
npm run check
npm run audit:depsnpm run check runs JS tests + Rust tests + Rust lints.
This app is designed for trusted local markdown files. No HTML sanitization is applied — the Content Security Policy (CSP) is the primary security boundary.
The user controls which .md files are opened. Markdown content is rendered directly in the webview, including any inline HTML. If you open a malicious file from an untrusted source, the mitigations below limit the damage but do not eliminate all risk.
| Vector | Risk | Mitigation |
|---|---|---|
<script> in markdown |
JS execution in webview | Blocked — CSP script-src 'self' prevents all inline and injected scripts |
<style> in markdown |
UI spoofing / phishing via CSS | Possible — CSP style-src 'unsafe-inline' is required for syntax-highlighted code; a crafted <style> tag could restyle the app UI |
<img src="https://..."> |
Tracking pixel / IP leak | Possible — CSP img-src allows https: images so remote images load and reveal the user's IP to the server |
<iframe> |
Embedding external content | Blocked — CSP frame-src 'none' |
<form action="..."> |
Data exfiltration via form POST | Blocked — CSP form-action 'none' |
<object> / <embed> |
Plugin-based exploits | Blocked — CSP object-src 'none' |
javascript: / data: links |
Script execution via click | Blocked — frontend scheme filter only allows http, https, mailto, tel |
| External link opens malware URL | Social engineering | Mitigated — links open in the system browser (via Tauri opener plugin default permission set), not inside the app |
| Very large file | Memory exhaustion / hang | Mitigated — 8 MiB per-file limit, 128 MiB per-batch limit, 256 KB cap on syntax highlighting tokenization |
| Path traversal / symlink tricks | Read arbitrary files | Mitigated — paths are canonicalized and validated against a markdown extension allowlist (.md, .markdown, .mdown, .mkd) |
| Duplicate file import | Unbounded resource use | Mitigated — deduplication by canonical path, 1024 paths per batch |
style-src 'unsafe-inline': required for syntect's inline code highlighting styles. A markdown file can include a<style>tag that overrides the app's appearance.- Remote images load by default: a markdown file with
will make a network request, revealing the user's IP address to that server. - No sandboxing of rendered HTML: the webview trusts the CSP to constrain what the HTML can do. The CSP blocks the most dangerous vectors (scripts, frames, forms, objects) but cannot prevent all CSS-based UI manipulation.