mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-05-19 18:58:03 +02:00
d105a1f488
* feat: add Safari bookmark and download extraction from plist files * test: add nested folder test for bookmark tree traversal Part of #565
180 lines
4.6 KiB
Go
180 lines
4.6 KiB
Go
package safari
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/moond4rk/plist"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func buildTestBookmarksPlist(t *testing.T, root safariBookmark) string {
|
|
t.Helper()
|
|
path := filepath.Join(t.TempDir(), "Bookmarks.plist")
|
|
f, err := os.Create(path)
|
|
require.NoError(t, err)
|
|
defer f.Close()
|
|
require.NoError(t, plist.NewBinaryEncoder(f).Encode(root))
|
|
return path
|
|
}
|
|
|
|
func TestExtractBookmarks(t *testing.T) {
|
|
root := safariBookmark{
|
|
Type: bookmarkTypeList,
|
|
Children: []safariBookmark{
|
|
{
|
|
Type: bookmarkTypeList,
|
|
Title: "BookmarksBar",
|
|
Children: []safariBookmark{
|
|
{
|
|
Type: bookmarkTypeLeaf,
|
|
URLString: "https://github.com",
|
|
URIDictionary: uriDictionary{Title: "GitHub"},
|
|
},
|
|
{
|
|
Type: bookmarkTypeLeaf,
|
|
URLString: "https://go.dev",
|
|
URIDictionary: uriDictionary{Title: "Go"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: bookmarkTypeList,
|
|
Title: "BookmarksMenu",
|
|
Children: []safariBookmark{
|
|
{
|
|
Type: bookmarkTypeLeaf,
|
|
URLString: "https://example.com",
|
|
URIDictionary: uriDictionary{Title: "Example"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
path := buildTestBookmarksPlist(t, root)
|
|
bookmarks, err := extractBookmarks(path)
|
|
require.NoError(t, err)
|
|
require.Len(t, bookmarks, 3)
|
|
|
|
// Verify folder assignment
|
|
assert.Equal(t, "GitHub", bookmarks[0].Name)
|
|
assert.Equal(t, "https://github.com", bookmarks[0].URL)
|
|
assert.Equal(t, "BookmarksBar", bookmarks[0].Folder)
|
|
|
|
assert.Equal(t, "Go", bookmarks[1].Name)
|
|
assert.Equal(t, "BookmarksBar", bookmarks[1].Folder)
|
|
|
|
assert.Equal(t, "Example", bookmarks[2].Name)
|
|
assert.Equal(t, "BookmarksMenu", bookmarks[2].Folder)
|
|
}
|
|
|
|
func TestExtractBookmarks_ReadingList(t *testing.T) {
|
|
root := safariBookmark{
|
|
Type: bookmarkTypeList,
|
|
Children: []safariBookmark{
|
|
{
|
|
Type: bookmarkTypeList,
|
|
Title: "com.apple.ReadingList",
|
|
Children: []safariBookmark{
|
|
{
|
|
Type: bookmarkTypeLeaf,
|
|
URLString: "https://blog.example.com/post",
|
|
URIDictionary: uriDictionary{Title: "Blog Post"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
path := buildTestBookmarksPlist(t, root)
|
|
bookmarks, err := extractBookmarks(path)
|
|
require.NoError(t, err)
|
|
require.Len(t, bookmarks, 1)
|
|
assert.Equal(t, "ReadingList", bookmarks[0].Folder)
|
|
}
|
|
|
|
func TestExtractBookmarks_SkipsEmptyURL(t *testing.T) {
|
|
root := safariBookmark{
|
|
Type: bookmarkTypeList,
|
|
Children: []safariBookmark{
|
|
{
|
|
Type: bookmarkTypeLeaf,
|
|
URLString: "", // no URL, should be skipped
|
|
URIDictionary: uriDictionary{Title: "Empty"},
|
|
},
|
|
{
|
|
Type: bookmarkTypeLeaf,
|
|
URLString: "https://valid.com",
|
|
URIDictionary: uriDictionary{Title: "Valid"},
|
|
},
|
|
},
|
|
}
|
|
|
|
path := buildTestBookmarksPlist(t, root)
|
|
bookmarks, err := extractBookmarks(path)
|
|
require.NoError(t, err)
|
|
require.Len(t, bookmarks, 1)
|
|
assert.Equal(t, "Valid", bookmarks[0].Name)
|
|
}
|
|
|
|
func TestExtractBookmarks_NestedFolders(t *testing.T) {
|
|
root := safariBookmark{
|
|
Type: bookmarkTypeList,
|
|
Children: []safariBookmark{
|
|
{
|
|
Type: bookmarkTypeList,
|
|
Title: "Work",
|
|
Children: []safariBookmark{
|
|
{
|
|
Type: bookmarkTypeList,
|
|
Title: "Projects",
|
|
Children: []safariBookmark{
|
|
{Type: bookmarkTypeLeaf, URLString: "https://deep.com", URIDictionary: uriDictionary{Title: "Deep"}},
|
|
},
|
|
},
|
|
{Type: bookmarkTypeLeaf, URLString: "https://shallow.com", URIDictionary: uriDictionary{Title: "Shallow"}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
path := buildTestBookmarksPlist(t, root)
|
|
bookmarks, err := extractBookmarks(path)
|
|
require.NoError(t, err)
|
|
require.Len(t, bookmarks, 2)
|
|
|
|
// Nested leaf gets the immediate parent folder name
|
|
assert.Equal(t, "Deep", bookmarks[0].Name)
|
|
assert.Equal(t, "Projects", bookmarks[0].Folder)
|
|
|
|
assert.Equal(t, "Shallow", bookmarks[1].Name)
|
|
assert.Equal(t, "Work", bookmarks[1].Folder)
|
|
}
|
|
|
|
func TestCountBookmarks(t *testing.T) {
|
|
root := safariBookmark{
|
|
Type: bookmarkTypeList,
|
|
Children: []safariBookmark{
|
|
{Type: bookmarkTypeLeaf, URLString: "https://a.com", URIDictionary: uriDictionary{Title: "A"}},
|
|
{Type: bookmarkTypeLeaf, URLString: "https://b.com", URIDictionary: uriDictionary{Title: "B"}},
|
|
},
|
|
}
|
|
|
|
path := buildTestBookmarksPlist(t, root)
|
|
count, err := countBookmarks(path)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, count)
|
|
}
|
|
|
|
func TestExtractBookmarks_Empty(t *testing.T) {
|
|
root := safariBookmark{Type: bookmarkTypeList}
|
|
path := buildTestBookmarksPlist(t, root)
|
|
|
|
bookmarks, err := extractBookmarks(path)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, bookmarks)
|
|
}
|