Commit graph

13 commits

Author SHA1 Message Date
Claude 7c6001291c
Fix documentation and code inconsistencies found during audit
- CLAUDE.md: Fix drawer width unit (80vw -> 80cqi), clarify group-by-due-date
  is toggle-only (sorting not yet implemented), clarify Phase 4 status
- PLAN.md: Fix conflict resolution label (last-write-wins -> remote wins),
  add username to keyring key format, mark fallback credential storage as
  not yet implemented
- sync.rs: Fix comment table to say "conflict" instead of "last-write-wins"
  for both-modified and both-added cases (matches actual Conflict action)
- config.rs: Add temp file cleanup on atomic write rename failure to match
  the pattern already used in storage.rs

https://claude.ai/code/session_01YWvxpzeT3hEUxD9aNvfNuL
2026-04-06 12:08:22 +00:00
Claude 7eec8e22c8
Add tests for audit fixes: 15 new tests covering security and data integrity
New sync tests:
- validate_sync_path rejects backslashes and .. components
- validate_sync_path allows valid paths
- SyncLock prevents concurrent access
- SyncLock released on drop, lock file cleaned up
- SyncState save leaves no .tmp files
- OfflineQueue save leaves no .tmp files
- parse_frontmatter_for_conflict rejects oversized YAML (>64KB)

New storage tests:
- Deduplication with equal versions uses mtime tiebreaker
- Frontmatter parsing rejects oversized YAML (>64KB)
- Frontmatter parsing accepts normal-sized content
- Version saturates at u64::MAX (no panic or wrap)
- Delete task removes from metadata before file
- atomic_write leaves no .tmp files

https://claude.ai/code/session_01AJoK28N4vqLqzskq6ybGri
2026-04-06 11:09:35 +00:00
Claude 6174836b7f
Fix critical and high-severity issues from project audit
Security:
- Fix path traversal via backslash bypass in sync validate_sync_path()
- Replace silent HTTP client fallback with proper error propagation
- Add 64KB YAML frontmatter size limit to prevent DoS via crafted files

Data integrity:
- Reorder delete operations: update metadata before removing files to
  prevent orphaned metadata entries on crash
- Fix task deduplication to use file mtime as tiebreaker when versions
  are equal, preventing non-deterministic data loss
- Add rollback on conflict recovery failure (remove orphaned duplicate
  files when metadata update fails)
- Clean up temp files on atomic write rename failure
- Add file-based sync lock to prevent concurrent sync operations
- Use saturating_add for task version to prevent overflow

Error handling:
- Surface move_task rollback failures as structured errors instead of
  silent warnings
- Log WebDAV parallel request failures instead of silently swallowing
- Emit watcher-error events to frontend instead of only printing to stderr

Frontend:
- Fix focus listener leak in auto-sync (clean up if stopAutoSync called
  while promise pending)
- Add prefers-reduced-motion CSS media query for accessibility
- Add ARIA labels, roles, and keyboard handlers to TaskItem, BottomSheet,
  and ConfirmDialog components
- Replace BottomSheet children: any with Snippet type

https://claude.ai/code/session_01AJoK28N4vqLqzskq6ybGri
2026-04-06 11:03:11 +00:00
Claude a12deb5182
Harden codebase: fix security, quality, and maintainability issues
- Replace dangerous unwrap() with proper error handling (storage.rs, webdav.rs)
- Add atomic writes (temp + rename) for config, sync state, and metadata files
- Add path traversal validation in sync executor
- Add workspace path validation in Tauri commands
- Add input size limits for task titles, descriptions, and list names
- Add file download size limit (10MB) to WebDAV get_file
- Fix move_task rollback to log failures instead of silently ignoring
- Fix JSON serialization unwrap in Tauri create_remote_workspace
- Fix swallowed errors in sync queue backup, metadata writes, sync state load
- Extract hardcoded strings into named constants (filenames, extensions, limits)
- Use REQUEST_TIMEOUT/CONNECT_TIMEOUT constants in WebDAV client builder
- Fix frontend: clear taskStack when viewed task is deleted or list is switched
- Fix frontend: surface credential loading and focus listener errors

https://claude.ai/code/session_01F67yfLLmSaBtT7aKKNus1M
2026-04-06 10:17:30 +00:00
Tristan Michael 0ae0705331 Add per-workspace sync interval and fix download timestamp recording
Add a configurable per-workspace sync interval (sync_interval_secs) with
Tauri command set_sync_interval, UI dropdown in Settings, and store
support to restart auto-sync when changed. Remove the redundant
main-panel sync status chip and surface upload/download counts in the
drawer footer and Tasks screen. Fix the sync logic to record the remote
file's last_modified timestamp when downloading (instead of local mtime)
so subsequent diffs don’t falsely trigger downloads.

These changes were needed to allow users to control auto-sync frequency
per workspace, simplify the UI by moving sync counts to the drawer
footer, and correct a bug where downloads were always considered new
because the local file mtime was used instead of the authoritative
remote timestamp.
2026-04-05 16:35:22 -07:00
Tristan Michael e33fb9dd0b Replace timestamp LWW with checksum-based conflict resolution
Rework sync conflict handling to stop using timestamp-based
last-write-wins and use a single Conflict action. The conflict handler
now downloads the remote file and compares SHA-256 checksums: identical
content is treated as a false conflict and skipped; when different, the
remote version wins and the local task file is recovered as a duplicate
with a new UUID and a "[RECOVERED FROM CONFLICT]" prefix. Duplicates are
inserted adjacent to the original in .listdata.json; non-task files
still use remote-wins without duplication. Removed local_wins() and its
tests, simplified action variants to a single Conflict, and updated
conflict-related tests and reporting accordingly.
2026-04-05 15:44:49 -07:00
Tristan Michael 25358a9eec Handle remote-deleted + local-unchanged as local delete
Fix compute_sync_actions to properly treat the case where a file has
been deleted on the remote but remains unchanged locally: previously the
code always emitted an Upload (re-uploading the local copy). Now it
checks the base checksum and emits DeleteLocal when the local copy
matches the base (unchanged), and emits Upload only when the local copy
differs (modified).

Updated tests: adjusted test_remote_deleted_local_unchanged to assert
DeleteLocal, and added test_remote_deleted_local_modified to verify the
upload behavior when the local file is modified while remote was
deleted.
2026-04-05 15:26:55 -07:00
Tristan Michael 753cb1cad5 Rename workspace metadata and add WebDAV folder browsing
Rework WebDAV workspace setup to use .onyx-workspace.json instead of
.metadata.json and to let users pick a remote folder instead of forcing
an Onyx/ subfolder. This updates storage, sync, config types, tests, and
CLI/Tauri commands to store a webdav_path in WorkspaceConfig and to
combine webdav_url + webdav_path for sync.

Changes include:
- Rename .metadata.json → .onyx-workspace.json across storage, sync, and tests so workspace detection and root metadata use the new filename.
- Remove hardcoded automatic "Onyx/" subfolder in sync and use the user-selected remote path directly.
- Add webdav_path field to WorkspaceConfig (Rust and TypeScript types) and thread it through add_webdav_workspace and frontend addWebdavWorkspace.
- Add three Tauri commands (list_remote_folder, inspect_remote_workspace, create_remote_workspace) to support remote folder browsing, workspace preview, and remote workspace creation.
- Rewrite SetupScreen WebDAV flow to Connect → Browse (lazy folder explorer) → Preview or Create, and wire UI state/handlers to the new commands.
- Update CLAUDE.md to document the new on-disk filename and note development phase allowing breaking changes.
2026-04-05 14:30:22 -07:00
Tristan Michael 58f37b08d6 fix: harden WebDAV sync — async credentials, consolidated command, Onyx subfolder 2026-04-03 10:11:46 -07:00
Tristan Michael be6b8d0d90 security: update callers for hardened credential API
- Handle Result from WebDavClient::new in CLI sync, core sync, and Tauri
- Unwrap Zeroizing<String> at Tauri serialization boundary
- Use .as_str() for basic_auth calls with Zeroizing<String> fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:11:40 -07:00
Tristan Michael e0c7292a7e fix: harden sync safety — conflict backup, timestamp parsing, credential zeroization
Back up local files before overwriting during ConflictRemoteWins so data
is never silently lost. Fix false-positive change detection by parsing
timestamps before comparing (different formats like RFC3339 vs HTTP date
were never equal as strings). Add zeroize crate to zero WebDAV credentials
in memory on drop, preventing exposure in core dumps.
2026-04-02 09:37:43 -07:00
Tristan Michael 056cd8ee49 fix: harden sync file validation and offline queue corruption handling
Restrict is_syncable() to validate path depth: .md files and .listdata.json
must be at depth 2 (inside list dirs), .metadata.json only at depth 1 (root).
Prevents syncing arbitrary files at unexpected depths. Back up corrupted
sync queue files before resetting, and log warnings on parse failures
instead of silently dropping queued operations.
2026-04-02 09:35:38 -07:00
Tristan Michael 9e204ef818 rename onyx-core crate (formerly bevy-tasks-core) 2026-03-31 09:46:56 -07:00
Renamed from crates/bevy-tasks-core/src/sync.rs (Browse further)