The current build keeps the Electron renderer outside the trusted computing
boundary while completing project creation, file/folder mutation, recent
projects, source-only ZIP export, and release-candidate verification. It adds no
network service, telemetry, remote content, arbitrary renderer filesystem
access, bundled TeX distribution, updater, or production dependency. The
implementation-matched threat model remains THREAT_MODEL.md.
Sprint 14 adds collaboration research controls only: COLLABORATION_SRS.md,
COLLABORATION_THREAT_MODEL.md, and ADR-0015. No collaboration listener,
runtime dependency, preload method, UI, or remote compile path exists.
The application:
- uses
spawn(executable, args, { shell: false }); - passes paths as argument-array entries;
- validates the project and root file through canonical paths;
- rejects build paths that lexically or through filesystem links leave the project;
- writes output under
.texpulse/buildby default; - passes
-norcby default and omits it only after explicit per-project trust; - always passes
-no-shell-escape; - reports only output paths that exist;
- enforces a default 120-second compiler timeout;
- limits aggregate child stdout/stderr capture to 8 MiB and terminates the process tree when the limit is reached;
- cancels by build ID through
AbortController; - terminates Windows compiler descendants with direct, shell-free
taskkill.exe /T /F; - isolates outputs by generation so stale or failed builds cannot overwrite the retained successful PDF;
- accepts only regular generated files, with at most 4,096 files, 128 MiB per file, and 512 MiB total per generation;
- rejects and link-safely removes output that exceeds quotas or contains links or non-regular entries;
- retains at most eight recognized build generations while preserving the current and visible successful output;
- rejects adapter results with mismatched build identity;
- canonicalizes the selected project directory;
- accepts only relative project entry paths and verifies resolved paths remain below the canonical root;
- rejects traversal through project-internal symbolic links and junctions;
- lists link entries without following them;
- decodes editor files as bounded valid UTF-8 text;
- saves through a same-directory temporary file with sync and atomic rename;
- requires a SHA-256 version token before replacing a file;
- reports changed or deleted external files explicitly;
- validates and migrates project and global settings, safely falling back with visible issues;
- ignores generated and dependency directories during project enumeration;
- enables the Electron sandbox before app readiness;
- uses
nodeIntegration: false,contextIsolation: true,sandbox: true, andwebSecurity: true; - disables renderer Node access in frames and workers;
- exposes thirty-four frozen
project/build/PDF/SyncTeX/settings/recovery/Git/event preload methods, never
ipcRenderer; - validates the sending web contents and main frame for every IPC call;
- validates every IPC request and response with strict Zod schemas;
- keeps the absolute project root out of renderer responses;
- resolves project-relative reveal requests in the main process and invokes the desktop shell only after canonical boundary, file-type, and link checks;
- recursively copies only validated regular project files and directories, rejects links, and rejects copying a directory into itself;
- denies permission requests, popups, webviews, and unexpected navigation;
- denies renderer-originated external navigation instead of exposing an external-URL capability;
- logs only the rejected URL scheme rather than the complete URL;
- disables DevTools for production and packaged windows; and
- applies a local-only Content Security Policy with no network connections, objects, forms, external bases, inline scripts, or evaluated scripts;
- permits only a same-origin PDF.js worker through
worker-src 'self'; - returns build metadata and opaque artifact tokens without structured canonical paths;
- revalidates artifact identity, canonical generation paths, and file type before PDF reads or desktop shell actions;
- limits PDF preview input to 100 MiB and renderer raw-log display to 2 MiB;
- loads only completed compiler output into PDF.js;
- keeps the last successful PDF visible after failure; and
- uses Electron shell open/reveal only for a revalidated generated PDF;
- starts the project watcher in the main process, never the renderer;
- prevents watcher traversal through symbolic links and junctions;
- excludes generated, metadata, dependency, coverage, and distribution directories from watcher traversal;
- suppresses editor-originated watcher events by matching resulting file versions;
- sends only an opaque project ID, relative path, and validated event kind to the renderer;
- returns read-only Git status as bounded counts and branch metadata, never as filesystem capabilities or path lists;
- treats watcher events as informational rather than direct save/build triggers;
- serializes saves and retains version-token checks before automatic builds;
- rejects build and PDF results when the renderer source revision changed; and
- persists only validated relative workspace state and preferences, never source text, PDF bytes, logs, canonical paths, or credentials;
- stores bounded dirty-buffer recovery separately under Electron
userData, keyed by opaque project ID; - limits recovery to 20 buffers, 2 MiB per buffer, and 10 MiB total;
- restores recovery into dirty editor state only after explicit user review and never writes project files automatically;
- records bounded structured application events in a 1 MiB current log plus one rotated log without storing document content by default;
- exports support logs only after user action and redacts home and active project paths where practical;
- lets the user clear project recovery or all recovery and application logs;
- parses only the bounded renderer log copy in a pure module without filesystem or process access;
- limits each build response to 200 diagnostics, 4,096 message characters, and 2,048 excerpt characters per diagnostic;
- resolves diagnostic links only to files enumerated inside the open project and returns project-relative paths;
- discards unknown absolute paths as navigation targets;
- sends diagnostics through a strict Zod response schema;
- rejects stale diagnostic generations and clears accepted diagnostics after source edits;
- renders messages and excerpts as escaped React text, never HTML; and
- reuses the existing validated
readTextFilecapability for source navigation; - accepts SyncTeX requests only for the current visible successful artifact;
- validates forward source paths through the canonical project service;
- resolves inverse paths only against enumerated project files and returns only project-relative paths;
- invokes SyncTeX with argument arrays,
shell: false, a five-second timeout, and the canonical project working directory; - removes
SYNCTEX_VIEWERandSYNCTEX_EDITORbefore invocation; - parses no more than 512 KiB of SyncTeX result text; and
- reports missing, malformed, failed, or stale navigation non-fatally without repeating canonical paths or child output;
- stores global settings under Electron
userDataand project settings under validated.texpulse/project.json; - resolves only fixed executable names from a user-selected custom tool directory and displays the resulting paths and versions;
- runs toolchain readiness through the isolated doctor fixture rather than a user project;
- never equates a skipped self-test with a successful compile;
- treats
.latexmkrcand otherlatexmkconfiguration files as executable trusted input and warns before enabling them; - performs clean builds through the normal timeout, cancellation, generation, and stale-result controls;
- deletes only allowlisted auxiliary suffixes under validated generation directories, skips links and junctions, and preserves PDFs, logs, SyncTeX data, and unknown files; and
- rejects project-settings changes and cleanup while session work could race;
- resolves fixed application resources from explicit development or packaged roots instead of the current working directory;
- exposes sample onboarding only as a no-argument fixed IPC method;
- copies only the bundled regular
main.texinto application data, preserves an existing regular file, and rejects unsafe destination entries; - packages only local production bundles, dependencies, and the fixed sample;
- does not bundle MiKTeX, Perl, credentials, analytics, an updater, or source projects; and
- preserves application data during uninstall instead of deleting user edits, settings, recovery, or logs without a separate user action;
- selects new-project and ZIP destinations in the main process rather than accepting renderer-provided absolute paths;
- creates a project only at a missing destination from the fixed bundled regular-file template;
- validates every project mutation through the canonical project service, pauses the watcher during mutation, and rejects mutation while a build or maintenance operation is active;
- requires content-version checks before renaming or deleting an open file;
- requires explicit modal confirmation before project deletion;
- exposes recent projects through bounded opaque IDs and re-resolves each ID in the main process before opening;
- exports only regular files, skips links and junctions, excludes metadata, generated, dependency, coverage, distribution, and VCS directories, and replaces the destination through a temporary ZIP file; and
- records release provenance as a tagged source archive plus installer and ASAR SHA-256 values without embedding credentials.
CodeMirror injects runtime styles, so the CSP currently permits inline styles. Inline and evaluated scripts remain disallowed. This exception is documented in ADR-0006 and must not be widened to scripts.
PDF.js 6.0.227 is pinned as a production dependency because the SRS requires a local PDF.js viewer. It is lazy-loaded with its local worker and is covered by the frozen lockfile and dependency audit.
Chokidar 5.0.0 is pinned as a production dependency because recursive native Windows project watching, event normalization, atomic-write handling, and link-following controls are required for live external-change detection. It runs only in the main process and is covered by the same lockfile, audit, path boundary, and integration tests.
Electron Builder 26.15.3 is pinned as a development-only dependency because the SRS requires a Windows installer and unpacked development package. It is covered by the frozen lockfile and dependency audit and is not shipped as application runtime code.
The raw user-visible compiler log may contain local absolute paths and
environment details emitted by MiKTeX or latexmk. These strings do not become
filesystem capabilities, and source remains local, but the path-text exposure is
intentional for unmodified troubleshooting output.
Structured diagnostics do not repeat arbitrary absolute paths in their file
field. A path becomes selectable only when it matches a known project-relative
entry. Raw excerpts may still contain the original local path text because they
are troubleshooting text, not capabilities.
The product remains a trusted local-project editor rather than a hostile-code sandbox. TeX executes with the desktop user's permissions. Generated-output quotas are checked after process exit, so transient disk or resource use is possible until timeout. Multi-user or hosted compilation requires a separately reviewed sandboxed execution design.
A custom executable directory and enabled latexmk configuration are explicit
local trust decisions. They do not enable TeX shell escape, but they can select
or execute user-controlled local programs and Perl configuration respectively.
- Treat the renderer and all user-controlled data as untrusted.
- Keep
nodeIntegration: falseandcontextIsolation: true. - Expose only narrow, typed, validated preload APIs.
- Canonicalize paths and enforce project boundaries.
- Use executable plus argument arrays for compiler and SyncTeX processes.
- Keep TeX shell escape disabled by default.
- Enforce compile timeout, cancellation, output bounds, and process cleanup.
- Reject stale build results.
- Reject stale file-version tokens before replacement.
- Do not traverse project-internal links or junctions.
- Validate external URLs and configure a Content Security Policy.
- Keep source content local and omit analytics from the initial release.
- Keep collaboration disabled and absent from runtime until a separate implementation sprint proves the feature flag, authority model, schemas, limits, and tests.
- Production dependencies require a documented reason.
- Versions are pinned by
package.jsonandpnpm-lock.yaml. - CI uses a frozen lockfile.
- Dependency vulnerability review is a release gate under
NFR-SEC-012. - Security findings must not be hidden by disabling tests or lint rules.
- Covered compiler, project, Electron session/IPC, renderer state, and toolchain modules enforce at least 85% aggregate statement and branch coverage.
- Electron and renderer dependencies are pinned exactly and audited through the same frozen lockfile.
Code signing, SmartScreen reputation, update integrity/rollback, and repeat
validation on additional clean Windows accounts or VMs remain release
operations. Automatic external-file reload/merge and any new preload or
external-URL capability require their own validated contracts and tests.
Collaboration implementation remains deferred behind the Sprint 14 SRS, threat
model, and ADR-0015. ADR-0005 defines the path/link policy; ADR-0006 defines the
Electron boundary; ADR-0007 defines completed PDF loading and artifact actions;
ADR-0008 defines live build and project watching; ADR-0009 defines structured
diagnostic parsing and source links; ADR-0010 defines the SyncTeX process,
artifact, path, and renderer boundary; ADR-0011 defines settings, toolchain
readiness, latexmk trust, and cleanup; ADR-0012 defines output bounds,
recovery, support data, and navigation denial; ADR-0013 defines Windows
packaging, resources, onboarding, and uninstall behavior; ADR-0015 defines
collaboration research boundaries.