From bc56eec35e682f321e6ab7c823125a3c5e814389 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Thu, 30 Apr 2026 05:37:58 +0200 Subject: [PATCH] fix: block frontmatter writes for attachments --- src-tauri/src/frontmatter/mod.rs | 46 +++++++++++++++++++++++++++++-- src/components/Inspector.test.tsx | 23 ++++++++++++++++ src/components/Inspector.tsx | 42 ++++++++++++++++------------ 3 files changed, 90 insertions(+), 21 deletions(-) diff --git a/src-tauri/src/frontmatter/mod.rs b/src-tauri/src/frontmatter/mod.rs index 8b9d4598..c0c99f59 100644 --- a/src-tauri/src/frontmatter/mod.rs +++ b/src-tauri/src/frontmatter/mod.rs @@ -9,15 +9,36 @@ use std::path::Path; pub use ops::update_frontmatter_content; pub use yaml::{format_yaml_key, FrontmatterValue}; +fn is_markdown_path(path: &Path) -> bool { + path.extension() + .and_then(|extension| extension.to_str()) + .is_some_and(|extension| { + extension.eq_ignore_ascii_case("md") || extension.eq_ignore_ascii_case("markdown") + }) +} + +fn validate_frontmatter_path(path: &str, file_path: &Path) -> Result<(), String> { + if !file_path.exists() { + return Err(format!("File does not exist: {}", path)); + } + + if !is_markdown_path(file_path) { + return Err(format!( + "Frontmatter can only be updated on Markdown notes: {}", + path + )); + } + + Ok(()) +} + /// Helper to read a file, apply a frontmatter transformation, and write back. pub fn with_frontmatter(path: &str, transform: F) -> Result where F: FnOnce(&str) -> Result, { let file_path = Path::new(path); - if !file_path.exists() { - return Err(format!("File does not exist: {}", path)); - } + validate_frontmatter_path(path, file_path)?; let content = fs::read_to_string(file_path).map_err(|e| format!("Failed to read {}: {}", path, e))?; @@ -58,6 +79,25 @@ mod tests { assert!(result.unwrap_err().contains("does not exist")); } + #[test] + fn test_update_frontmatter_rejects_binary_attachment_before_utf8_read() { + let dir = tempfile::tempdir().unwrap(); + let attachment_dir = dir.path().join("attachments"); + fs::create_dir_all(&attachment_dir).unwrap(); + let attachment_path = attachment_dir.join("screenshot.png"); + fs::write(&attachment_path, [0xff, 0xfe, 0xfd]).unwrap(); + + let err = update_frontmatter( + attachment_path.to_str().unwrap(), + "Status", + FrontmatterValue::String("Done".to_string()), + ) + .unwrap_err(); + + assert!(err.contains("Frontmatter can only be updated on Markdown notes")); + assert!(err.contains("screenshot.png")); + } + #[test] fn test_roundtrip_update_string() { let content = "---\nStatus: Draft\n---\n# Test\n"; diff --git a/src/components/Inspector.test.tsx b/src/components/Inspector.test.tsx index 39a6b52d..ddbc76df 100644 --- a/src/components/Inspector.test.tsx +++ b/src/components/Inspector.test.tsx @@ -634,6 +634,29 @@ Status: Active expect(onInit).toHaveBeenCalledWith('/vault/plain-note.md') }) + it('does not offer frontmatter initialization for binary attachments', () => { + const onInit = vi.fn() + const attachmentEntry: VaultEntry = { + ...noFrontmatterEntry, + path: '/vault/attachments/screenshot.png', + filename: 'screenshot.png', + title: 'screenshot.png', + fileKind: 'binary', + } + + render( + + ) + + expect(screen.queryByText('Initialize properties')).not.toBeInTheDocument() + expect(onInit).not.toHaveBeenCalled() + }) + it('shows invalid frontmatter notice with fix button', () => { render( { return map } +function supportsFrontmatter(entry: VaultEntry): boolean { + return entry.fileKind === undefined || entry.fileKind === 'markdown' +} + function ValidFrontmatterPanels({ entry, entries, @@ -212,24 +216,26 @@ function InspectorBody({ return ( <> - + {supportsFrontmatter(entry) && ( + + )} {backlinks.length > 0 && }