138 lines
3.3 KiB
Go
138 lines
3.3 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|