v0.7.0
Released: 2026-07-24
Changes since v0.6.1 (8f6f69b..merge of #120–#124). A batch of security and robustness fixes plus one UX feature, all from @samirhvbr, integrated with additional hardening.
Security
-
Path containment by ancestry, not string prefix (PR #120 by @samirhvbr): The traversal guards in
NotesController#serve_asset,NotesService#safe_path, andImagesService#safe_pathare consolidated into onePathSafety.contain. It fixes a sibling-prefix bypass (base/data/notesno longer accepts/data/notes-backup/...), rejects absolute-path and../escapes, and — via a realpath re-check — rejects symlinks inside the tree that resolve outside it. Behavior note: notes that are symlinks pointing outside the notes directory are now treated as out-of-bounds and can't be read/served; keep note content insideNOTES_PATH. -
Enforce the image allow-list on base64 uploads (PR #121 by @samirhvbr): The base64/AI-image upload path (
ImagesService.upload_base64_data) previously skipped the extension allow-list, so animage/svg+xmlmime or an.htmlfilename could be stored under the notes dir and served inline (stored XSS) — the same class fixed for multipart uploads in v0.4.0, now closed for base64 too. Validation runs against the on-disk extension with no bypass; valid AI images are unaffected.
Robustness
-
Atomic note writes (PR #122 by @samirhvbr): Notes are written to a temp file in the destination's own directory and atomically renamed over the target, so an interrupted or failed write (crash, ENOSPC) never truncates the existing note. Integration adds file-mode preservation — the existing note's permission bits are carried across the rename instead of being reset to the umask default.
-
Content search survives invalid-UTF-8 files (PR #123 by @samirhvbr): A single Latin-1/binary
.mdfile no longer aborts the whole search — bad bytes are scrubbed per line, and an unreadable file (permission/TOCTOU) is skipped rather than failing the search.
Features
- Show where a new note/folder will be created (PR #124 by @samirhvbr): The new-note / new-folder dialog now displays the destination path, so you're not guessing which folder is the target. New strings are translated across all 7 locales. Integration also fixes a pre-existing bug the feature surfaced: a root "New Note" after a cancelled "New Note in Folder" no longer inherits the stale folder.
Chores
- SimpleCov
add_filter→skip(silences the deprecation introduced by the simplecov 1.0 bump).
Test Coverage
- 602 Ruby tests, 1,437 JavaScript tests (2,039 total).