diff --git a/README.md b/README.md index 5c0dd08..56f7016 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # HackBrowserData -[![Lint](https://github.com/moonD4rk/HackBrowserData/actions/workflows/lint.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/lint.yml) [![Build](https://github.com/moonD4rk/HackBrowserData/actions/workflows/build.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/build.yml) [![Release](https://github.com/moonD4rk/HackBrowserData/actions/workflows/release.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/release.yml) [![Tests](https://github.com/moonD4rk/HackBrowserData/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/test.yml) [![Coverage Status](https://coveralls.io/repos/github/moonD4rk/HackBrowserData/badge.svg)](https://coveralls.io/github/moonD4rk/HackBrowserData) +[![Lint](https://github.com/moonD4rk/HackBrowserData/actions/workflows/lint.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/lint.yml) [![Build](https://github.com/moonD4rk/HackBrowserData/actions/workflows/build.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/build.yml) [![Release](https://github.com/moonD4rk/HackBrowserData/actions/workflows/release.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/release.yml) [![Tests](https://github.com/moonD4rk/HackBrowserData/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/moonD4rk/HackBrowserData/branch/main/graph/badge.svg?token=KWJCN38657)](https://codecov.io/gh/moonD4rk/HackBrowserData) `HackBrowserData` is a command-line tool for decrypting and exporting browser data (passwords, history, cookies, bookmarks, credit cards, download history, localStorage and extensions) from the browser. It supports the most popular browsers on the market and runs on Windows, macOS and Linux. diff --git a/browserdata/browser_data.go b/browserdata/browser_data.go new file mode 100644 index 0000000..d1d4a96 --- /dev/null +++ b/browserdata/browser_data.go @@ -0,0 +1,18 @@ +package browserdata + +import "github.com/moond4rk/hackbrowserdata/types" + +// Data holds all extracted data from one browser profile. +// Each field is a slice that may be nil (not supported) or empty (no data found). +// This struct will replace the current BrowserData once the refactoring is complete. +type Data struct { + Passwords []types.LoginEntry `json:"passwords,omitempty"` + Cookies []types.CookieEntry `json:"cookies,omitempty"` + Bookmarks []types.BookmarkEntry `json:"bookmarks,omitempty"` + Histories []types.HistoryEntry `json:"histories,omitempty"` + Downloads []types.DownloadEntry `json:"downloads,omitempty"` + CreditCards []types.CreditCardEntry `json:"credit_cards,omitempty"` + Extensions []types.ExtensionEntry `json:"extensions,omitempty"` + LocalStorage []types.StorageEntry `json:"local_storage,omitempty"` + SessionStorage []types.StorageEntry `json:"session_storage,omitempty"` +} diff --git a/types/category.go b/types/category.go new file mode 100644 index 0000000..6cedf34 --- /dev/null +++ b/types/category.go @@ -0,0 +1,71 @@ +package types + +// Category represents a kind of browser data. +// It is browser-agnostic — a password is a password regardless of which browser it came from. +type Category int + +const ( + Password Category = iota + Cookie + Bookmark + History + Download + CreditCard + Extension + LocalStorage + SessionStorage +) + +// AllCategories returns all supported data categories. +var AllCategories = []Category{ + Password, Cookie, Bookmark, History, Download, + CreditCard, Extension, LocalStorage, SessionStorage, +} + +// String returns the human-readable name of the category. +func (c Category) String() string { + switch c { + case Password: + return "password" + case Cookie: + return "cookie" + case Bookmark: + return "bookmark" + case History: + return "history" + case Download: + return "download" + case CreditCard: + return "creditcard" + case Extension: + return "extension" + case LocalStorage: + return "localstorage" + case SessionStorage: + return "sessionstorage" + default: + return "unknown" + } +} + +// IsSensitive returns whether the category contains sensitive data +// that requires explicit opt-in to export. +func (c Category) IsSensitive() bool { + switch c { + case Password, Cookie, CreditCard: + return true + default: + return false + } +} + +// NonSensitiveCategories returns categories that are safe to export by default. +func NonSensitiveCategories() []Category { + var cats []Category + for _, c := range AllCategories { + if !c.IsSensitive() { + cats = append(cats, c) + } + } + return cats +} diff --git a/types/category_test.go b/types/category_test.go new file mode 100644 index 0000000..af64f69 --- /dev/null +++ b/types/category_test.go @@ -0,0 +1,52 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCategory_String(t *testing.T) { + tests := []struct { + cat Category + want string + }{ + {Password, "password"}, + {Cookie, "cookie"}, + {Bookmark, "bookmark"}, + {History, "history"}, + {Download, "download"}, + {CreditCard, "creditcard"}, + {Extension, "extension"}, + {LocalStorage, "localstorage"}, + {SessionStorage, "sessionstorage"}, + {Category(999), "unknown"}, + } + for _, tt := range tests { + assert.Equal(t, tt.want, tt.cat.String()) + } +} + +func TestCategory_IsSensitive(t *testing.T) { + sensitive := []Category{Password, Cookie, CreditCard} + for _, c := range sensitive { + assert.True(t, c.IsSensitive(), "%s should be sensitive", c) + } + + notSensitive := []Category{Bookmark, History, Download, Extension, LocalStorage, SessionStorage} + for _, c := range notSensitive { + assert.False(t, c.IsSensitive(), "%s should not be sensitive", c) + } +} + +func TestAllCategories(t *testing.T) { + assert.Len(t, AllCategories, 9) +} + +func TestNonSensitiveCategories(t *testing.T) { + cats := NonSensitiveCategories() + assert.Len(t, cats, 6) + for _, c := range cats { + assert.False(t, c.IsSensitive()) + } +} diff --git a/types/models.go b/types/models.go new file mode 100644 index 0000000..0406f8a --- /dev/null +++ b/types/models.go @@ -0,0 +1,71 @@ +package types + +import "time" + +// LoginEntry represents a single saved login credential. +type LoginEntry struct { + URL string `json:"url" csv:"url"` + Username string `json:"username" csv:"username"` + Password string `json:"password" csv:"password"` + CreatedAt time.Time `json:"created_at" csv:"created_at"` +} + +// CookieEntry represents a single browser cookie. +type CookieEntry struct { + Host string `json:"host" csv:"host"` + Path string `json:"path" csv:"path"` + Name string `json:"name" csv:"name"` + Value string `json:"value" csv:"value"` + IsSecure bool `json:"is_secure" csv:"is_secure"` + IsHTTPOnly bool `json:"is_http_only" csv:"is_http_only"` + ExpireAt time.Time `json:"expire_at" csv:"expire_at"` + CreatedAt time.Time `json:"created_at" csv:"created_at"` +} + +// BookmarkEntry represents a single browser bookmark. +type BookmarkEntry struct { + Name string `json:"name" csv:"name"` + URL string `json:"url" csv:"url"` + Folder string `json:"folder" csv:"folder"` + CreatedAt time.Time `json:"created_at" csv:"created_at"` +} + +// HistoryEntry represents a single browser history record. +type HistoryEntry struct { + URL string `json:"url" csv:"url"` + Title string `json:"title" csv:"title"` + VisitCount int `json:"visit_count" csv:"visit_count"` + LastVisit time.Time `json:"last_visit" csv:"last_visit"` +} + +// DownloadEntry represents a single browser download record. +type DownloadEntry struct { + URL string `json:"url" csv:"url"` + TargetPath string `json:"target_path" csv:"target_path"` + TotalBytes int64 `json:"total_bytes" csv:"total_bytes"` + StartTime time.Time `json:"start_time" csv:"start_time"` + EndTime time.Time `json:"end_time" csv:"end_time"` +} + +// CreditCardEntry represents a single saved credit card. +type CreditCardEntry struct { + Name string `json:"name" csv:"name"` + Number string `json:"number" csv:"number"` + ExpMonth string `json:"exp_month" csv:"exp_month"` + ExpYear string `json:"exp_year" csv:"exp_year"` +} + +// StorageEntry represents a single key-value pair from local or session storage. +type StorageEntry struct { + URL string `json:"url" csv:"url"` + Key string `json:"key" csv:"key"` + Value string `json:"value" csv:"value"` +} + +// ExtensionEntry represents a single browser extension. +type ExtensionEntry struct { + Name string `json:"name" csv:"name"` + ID string `json:"id" csv:"id"` + Description string `json:"description" csv:"description"` + Version string `json:"version" csv:"version"` +}