Commit graph

72 commits

Author SHA1 Message Date
Horizontal42
a4b69ec9e7 sync Cargo.lock with 1.2.9 2026-08-19 16:50:11 +03:00
Horizontal42
b91f650c31 update tauri.conf version to 1.2.9 2026-08-19 16:49:57 +03:00
Horizontal42
1cd9807543 bump cargo version to 1.2.9 2026-08-19 16:49:50 +03:00
Horizontal42
8c50fc8aa9
🔒 [security fix] Remove unsafe path canonicalization in directory gate (#111)
* 🔒 [security fix] Remove unsafe path canonicalization in directory gate

🎯 What:
Removed filesystem-level path canonicalization in the DataDirGate check (`canon_or_raw`).

⚠️ Risk:
An attacker could bypass directory authorization by creating a symlink to an authorized directory, allowing them to redirect arbitrary path reads/writes via TOCTOU (Time-of-Check to Time-of-Use) attacks or misleading unverified paths.

🛡️ Solution:
Replaced `fs::canonicalize` with purely lexical path normalization (resolving `.` and `..` in memory) to ensure strict string match validation without following symlinks. Ensure relative paths with `..` that go above the start are handled securely.

Co-authored-by: Horizontal42 <75383519+Horizontal42@users.noreply.github.com>

* 🔒 [security fix] Remove unsafe path canonicalization in directory gate

🎯 What:
Removed filesystem-level path canonicalization in the DataDirGate check (`canon_or_raw`).

⚠️ Risk:
An attacker could bypass directory authorization by creating a symlink to an authorized directory, allowing them to redirect arbitrary path reads/writes via TOCTOU (Time-of-Check to Time-of-Use) attacks or misleading unverified paths.

🛡️ Solution:
Replaced `fs::canonicalize` with purely lexical path normalization (resolving `.` and `..` in memory) to ensure strict string match validation without following symlinks. Ensure relative paths with `..` that go above the start are handled securely without dropping components incorrectly. Added proper Iterator methods to appease clippy.

Co-authored-by: Horizontal42 <75383519+Horizontal42@users.noreply.github.com>

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-08-19 16:35:45 +03:00
Horizontal42
868a34497b cargo fmt 2026-08-19 04:17:58 +03:00
Horizontal42
15e4fc5157 sync Cargo.lock with 1.2.8 2026-08-19 04:13:59 +03:00
Horizontal42
e88872a2b3 update tauri.conf version to 1.2.8 2026-08-19 04:13:49 +03:00
Horizontal42
6a81673cd5 bump cargo version to 1.2.8 2026-08-19 04:13:38 +03:00
Andrew Pimenov
b285f546c2 stream-cap rates responses instead of buffering then checking size
resp.bytes() already pulls the whole body into memory before the 5MB
check ran, so a malicious server could still OOM the process during
download. read_body_capped aborts mid-stream once the cap is hit.

also fix goal-seek probe fallback: it could update lo/hi to a genuine
bracket without ever setting found=true, throwing "no solution" on
cases that do have one.
2026-08-19 04:11:27 +03:00
Андрей Пименов
509926bcef fix: address adversarial review findings
- Fix race condition in write_atomic_bytes by using unique temp file names
- Fix logic flaw in DataDirGate authorization on migration failures
- Reject invalid padding in base64_decode
- Set size limits on json fetches for rates
- Fix Date math around DST boundaries for tomorrow/yesterday
- Handle division by zero exceptions gracefully in goal seek
- Fix OOM vector for huge hexadecimal number rendering
2026-08-19 04:11:27 +03:00
Horizontal42
0fb63e9150 sync Cargo.lock with 1.2.7 2026-08-14 13:39:08 +03:00
Horizontal42
5918d89755 update tauri.conf version to 1.2.7 2026-08-14 13:39:08 +03:00
Horizontal42
c29b9fc244 bump cargo version to 1.2.7 2026-08-14 13:39:08 +03:00
Horizontal42
51ee3b3208 linux + macos release builds (unsigned)
split bundle config into tauri.{windows,linux,macos}.conf.json so each
platform only carries its own targets; gen-icon emits icon.icns; macos is
ad-hoc signed since arm64 refuses unsigned downloads
2026-08-14 13:39:08 +03:00
Horizontal42
e5667966fd sync Cargo.lock with 1.2.6 2026-08-14 13:39:08 +03:00
Horizontal42
c09aec627e update tauri.conf version to 1.2.6 2026-08-14 13:39:07 +03:00
Horizontal42
5ec2f59b0c bump cargo version to 1.2.6 2026-08-14 13:39:07 +03:00
Horizontal42
a992f6d79a Fix CSP vulnerability by removing unsafe-inline for styles
Removes the 'unsafe-inline' directive from the style-src Content
Security Policy to prevent potential Cross-Site Scripting (XSS)
or malicious style injections. Replaces inline style manipulations
(`style.display = "none"`, `style=""`) with class list toggling
using a new `.d-none` utility class.
2026-08-14 13:39:06 +03:00
Horizontal42
359d94cf7c sync Cargo.lock with 1.2.5 2026-08-12 20:11:01 +03:00
Horizontal42
51972c0599 update tauri.conf version to 1.2.5 2026-08-12 20:10:40 +03:00
Horizontal42
d6c79b4662 bump cargo version to 1.2.5 2026-08-12 20:10:27 +03:00
Horizontal42
c3bc67bd5b gate every dir-taking IPC command against a Rust-owned trust anchor
load_documents, save_documents, run_backups, backup_deleted_sheet,
open_backups_folder and data_dir_has_documents all took a `dir` param
straight from the frontend and used it unchecked — any IPC caller
(a buggy extension, compromised webview content) could point Rust at
an arbitrary path, and migrate_data_dir's old_dir was the worst of it:
combined with strategy="move" it deletes documents.json and everything
under backups/ at whatever path it's handed.

Add DataDirGate: a path only counts as authorized if it was proven
user-chosen, either just now via the native folder picker or because
it's the data-dir.txt this file itself wrote after a previous
successful commit. save_file/load_file refuse to touch that filename
so the frontend can't forge its own authorization. docs_dir() now
returns Result and every command above goes through it.

migrate_data_dir authorizes the target before copying but only writes
data-dir.txt once every file has actually landed, so a migration that
dies partway leaves the app still pointing at a folder that works.
2026-08-12 20:00:00 +03:00
Horizontal42
3bc946bd44 consistent size cap for OS-opened sheet files
read_text_file (drag&drop) capped sheets at 1MB with an extension
whitelist; get_launch_file and the single-instance file-open handler
only checked the extension. Route both through read_sheet_file so a
file association or second launch gets the same cap.
2026-08-12 19:59:12 +03:00
Horizontal42
dea7314842 move cache file I/O off the async runtime thread
rates.json, market.json and rates-<date>.json reads/writes ran
straight on the tokio worker calling the Tauri command, blocking the
shared async runtime for the duration. Wrap them in
tauri::async_runtime::spawn_blocking via read_file_async/write_atomic_async.
2026-08-12 19:57:55 +03:00
Horizontal42
2d682448c6 dedupe historical-rate map parsing
fetch_historical_rates and fetch_historical_rates_batch each built the
same USD-anchored, uppercased rate map inline. Extract parse_rate_map
and cover it with tests.
2026-08-12 19:56:11 +03:00
Horizontal42
a8121dbc90 share atomic-write bytes helper between write_atomic and image export
write_image_file hand-rolled the same temp+rename dance write_atomic
already did for text, just for bytes. Split write_atomic into a
write_atomic_bytes core and have both callers use it.
2026-08-12 19:54:37 +03:00
Horizontal42
d7b88cc9c9 atomic write for deleted-sheet backups
fs::write left the one safety-net copy of a deleted sheet exposed to
mid-write corruption; use the existing temp+rename helper like every
other user-data write.
2026-08-12 19:54:06 +03:00
Horizontal42
b399b95263 sync Cargo.lock with 1.2.4 2026-08-09 16:33:24 +03:00
Horizontal42
6ab5b9e5d9 update tauri.conf version to 1.2.4 2026-08-09 16:31:24 +03:00
Horizontal42
78538511b8 bump cargo version to 1.2.4 2026-08-09 16:31:04 +03:00
Horizontal42
dad3de70a5
bound migrate_data_dir cleanup to known files, add dialog-verified new_dir 2026-08-09 16:27:08 +03:00
Horizontal42
354705cf15
scope read_text_file to files from real drop events 2026-08-09 16:23:33 +03:00
Horizontal42
55d8401a44
batch historical rates IPC calls with a pooled http client 2026-08-09 15:59:44 +03:00
Horizontal42
c958f30662 Revert "rotate updater signing pubkey"
This reverts commit c0bea836bc.
2026-07-29 15:54:41 +03:00
Horizontal42
c0bea836bc rotate updater signing pubkey 2026-07-29 15:46:13 +03:00
Horizontal42
40cdf22461 sync Cargo.lock with 1.2.3 2026-07-29 15:35:03 +03:00
Horizontal42
8bfe65a1c4 update tauri.conf version to 1.2.3 2026-07-29 15:35:03 +03:00
Horizontal42
2798bfcafa bump cargo version to 1.2.3 2026-07-29 15:35:03 +03:00
Horizontal42
b0f6e9e89b run extensions in a quickjs wasm sandbox
replaces new Function() in the main window; per-script and per-call
interrupt deadlines, memory and stack limits, graceful skip if the
wasm module fails to load. csp script-src drops 'unsafe-eval' for
'wasm-unsafe-eval'.
2026-07-29 14:38:28 +03:00
Horizontal42
5c4f2db2d2 fix clippy collapsible_match warning in safe_name 2026-07-29 14:14:49 +03:00
Horizontal42
a6fc5b90f3 harden safe_name path validation 2026-07-29 14:14:49 +03:00
Horizontal42
f5b1fc038b sync Cargo.lock with 1.2.2 2026-07-16 03:41:10 +03:00
Horizontal42
ca19042af0 update tauri.conf version to 1.2.2 2026-07-16 02:52:03 +03:00
Horizontal42
539fb6eff5 bump cargo version to 1.2.2 2026-07-16 02:49:05 +03:00
Horizontal42
6f6c3b439a bump version to 1.2.1 2026-07-08 15:58:14 +03:00
Horizontal42
c1a7254fdf fix arbitrary file write via write_text_file/write_image_file
Both Tauri commands took a raw path string straight from the frontend
and wrote to it with no validation — any code able to call invoke()
(a compromised or malicious extension script, which run with full page
privileges) could write to any path the OS user has access to, not just
one chosen through the save dialog. Move the save-file dialog into the
Rust command itself so the only path ever used is one the native dialog
returned; the frontend now only sends the content to save.
2026-07-08 15:50:13 +03:00
Horizontal42
f634b1858e fetch market data symbols concurrently instead of sequentially
fetch_market_data hit Yahoo's v8 chart endpoint once per symbol in a
sequential loop — with 9 symbols that's 9 round trips end to end.
join_all fires them together.
2026-07-08 15:47:31 +03:00
Horizontal42
b179f64fb4 bump version to 1.2.0 2026-07-03 13:26:32 +03:00
Horizontal42
8b4e6fc0fe market data: switch to Yahoo v8 chart, drop delisted tickers
v7/finance/quote returns 401 now — Yahoo stopped issuing anonymous
clients the cookie+crumb pair it requires. v8 chart still answers
without auth, one symbol per request instead of one bulk call.
Also dropped SBER/GAZP from the tracked symbols; Yahoo delisted
Russian tickers in 2022, so those two always missed anyway.
2026-07-03 11:51:40 +03:00
Horizontal42
d0d13ec1c0 historical rates: point at frankfurter.dev, fetch on cold start too
api.frankfurter.app 301-redirects to frankfurter.dev now, and the old
host is flaky over reqwest/rustls-tls — talk to the new one directly
(Rust backend, the vite-dev web fallback, and the CSP allowlist).

Separately, a sheet reopened on boot with "on 2024-01-01" in it never
fetched historical rates at all: the fetch only ran from switchDoc()
and the editor's onChange, and the editor's initial text is seeded
directly, bypassing both.
2026-07-03 11:46:47 +03:00