package notes import ( "os" "path/filepath" "testing" ) func TestAssertContained(t *testing.T) { tests := []struct { name string root string target string wantErr bool }{ { name: "file inside root", root: "/tmp/vault/Notes", target: "/tmp/vault/Notes/test.md", wantErr: false, }, { name: "file at root boundary", root: "/tmp/vault/Notes", target: "/tmp/vault/Notes", wantErr: false, }, { name: "path traversal via ..", root: "/tmp/vault/Notes", target: "/tmp/vault/Notes/../../../etc/passwd", wantErr: true, }, { name: "path traversal to parent", root: "/tmp/vault/Notes", target: "/tmp/vault/other/file.md", wantErr: true, }, { name: "completely different path", root: "/tmp/vault/Notes", target: "/etc/passwd", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { root := filepath.Clean(tt.root) target := filepath.Clean(tt.target) err := assertContained(root, target) if (err != nil) != tt.wantErr { t.Errorf("assertContained(%q, %q) error = %v, wantErr %v", root, target, err, tt.wantErr) } }) } } func TestAssertContainedSymlinkEscape(t *testing.T) { dir := t.TempDir() // Create a real directory structure. notesDir := filepath.Join(dir, "vault", "Notes") os.MkdirAll(notesDir, 0o750) // Create a directory outside the vault. outsideDir := filepath.Join(dir, "outside") os.MkdirAll(outsideDir, 0o750) // Create a file outside the vault. outsideFile := filepath.Join(outsideDir, "secret.md") os.WriteFile(outsideFile, []byte("secret"), 0o640) // Create a symlink inside Notes/ pointing outside. symlinkPath := filepath.Join(notesDir, "escape.md") if err := os.Symlink(outsideFile, symlinkPath); err != nil { t.Skipf("cannot create symlink: %v", err) } // assertContained should detect the symlink escape. err := assertContained(notesDir, symlinkPath) if err == nil { t.Error("expected error for symlink escape, got nil") } } func TestAssertContainedNonExistentPath(t *testing.T) { dir := t.TempDir() notesDir := filepath.Join(dir, "vault", "Notes") os.MkdirAll(notesDir, 0o750) // Non-existent file inside root — should pass. err := assertContained(notesDir, filepath.Join(notesDir, "newfile.md")) if err != nil { t.Errorf("expected no error for non-existent file inside root: %v", err) } // Non-existent file outside root — should fail. err = assertContained(notesDir, filepath.Join(dir, "outside", "file.md")) if err == nil { t.Error("expected error for non-existent file outside root") } } func TestNoteFileRoot(t *testing.T) { tests := []struct { name string vaultRoot string parentFsPath string want string }{ { name: "parentless note", vaultRoot: "/tmp/vault", parentFsPath: "", want: "/tmp/vault/Notes", }, { name: "note inside project", vaultRoot: "/tmp/vault", parentFsPath: "Projects/MyProject", want: "/tmp/vault/Projects/MyProject/Notes", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := noteFileRoot(tt.vaultRoot, tt.parentFsPath) got = filepath.Clean(got) want := filepath.Clean(tt.want) if got != want { t.Errorf("noteFileRoot(%q, %q) = %q, want %q", tt.vaultRoot, tt.parentFsPath, got, want) } }) } }