From e7cd37547612b848512039f44376b1d008b95943 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Sun, 9 Aug 2026 00:39:48 +0530 Subject: [PATCH 1/6] test(reports): mutation-proof document output guards Harden the unprotected client-facing document boundaries demonstrated by the W1 mutation sweep. Add regression coverage that makes PDF invalid-date substitution fail, verifies bulk output bytes rather than file existence alone, and records per-party creation failures in the returned batch manifest. Extract the existing shared report filename predicate unchanged and cover CSV/local-export traversal, hidden-name, and separator rejection; cover single-statement slug portability. Mutation proof: defaulting PDF dates to 01-Jan-2000 passed the prior valid-only test, then failed new invalid_statement_date_fails_instead_of_using_a_default. Replacing bulk write_all(bytes) with write_all(&[]) passed the prior existence-only test, then failed its byte assertion. Silently dropping a file-creation error failed the new manifest test. Returning a default filename and raw party slug each failed their new tests. Existing XLSX/PDF amount/date/text and bulk slug/collision/renderer error mutations were already caught. --- src-tauri/src/commands.rs | 33 +++++++++++++++++-- src-tauri/src/reports/bulk_party_statement.rs | 27 +++++++++++++++ src-tauri/src/reports/party_statement_pdf.rs | 17 ++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 5c2bf43..7f98840 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -2843,8 +2843,7 @@ fn save_report_download_bytes( contents: &[u8], ) -> Result { use tauri::Manager as _; - let file_name = portable_export_file_name(file_name)?; - + let file_name = checked_export_file_name(file_name)?; // Tauri's own path resolver, so this needs no extra crate and no // capability grant. let downloads = app @@ -2857,6 +2856,10 @@ fn save_report_download_bytes( Ok(path.to_string_lossy().into_owned()) } +fn checked_export_file_name(file_name: &str) -> Result { + portable_export_file_name(file_name) +} + /// Reveals an exported file in the OS file manager. /// /// Only ever called with a path this process just wrote, and the path is @@ -3244,6 +3247,32 @@ mod party_statement_export_tests { let pdf: ExportPartyStatementRequest = serde_json::from_value(pdf).unwrap(); assert!(matches!(pdf.format, PartyStatementFormat::Pdf)); } + + #[test] + fn local_export_file_names_reject_path_like_and_hidden_values() { + assert_eq!( + checked_export_file_name(" statement.csv ").unwrap(), + "statement.csv" + ); + for name in [ + "", + ".hidden.csv", + "../statement.csv", + "nested/report.csv", + "nested\\report.csv", + ] { + assert!( + checked_export_file_name(name).is_err(), + "{name:?} must be rejected" + ); + } + } + + #[test] + fn statement_party_slug_is_portable_and_nonempty() { + assert_eq!(statement_filename_slug(" ../Aarav & Sons "), "aarav-sons"); + assert_eq!(statement_filename_slug("///"), "party"); + } } #[derive(Debug, Deserialize)] diff --git a/src-tauri/src/reports/bulk_party_statement.rs b/src-tauri/src/reports/bulk_party_statement.rs index 69399e1..4d7d4ac 100644 --- a/src-tauri/src/reports/bulk_party_statement.rs +++ b/src-tauri/src/reports/bulk_party_statement.rs @@ -301,6 +301,10 @@ mod tests { let file = destination.path().join(&result.written[0].file_name); assert!(file.starts_with(destination.path())); assert!(file.is_file()); + assert_eq!( + fs::read(&file).expect("statement bytes are readable"), + b"synthetic workbook" + ); assert_eq!( result.written[0].file_name, "statement-etc-passwd-20260808.xlsx" @@ -369,6 +373,29 @@ mod tests { assert_eq!(result.written[0].payable_amount, "7"); } + #[test] + fn per_party_file_creation_failure_is_retained_in_the_manifest() { + let destination = tempfile::tempdir().expect("temporary destination"); + let result = write_bulk_party_statements( + destination.path(), + "Synthetic Books Pvt Ltd", + "20260808", + "pdf/invalid", + &[bill("Write Failure", "10.00")], + &[], + |_| Ok(b"synthetic PDF".to_vec()), + ) + .expect("a partial batch result is returned"); + + assert!(result.written.is_empty()); + assert_eq!(result.failures.len(), 1); + assert_eq!(result.failures[0].party, "Write Failure"); + assert!(result.failures[0].error.contains("could not create")); + let manifest = fs::read_to_string(&result.manifest_path).expect("manifest is written"); + assert!(manifest.contains("Write Failure")); + assert!(manifest.contains("could not create")); + } + #[test] fn colliding_safe_names_are_written_to_distinct_files() { let destination = tempfile::tempdir().expect("temporary destination"); diff --git a/src-tauri/src/reports/party_statement_pdf.rs b/src-tauri/src/reports/party_statement_pdf.rs index 050be7e..db54d15 100644 --- a/src-tauri/src/reports/party_statement_pdf.rs +++ b/src-tauri/src/reports/party_statement_pdf.rs @@ -587,6 +587,23 @@ mod tests { )); } + #[test] + fn invalid_statement_date_fails_instead_of_using_a_default() { + let statement = build_party_statement( + "Synthetic Books Pvt Ltd", + "not-a-date", + "Synthetic Party", + &[bill("INV-1", "10.00", 5)], + &[], + ) + .expect("the renderer owns document-date validation"); + + assert!(matches!( + render_party_statement_pdf(&statement), + Err(PartyStatementPdfError::InvalidDate(value)) if value == "not-a-date" + )); + } + #[test] fn an_unrepresentable_amount_fails_instead_of_becoming_zero() { assert_eq!( From d3f8e3a0d937d33ff8c563bc57bc6149e925a8c8 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Mon, 17 Aug 2026 18:42:57 +0530 Subject: [PATCH 2/6] chore(tally): reseal compatibility surface for F1X Signed-off-by: Tapish Khandelwal --- .../compatibility/compatibility-surface.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/tally/compatibility/compatibility-surface.json b/docs/tally/compatibility/compatibility-surface.json index a0ddca3..6973826 100644 --- a/docs/tally/compatibility/compatibility-surface.json +++ b/docs/tally/compatibility/compatibility-surface.json @@ -63,7 +63,7 @@ }, { "path": "docs/tally/compatibility/synthetic-write-canary-fixture.md", - "sha256": "9ed85e60adb7306f11496b47f5a86d49327abfdd8e2735678c521187b9ce76e4" + "sha256": "dd2f1c68c0925523af1468dd9c61330433130c0e72b7c713dfc1ef9205b4756f" }, { "path": "docs/tally/support-matrix.md", @@ -79,7 +79,7 @@ }, { "path": "scripts/outstandings-copy.test.mjs", - "sha256": "8c9adb100a45704ca8ba72203a397defca3e8b01a297e96043c543cc052b980a" + "sha256": "58ca3cb255a8e54dc3a4590146b908b800fd4186d474eb05c3e1a46ae6c005a0" }, { "path": "scripts/tally-company-selection.test.mjs", @@ -143,7 +143,7 @@ }, { "path": "src-tauri/crates/bridge-tally-protocol/src/lib.rs", - "sha256": "e1c9082a214a125454c2bbddef8494283d41efa025e4acfc1525f0a22aa2bd1e" + "sha256": "c5c61049fdbedf31cfafb43d349961e736eb7c6a361ec08e410f2f94099b1200" }, { "path": "src-tauri/crates/bridge-tally-protocol/src/outstandings/completeness.rs", @@ -175,7 +175,7 @@ }, { "path": "src-tauri/crates/bridge-tally-protocol/tests/simulator_corpus.rs", - "sha256": "616eb8fa5e387bff74f62d8e775b7eb118860763b18ff63ad88ad5015f7f752f" + "sha256": "65da6e2a0cf543c46e1834d82f836b2966f9f574952b991259179cf743f907d6" }, { "path": "src-tauri/crates/bridge-tally-protocol/tests/stream_text_decoder.rs", @@ -219,7 +219,7 @@ }, { "path": "src-tauri/src/commands.rs", - "sha256": "052b45f94751a01a1b7960cd861d1c122b49f0b80088b0434fc33e958396992d" + "sha256": "e9e817e00ca33349d1735bce92e8c750685971d244f09f7912d1018eb37fead0" }, { "path": "src-tauri/src/db/encrypted.rs", @@ -339,7 +339,7 @@ }, { "path": "src-tauri/src/tally/runtime.rs", - "sha256": "1d2ae890a240344ad9a2a59b4624be2726d13c11e5e529a0b084e355f865deec" + "sha256": "4a4cb2bc46cfcadbb082550c8f24e735d8bfb9206f524d6bee4db8bbf69ef95d" }, { "path": "src-tauri/src/tally/serial_queue.rs", @@ -359,7 +359,7 @@ }, { "path": "src/OutstandingsScreen.tsx", - "sha256": "6835d23a31eb92e1037e8f1e9c268ea86c59554a6b7060009e4dfa086bfa03d6" + "sha256": "479aff297f63050a4903154e565062f774057bec2b9c452b81c5934782f9ea5e" }, { "path": "src/TallyReadinessFlow.tsx", @@ -371,7 +371,7 @@ }, { "path": "src/outstandings-copy.ts", - "sha256": "a2bcf820f694443ac122e91aa98fb354b6e2d5c6be3e1e9503b368a3a0a4f696" + "sha256": "0e78dd97d79c0166fe17a4e933733c369a7e4be3046ddbe3e38122ad5e234216" }, { "path": "src/outstandings-csv.ts", @@ -434,5 +434,5 @@ "sha256": "a27f294ee15e407b69fdfc73609e8708ac0509b6e6a8872daef5451fde61a8db" } ], - "manifest_sha256": "6b8b8ab6ed23d0e70d44b79bf497e23a1ebc32422911b19cdc213cac7dcd6c82" + "manifest_sha256": "" } From 2ff2d816081b60cc2489fe914dad7bc0638f60f7 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Tue, 18 Aug 2026 05:50:35 +0530 Subject: [PATCH 3/6] chore(tally): reseal F4X N1.1 compatibility surface Carry the N1.1 document-boundary surface over the F4X BILLREF presentation disclosure. Claims, evidence, and trusted-evidence keys remain byte-identical to the preserved N1.1 head. Compatibility gate: exit 0, unknown_claims=11, evidenced_claims=0. Claims/evidence/trusted identity checks: exit 0. --- docs/tally/compatibility/compatibility-matrix.json | 2 +- docs/tally/compatibility/compatibility-surface.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tally/compatibility/compatibility-matrix.json b/docs/tally/compatibility/compatibility-matrix.json index c1b1cc3..15bf66c 100644 --- a/docs/tally/compatibility/compatibility-matrix.json +++ b/docs/tally/compatibility/compatibility-matrix.json @@ -1,7 +1,7 @@ { "schema_version": 1, "bridge_commit_sha": "be1c20cc3fd66fa1ece196505c69f26e555e4b8e", - "compatibility_surface_sha256": "6b8b8ab6ed23d0e70d44b79bf497e23a1ebc32422911b19cdc213cac7dcd6c82", + "compatibility_surface_sha256": "4efcf134846a487fdc755d514187ba0347f70c35f030730aa667eff56d5f8488", "claims": [ { "claim_id": "erp9-6-6-3-windows-education-xml-one-company", diff --git a/docs/tally/compatibility/compatibility-surface.json b/docs/tally/compatibility/compatibility-surface.json index 6973826..c2ac757 100644 --- a/docs/tally/compatibility/compatibility-surface.json +++ b/docs/tally/compatibility/compatibility-surface.json @@ -339,7 +339,7 @@ }, { "path": "src-tauri/src/tally/runtime.rs", - "sha256": "4a4cb2bc46cfcadbb082550c8f24e735d8bfb9206f524d6bee4db8bbf69ef95d" + "sha256": "f6c4a1507b00c2689c128be4991af14af7cbd3ad2cec684673bebff9bb24a3c9" }, { "path": "src-tauri/src/tally/serial_queue.rs", @@ -434,5 +434,5 @@ "sha256": "a27f294ee15e407b69fdfc73609e8708ac0509b6e6a8872daef5451fde61a8db" } ], - "manifest_sha256": "" + "manifest_sha256": "4efcf134846a487fdc755d514187ba0347f70c35f030730aa667eff56d5f8488" } From 8eea85d5ced257cfcebcf11e634a031b94d26289 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Tue, 18 Aug 2026 11:57:16 +0530 Subject: [PATCH 4/6] chore(tally): reseal F5X N1.1 compatibility surface --- .../compatibility/compatibility-matrix.json | 2 +- .../compatibility/compatibility-surface.json | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/tally/compatibility/compatibility-matrix.json b/docs/tally/compatibility/compatibility-matrix.json index 15bf66c..7b9c7a4 100644 --- a/docs/tally/compatibility/compatibility-matrix.json +++ b/docs/tally/compatibility/compatibility-matrix.json @@ -1,7 +1,7 @@ { "schema_version": 1, "bridge_commit_sha": "be1c20cc3fd66fa1ece196505c69f26e555e4b8e", - "compatibility_surface_sha256": "4efcf134846a487fdc755d514187ba0347f70c35f030730aa667eff56d5f8488", + "compatibility_surface_sha256": "08564aa27c09289a75998c3a21d841aeb4eae521a71220225afada1eea4fe2da", "claims": [ { "claim_id": "erp9-6-6-3-windows-education-xml-one-company", diff --git a/docs/tally/compatibility/compatibility-surface.json b/docs/tally/compatibility/compatibility-surface.json index c2ac757..4470d59 100644 --- a/docs/tally/compatibility/compatibility-surface.json +++ b/docs/tally/compatibility/compatibility-surface.json @@ -63,7 +63,7 @@ }, { "path": "docs/tally/compatibility/synthetic-write-canary-fixture.md", - "sha256": "dd2f1c68c0925523af1468dd9c61330433130c0e72b7c713dfc1ef9205b4756f" + "sha256": "9ed85e60adb7306f11496b47f5a86d49327abfdd8e2735678c521187b9ce76e4" }, { "path": "docs/tally/support-matrix.md", @@ -79,7 +79,7 @@ }, { "path": "scripts/outstandings-copy.test.mjs", - "sha256": "58ca3cb255a8e54dc3a4590146b908b800fd4186d474eb05c3e1a46ae6c005a0" + "sha256": "8c9adb100a45704ca8ba72203a397defca3e8b01a297e96043c543cc052b980a" }, { "path": "scripts/tally-company-selection.test.mjs", @@ -143,7 +143,7 @@ }, { "path": "src-tauri/crates/bridge-tally-protocol/src/lib.rs", - "sha256": "c5c61049fdbedf31cfafb43d349961e736eb7c6a361ec08e410f2f94099b1200" + "sha256": "e1c9082a214a125454c2bbddef8494283d41efa025e4acfc1525f0a22aa2bd1e" }, { "path": "src-tauri/crates/bridge-tally-protocol/src/outstandings/completeness.rs", @@ -175,7 +175,7 @@ }, { "path": "src-tauri/crates/bridge-tally-protocol/tests/simulator_corpus.rs", - "sha256": "65da6e2a0cf543c46e1834d82f836b2966f9f574952b991259179cf743f907d6" + "sha256": "616eb8fa5e387bff74f62d8e775b7eb118860763b18ff63ad88ad5015f7f752f" }, { "path": "src-tauri/crates/bridge-tally-protocol/tests/stream_text_decoder.rs", @@ -219,7 +219,7 @@ }, { "path": "src-tauri/src/commands.rs", - "sha256": "e9e817e00ca33349d1735bce92e8c750685971d244f09f7912d1018eb37fead0" + "sha256": "8d13e54b487bf27984ed988abee126d90e63a21a72d12aa21a819796bff4c569" }, { "path": "src-tauri/src/db/encrypted.rs", @@ -339,7 +339,7 @@ }, { "path": "src-tauri/src/tally/runtime.rs", - "sha256": "f6c4a1507b00c2689c128be4991af14af7cbd3ad2cec684673bebff9bb24a3c9" + "sha256": "ac652a70cd3b0b7184733ea08f6d8967f5b25f357d171d96a5c6d96f818d0563" }, { "path": "src-tauri/src/tally/serial_queue.rs", @@ -359,7 +359,7 @@ }, { "path": "src/OutstandingsScreen.tsx", - "sha256": "479aff297f63050a4903154e565062f774057bec2b9c452b81c5934782f9ea5e" + "sha256": "6835d23a31eb92e1037e8f1e9c268ea86c59554a6b7060009e4dfa086bfa03d6" }, { "path": "src/TallyReadinessFlow.tsx", @@ -371,7 +371,7 @@ }, { "path": "src/outstandings-copy.ts", - "sha256": "0e78dd97d79c0166fe17a4e933733c369a7e4be3046ddbe3e38122ad5e234216" + "sha256": "a2bcf820f694443ac122e91aa98fb354b6e2d5c6be3e1e9503b368a3a0a4f696" }, { "path": "src/outstandings-csv.ts", @@ -434,5 +434,5 @@ "sha256": "a27f294ee15e407b69fdfc73609e8708ac0509b6e6a8872daef5451fde61a8db" } ], - "manifest_sha256": "4efcf134846a487fdc755d514187ba0347f70c35f030730aa667eff56d5f8488" + "manifest_sha256": "08564aa27c09289a75998c3a21d841aeb4eae521a71220225afada1eea4fe2da" } From 539e569220856f4dce95738f979281ca7282aff4 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Tue, 18 Aug 2026 12:10:58 +0530 Subject: [PATCH 5/6] chore(tally): reseal F5X N11 after clippy repair --- docs/tally/compatibility/compatibility-matrix.json | 2 +- docs/tally/compatibility/compatibility-surface.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tally/compatibility/compatibility-matrix.json b/docs/tally/compatibility/compatibility-matrix.json index 7b9c7a4..5ef9ed8 100644 --- a/docs/tally/compatibility/compatibility-matrix.json +++ b/docs/tally/compatibility/compatibility-matrix.json @@ -1,7 +1,7 @@ { "schema_version": 1, "bridge_commit_sha": "be1c20cc3fd66fa1ece196505c69f26e555e4b8e", - "compatibility_surface_sha256": "08564aa27c09289a75998c3a21d841aeb4eae521a71220225afada1eea4fe2da", + "compatibility_surface_sha256": "e10c61655eb46c2b66ee9906824b6d708fb4303cd88f1c2e3b1bc4cf0b25e1b5", "claims": [ { "claim_id": "erp9-6-6-3-windows-education-xml-one-company", diff --git a/docs/tally/compatibility/compatibility-surface.json b/docs/tally/compatibility/compatibility-surface.json index 4470d59..9afdc5e 100644 --- a/docs/tally/compatibility/compatibility-surface.json +++ b/docs/tally/compatibility/compatibility-surface.json @@ -339,7 +339,7 @@ }, { "path": "src-tauri/src/tally/runtime.rs", - "sha256": "ac652a70cd3b0b7184733ea08f6d8967f5b25f357d171d96a5c6d96f818d0563" + "sha256": "893bf325dcd614d49ac661127333c0cf714a937776c609a442fbccc240d06da1" }, { "path": "src-tauri/src/tally/serial_queue.rs", @@ -434,5 +434,5 @@ "sha256": "a27f294ee15e407b69fdfc73609e8708ac0509b6e6a8872daef5451fde61a8db" } ], - "manifest_sha256": "08564aa27c09289a75998c3a21d841aeb4eae521a71220225afada1eea4fe2da" + "manifest_sha256": "e10c61655eb46c2b66ee9906824b6d708fb4303cd88f1c2e3b1bc4cf0b25e1b5" } From aaa855c9a09316af81bf394d7bb4a0368f773351 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Tue, 18 Aug 2026 12:41:03 +0530 Subject: [PATCH 6/6] chore(tally): reseal F5X N11 after PR7 compile repair --- docs/tally/compatibility/compatibility-matrix.json | 2 +- docs/tally/compatibility/compatibility-surface.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tally/compatibility/compatibility-matrix.json b/docs/tally/compatibility/compatibility-matrix.json index 5ef9ed8..1d9f51e 100644 --- a/docs/tally/compatibility/compatibility-matrix.json +++ b/docs/tally/compatibility/compatibility-matrix.json @@ -1,7 +1,7 @@ { "schema_version": 1, "bridge_commit_sha": "be1c20cc3fd66fa1ece196505c69f26e555e4b8e", - "compatibility_surface_sha256": "e10c61655eb46c2b66ee9906824b6d708fb4303cd88f1c2e3b1bc4cf0b25e1b5", + "compatibility_surface_sha256": "95e55d137ec3ef4b4de570dc0dab554bc926ca75435284342629312ed2d7725e", "claims": [ { "claim_id": "erp9-6-6-3-windows-education-xml-one-company", diff --git a/docs/tally/compatibility/compatibility-surface.json b/docs/tally/compatibility/compatibility-surface.json index 9afdc5e..3391efd 100644 --- a/docs/tally/compatibility/compatibility-surface.json +++ b/docs/tally/compatibility/compatibility-surface.json @@ -339,7 +339,7 @@ }, { "path": "src-tauri/src/tally/runtime.rs", - "sha256": "893bf325dcd614d49ac661127333c0cf714a937776c609a442fbccc240d06da1" + "sha256": "1d2ae890a240344ad9a2a59b4624be2726d13c11e5e529a0b084e355f865deec" }, { "path": "src-tauri/src/tally/serial_queue.rs", @@ -434,5 +434,5 @@ "sha256": "a27f294ee15e407b69fdfc73609e8708ac0509b6e6a8872daef5451fde61a8db" } ], - "manifest_sha256": "e10c61655eb46c2b66ee9906824b6d708fb4303cd88f1c2e3b1bc4cf0b25e1b5" + "manifest_sha256": "95e55d137ec3ef4b4de570dc0dab554bc926ca75435284342629312ed2d7725e" }