feat: add types.Category, data models, and browserdata.Data (#512)

* feat: add new types.Category, data models, and browserdata.Data

Phase 1 of architecture refactoring (RFC-001/RFC-002):

- types/category.go: Category enum (9 values) replacing DataType (22 values)
  with String(), IsSensitive(), AllCategories, NonSensitiveCategories()
- types/models.go: browser-agnostic data models (LoginEntry, CookieEntry,
  BookmarkEntry, HistoryEntry, DownloadEntry, CreditCardEntry, StorageEntry,
  ExtensionEntry) — no encrypted fields, no browser prefixes
- types/category_test.go: tests for Category methods
- browserdata/browser_data.go: new Data struct with typed slices,
  coexists with old BrowserData during migration

* docs: replace Coveralls badge with Codecov in README

* fix: apply review suggestions (is_http_only tag, json tags on Data)
This commit is contained in:
Roger
2026-03-23 02:30:42 +08:00
committed by moonD4rk
parent c493804ede
commit b680d43caa
5 changed files with 213 additions and 1 deletions
+1 -1
View File
@@ -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.
+18
View File
@@ -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"`
}
+71
View File
@@ -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
}
+52
View File
@@ -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())
}
}
+71
View File
@@ -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"`
}