Commit graph

21 commits

Author SHA1 Message Date
escouade-bot
2f610bf10a fix: make legacy PIN rehash non-blocking in verify_pin (#54)
Replace hash_pin(pin)? with hash_pin(pin).ok() so that a rehash
failure does not propagate as an error. The user can now switch
profiles even if the Argon2id re-hashing step fails — the PIN
is still correctly verified, and the legacy hash remains until
the next successful login.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:46:27 -04:00
escouade-bot
34626711eb fix: address reviewer feedback (#54)
- Add automatic re-hashing of legacy SHA-256 PINs to Argon2id on
  successful verification, returning new hash to frontend for persistence
- Use constant-time comparison (subtle::ConstantTimeEq) for both
  Argon2id and legacy SHA-256 hash verification
- Add unit tests for hash_pin, verify_pin (Argon2id and legacy paths),
  re-hashing flow, error cases, and hex encoding roundtrip
- Update frontend to handle VerifyPinResult struct and save rehashed
  PIN hash via profile update

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:46:27 -04:00
escouade-bot
cea16c24ae fix: migrate PIN hashing from SHA-256 to Argon2id (#54)
Replace SHA-256 with Argon2id (m=64MiB, t=3, p=1) for PIN hashing.
Existing SHA-256 hashes are verified transparently via format detection
(argon2id: prefix). New PINs are always hashed with Argon2id.

Addresses CWE-916: Use of Password Hash With Insufficient Computational Effort.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:46:27 -04:00
le king fu
2e9df1c0b9 fix(rust): pass raw public key bytes to DecodingKey::from_ed_der
All checks were successful
PR Check / rust (push) Successful in 15m54s
PR Check / frontend (push) Successful in 2m15s
PR Check / rust (pull_request) Successful in 16m7s
PR Check / frontend (pull_request) Successful in 2m15s
Previous test refactor wrapped both keys in their respective DER
envelopes. CI surfaced the asymmetry: jsonwebtoken's two from_ed_der
constructors expect different inputs.

- EncodingKey::from_ed_der → PKCS#8 v1 wrapped (ring's
  Ed25519KeyPair::from_pkcs8 path). The 16-byte prefix + 32-byte seed
  blob is correct.
- DecodingKey::from_ed_der → raw 32-byte public key. Internally it
  becomes ring's UnparsedPublicKey::new(&ED25519, key_bytes), which
  takes the bare bytes, NOT a SubjectPublicKeyInfo wrapper.

The test was building an SPKI DER for the public key, so verification
saw a malformed key and failed every signature with InvalidSignature
(`accepts_well_formed_base_license` and `activation_token_matches_machine`).

Drop the SPKI helper, pass `signing_key.verifying_key().to_bytes()`
straight into DecodingKey::from_ed_der. Inline doc-comment captures
the asymmetry so the next person doesn't fall in the same hole.
2026-04-09 11:12:10 -04:00
le king fu
69e136cab0 fix(rust): use DER-built keys in license tests, drop ed25519-dalek pem feature
Some checks failed
PR Check / rust (push) Failing after 10m20s
PR Check / frontend (push) Successful in 2m15s
PR Check / rust (pull_request) Failing after 9m30s
PR Check / frontend (pull_request) Successful in 2m7s
cargo CI flagged: `unresolved import ed25519_dalek::pkcs8::LineEnding`. The
`LineEnding` re-export path varies between pkcs8/spki/der versions, so the
test code that called `to_pkcs8_pem(LineEnding::LF)` won't compile against
the dependency tree we get with ed25519-dalek 2.2 + pkcs8 0.10.

Fix:
- Drop the `pem` feature from the ed25519-dalek dev-dependency.
- In tests, build the PKCS#8 v1 PrivateKeyInfo and SubjectPublicKeyInfo
  DER blobs manually from the raw 32-byte Ed25519 seed/public key. The
  Ed25519 layout is fixed (16-byte prefix + 32-byte key) so this is short
  and stable.
- Pass the resulting DER bytes to `EncodingKey::from_ed_der` /
  `DecodingKey::from_ed_der`.

Refactor:
- Extract `strict_validation()` and `embedded_decoding_key()` helpers so
  the validation config (mandatory exp/iat for CWE-613) lives in one
  place and production callers all share the same DecodingKey constructor.
- `validate_with_key` and `validate_activation_with_key` now take a
  `&DecodingKey` instead of raw PEM bytes; production builds the key
  once via `embedded_decoding_key()`.
- New canary test `embedded_public_key_pem_parses` fails fast if the
  embedded PEM constant ever becomes malformed.
2026-04-09 10:59:12 -04:00
le king fu
99fef19a6b feat: add license validation and entitlements (Rust) (#46)
Some checks failed
PR Check / rust (push) Failing after 5m50s
PR Check / frontend (push) Successful in 2m9s
PR Check / rust (pull_request) Failing after 6m1s
PR Check / frontend (pull_request) Successful in 2m12s
Introduces the offline license infrastructure for the Base/Premium editions.

- jsonwebtoken (EdDSA) verifies license JWTs against an embedded Ed25519
  public key. The exp claim is mandatory (CWE-613) and is enforced via
  Validation::set_required_spec_claims.
- Activation tokens (server-issued, machine-bound) prevent license.key
  copying between machines. Storage is wired up; the actual issuance flow
  ships with Issue #49.
- get_edition() fails closed to "free" when the license is missing,
  invalid, expired, or activated for a different machine.
- New commands/entitlements module centralizes feature → tier mapping so
  Issue #48 (and any future gate) reads from a single source of truth.
- machine-uid provides the cross-platform machine identifier; OS reinstall
  invalidates the activation token by design.
- Tests cover happy path, expiry, wrong-key signature, malformed JWT,
  unknown edition, and machine_id matching for activation tokens.

The embedded PUBLIC_KEY_PEM is the RFC 8410 §10.3 test vector, clearly
labelled as a development placeholder; replacing it with the production
public key is a release-time task.
2026-04-09 10:02:02 -04:00
le king fu
08c54b1f75 Fix migration repair: update checksums instead of deleting records
Some checks are pending
Release / build-and-release (push) Waiting to run
The previous approach deleted migration records to force re-application,
but this is dangerous for migration 2 which DELETEs all categories and
keywords before re-inserting them, wiping user customizations.

Now computes the expected SHA-384 checksum (matching sqlx) and updates
the stored checksum in _sqlx_migrations, so the migration is recognized
as already applied without being re-run.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 16:27:02 -05:00
le king fu
15d626cbbb Fix migration checksum mismatch on startup
Add repair_migrations Tauri command that deletes stale migration 1
checksum from _sqlx_migrations before Database.load(). Migration 1
is idempotent (CREATE IF NOT EXISTS) so re-applying is safe.
Fixes "migration 1 was previously applied but has been modified".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 09:17:35 -05:00
le king fu
d2a0ee65b3 Fix app stuck on spinner after v0.4.0 update (GH #9)
Restore seed_categories.sql to its original content so the migration 2
checksum matches existing databases. Move the level-3 insurance
subcategories (310-312) into a new migration 7 using INSERT OR IGNORE.
Add .catch() on connectActiveProfile() to surface DB errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 21:54:42 -05:00
le king fu
a04813ced2 feat: add 3rd level of category hierarchy
Support up to 3 levels of categories (e.g., Dépenses récurrentes →
Assurances → Assurance-auto) while keeping SQL JOINs bounded and
existing 2-level branches fully compatible.

Changes across 14 files:
- Types: add "level3" pivot field, depth property on budget row types
- Reports: grandparent JOIN for 3-level resolution in dynamic reports
- Categories: depth validation (max 3), auto is_inputable management,
  recursive tree operations, 3-level drag-drop with subtree validation
- Budget: 3-level grouping with intermediate subtotals, leaf-only
  aggregation, depth-based indentation (pl-8/pl-14)
- Seed data: Assurances split into Assurance-auto/habitation/vie
- i18n: level3 translations for FR and EN

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:54:05 -05:00
Le-King-Fu
20cae64f60 feat: add multiple profiles with separate databases and optional PIN (v0.3.0)
Some checks failed
Release / build (windows-latest) (push) Has been cancelled
Each profile gets its own SQLite database file for complete data isolation.
Profile selection screen at launch, sidebar switcher for quick switching,
and optional 4-6 digit PIN for privacy. Existing database becomes the
default profile with seamless upgrade.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 12:54:09 +00:00
Le-King-Fu
db1d47ea94 fix: allow duplicate-content files with different names (#1)
Change imported_files UNIQUE constraint from (source_id, file_hash) to
(source_id, filename) so files with identical content but different names
each get their own record. Update createImportedFile to look up existing
records by filename instead of hash.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 12:40:01 +00:00
Le-King-Fu
87e8f26754 feat: add data export/import with optional AES-256-GCM encryption (#3)
Add export (JSON/CSV) and import (full replace) to the Settings page.
Export supports 3 modes (transactions+categories, transactions only,
categories only) with optional password encryption using Argon2id key
derivation. Import detects encrypted .sref files, prompts for password,
and shows a destructive confirmation modal before replacing data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 11:40:28 +00:00
Le-King-Fu
c7f7bab98f fix: restore schema.sql to exact v0.2.4 bytes for migration checksum (v0.2.7)
Some checks failed
Release / build (windows-latest) (push) Has been cancelled
v0.2.6 still had comment lines in schema.sql that changed the SHA-384
checksum. sqlx requires byte-for-byte match with the originally applied
migration SQL. schema.sql is now identical to the v0.2.4 original.

Migrations 4 (is_inputable column) and 5 (import_config_templates table)
in lib.rs handle adding these to existing databases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 17:23:36 +00:00
Le-King-Fu
9ed79b4fa3 fix: revert schema.sql to match migration 1 checksum (v0.2.5.1)
schema.sql was modified in v0.2.5 to include is_inputable column and
import_config_templates table. Since schema.sql is include_str!'d into
migration 1, this changed its SHA-256 checksum in sqlx's migration
tracker, blocking migrations 4 and 5 from running.

Reverts schema.sql to its original v0.2.4 state so the checksum matches
and new migrations can apply. Fixes both "no such table:
import_config_templates" and is_inputable defaulting to false.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 16:47:07 +00:00
Le-King-Fu
ccdab1f06a feat: add import config templates, budget/category fixes (v0.2.5)
Add reusable import config templates so users can save and apply CSV
parsing configurations across different import sources. Includes
database table, service, hook integration, and template UI in the
source config panel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 15:06:44 +00:00
Le-King-Fu
c73f466429 fix: persist has_header for imports, fix orphan categories, add re-initialize
Some checks failed
Release / build (windows-latest) (push) Has been cancelled
- Import: persist `has_header` flag to DB (migration v3) so headerless
  CSVs like Desjardins don't lose their first data row on re-import.
- Categories: promote children to root on parent deletion instead of
  cascading deactivation, preventing invisible orphans.
- Categories: add re-initialize button to reset all categories and
  keywords to seed defaults.
- Bump version to 0.2.1 across tauri.conf.json, package.json, Cargo.toml.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:54:33 +00:00
Le-King-Fu
0adfa5fe5e feat: add Settings page with in-app updater support
Add a Settings page with about card (app name + version) and an update
section that uses the Tauri v2 updater plugin to check GitHub Releases,
download signed installers, and relaunch. Includes full state machine
(idle/checking/available/downloading/readyToInstall/installing/error)
with progress bar and retry. Database in %APPDATA% is never touched.

- Add tauri-plugin-updater and tauri-plugin-process (Rust + npm)
- Configure updater endpoint, pubkey placeholder, and passive install mode
- Add signing env vars and updaterJsonPreferNsis to release workflow
- Add Settings nav item, route, and fr/en translations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 11:47:25 +00:00
Le-King-Fu
9ea8314149 feat: seed categories and keywords from SommaireDepense.csv
Add v2 migration with 6 parent categories, 36 child categories, and ~85
keywords extracted from the expense summary to enable auto-categorization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 01:40:47 +00:00
Le-King-Fu
49e0bd2c94 feat: implement CSV import wizard with folder-based source detection
Full import pipeline: Rust backend (6 Tauri commands for folder scanning,
file reading, encoding detection, hashing, folder picker), TypeScript
services (DB, import sources, transactions, auto-categorization, user
preferences), utility parsers (French amounts, multi-format dates),
12 React components forming a 7-step wizard (source list, config,
column mapping, preview, duplicate detection, import, report), and
i18n support (FR/EN, ~60 keys each).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 03:38:46 +00:00
Le-King-Fu
801404ca21 Initial project scaffold: Tauri v2 + React + TypeScript + TailwindCSS v4
- Tauri v2 with SQLite plugin and full database schema
- React with react-router-dom, i18n (FR/EN), recharts, lucide-react
- TailwindCSS v4 with custom Bleu/Creme/Terracotta palette
- App shell with sidebar navigation (7 pages)
- Dashboard with summary cards, page stubs for all sections
- Default category configuration (10 top-level categories)
- TypeScript interfaces matching SQLite schema

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 11:05:11 +00:00