mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-07-04 21:37:47 +02:00
feat: add Firefox extract methods and complete data model fields (#527)
* feat: add Firefox extract methods and complete data model fields Firefox extract methods: - extractPasswords: JSON + ASN1PBE decryption via decryptPBE helper - extractCookies: SQLite, plaintext (no encryption), journalOff - extractHistories: SQLite, visit count ASC sort (matches old behavior) - extractDownloads: SQLite, moz_annos JOIN with JSON content parsing - extractBookmarks: SQLite, moz_bookmarks JOIN moz_places - extractExtensions: JSON, filter by location=app-profile - extractLocalStorage: SQLite webappsstore2, reversed originKey parsing Complete data model fields (union of Chromium and Firefox): - CookieEntry: add HasExpire, IsPersistent - DownloadEntry: add MimeType - CreditCardEntry: add NickName, Address - ExtensionEntry: add HomepageURL, Enabled Update Chromium extractors to populate new fields: - extract_cookie.go: fill HasExpire, IsPersistent - extract_download.go: SELECT and fill mime_type - extract_creditcard.go: SELECT nickname, billing_address_id - extract_extension.go: fill HomepageURL, Enabled (state==1) Tests: - Full test coverage for all 7 Firefox extract functions - Password test uses known ASN1PBE test vectors from crypto package - Table-driven tests for parseOriginKey - Updated Chromium tests for new fields * fix: add COALESCE for nullable bookmark title in Firefox query Firefox moz_bookmarks.title can be NULL (PR #500 fixed this in old code). Add COALESCE to handle NULL gracefully in SQL instead of relying on driver-specific NULL→string conversion behavior. * fix: enable journalOff for all Firefox SQLite extractors and populate cookie flags - Set journalOff=true for extract_history, extract_download, extract_bookmark (Firefox databases require PRAGMA journal_mode=off to avoid lock errors) - Populate HasExpire and IsPersistent for Firefox cookies (derived from expiry>0) - Add test assertions for HasExpire/IsPersistent in both Chromium and Firefox
This commit is contained in:
@@ -34,14 +34,16 @@ func extractCookies(masterKey []byte, path string) ([]types.CookieEntry, error)
|
||||
value, _ := decryptValue(masterKey, encryptedValue)
|
||||
value = stripCookieHash(value, host)
|
||||
return types.CookieEntry{
|
||||
Name: name,
|
||||
Host: host,
|
||||
Path: cookiePath,
|
||||
Value: string(value),
|
||||
IsSecure: isSecure != 0,
|
||||
IsHTTPOnly: isHTTPOnly != 0,
|
||||
ExpireAt: typeutil.TimeEpoch(expireAt),
|
||||
CreatedAt: typeutil.TimeEpoch(createdAt),
|
||||
Name: name,
|
||||
Host: host,
|
||||
Path: cookiePath,
|
||||
Value: string(value),
|
||||
IsSecure: isSecure != 0,
|
||||
IsHTTPOnly: isHTTPOnly != 0,
|
||||
HasExpire: hasExpire != 0,
|
||||
IsPersistent: isPersistent != 0,
|
||||
ExpireAt: typeutil.TimeEpoch(expireAt),
|
||||
CreatedAt: typeutil.TimeEpoch(createdAt),
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -27,6 +27,8 @@ func TestExtractCookies(t *testing.T) {
|
||||
assert.Equal(t, "/api", got[0].Path)
|
||||
assert.True(t, got[0].IsSecure)
|
||||
assert.False(t, got[0].IsHTTPOnly)
|
||||
assert.True(t, got[0].HasExpire)
|
||||
assert.True(t, got[0].IsPersistent)
|
||||
assert.False(t, got[0].CreatedAt.IsZero())
|
||||
assert.True(t, got[0].ExpireAt.After(got[0].CreatedAt))
|
||||
assert.True(t, got[1].IsHTTPOnly)
|
||||
|
||||
@@ -8,14 +8,14 @@ import (
|
||||
)
|
||||
|
||||
const defaultCreditCardQuery = `SELECT name_on_card, expiration_month, expiration_year,
|
||||
card_number_encrypted FROM credit_cards`
|
||||
card_number_encrypted, COALESCE(nickname, ''), COALESCE(billing_address_id, '') FROM credit_cards`
|
||||
|
||||
func extractCreditCards(masterKey []byte, path string) ([]types.CreditCardEntry, error) {
|
||||
return sqliteutil.QueryRows(path, false, defaultCreditCardQuery,
|
||||
func(rows *sql.Rows) (types.CreditCardEntry, error) {
|
||||
var name, month, year string
|
||||
var name, month, year, nickName, address string
|
||||
var encNumber []byte
|
||||
if err := rows.Scan(&name, &month, &year, &encNumber); err != nil {
|
||||
if err := rows.Scan(&name, &month, &year, &encNumber, &nickName, &address); err != nil {
|
||||
return types.CreditCardEntry{}, err
|
||||
}
|
||||
number, _ := decryptValue(masterKey, encNumber)
|
||||
@@ -24,6 +24,8 @@ func extractCreditCards(masterKey []byte, path string) ([]types.CreditCardEntry,
|
||||
Number: string(number),
|
||||
ExpMonth: month,
|
||||
ExpYear: year,
|
||||
NickName: nickName,
|
||||
Address: address,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
func TestExtractCreditCards(t *testing.T) {
|
||||
path := createTestDB(t, "Web Data", creditCardsSchema,
|
||||
insertCreditCard("John Doe", 12, 2025, ""),
|
||||
insertCreditCard("Jane Smith", 6, 2027, ""),
|
||||
insertCreditCard("John Doe", 12, 2025, "", "Johnny", "addr-1"),
|
||||
insertCreditCard("Jane Smith", 6, 2027, "", "", ""),
|
||||
)
|
||||
|
||||
got, err := extractCreditCards(nil, path)
|
||||
|
||||
@@ -9,19 +9,21 @@ import (
|
||||
"github.com/moond4rk/hackbrowserdata/utils/typeutil"
|
||||
)
|
||||
|
||||
const defaultDownloadQuery = `SELECT target_path, tab_url, total_bytes, start_time, end_time FROM downloads`
|
||||
const defaultDownloadQuery = `SELECT target_path, tab_url, total_bytes, start_time, end_time,
|
||||
mime_type FROM downloads`
|
||||
|
||||
func extractDownloads(path string) ([]types.DownloadEntry, error) {
|
||||
downloads, err := sqliteutil.QueryRows(path, false, defaultDownloadQuery,
|
||||
func(rows *sql.Rows) (types.DownloadEntry, error) {
|
||||
var targetPath, url string
|
||||
var targetPath, url, mimeType string
|
||||
var totalBytes, startTime, endTime int64
|
||||
if err := rows.Scan(&targetPath, &url, &totalBytes, &startTime, &endTime); err != nil {
|
||||
if err := rows.Scan(&targetPath, &url, &totalBytes, &startTime, &endTime, &mimeType); err != nil {
|
||||
return types.DownloadEntry{}, err
|
||||
}
|
||||
return types.DownloadEntry{
|
||||
URL: url,
|
||||
TargetPath: targetPath,
|
||||
MimeType: mimeType,
|
||||
TotalBytes: totalBytes,
|
||||
StartTime: typeutil.TimeEpoch(startTime),
|
||||
EndTime: typeutil.TimeEpoch(endTime),
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
func TestExtractDownloads(t *testing.T) {
|
||||
path := createTestDB(t, "History", downloadsSchema,
|
||||
insertDownload("/tmp/old.zip", "https://old.com/file.zip", 1024, 13340000000000000, 13340000100000000),
|
||||
insertDownload("/tmp/new.pdf", "https://new.com/doc.pdf", 2048, 13360000000000000, 13360000200000000),
|
||||
insertDownload("/tmp/old.zip", "https://old.com/file.zip", "application/zip", 1024, 13340000000000000, 13340000100000000),
|
||||
insertDownload("/tmp/new.pdf", "https://new.com/doc.pdf", "application/pdf", 2048, 13360000000000000, 13360000200000000),
|
||||
)
|
||||
|
||||
got, err := extractDownloads(path)
|
||||
@@ -23,6 +23,7 @@ func TestExtractDownloads(t *testing.T) {
|
||||
|
||||
// Verify field mapping
|
||||
assert.Equal(t, "/tmp/new.pdf", got[0].TargetPath)
|
||||
assert.Equal(t, "application/pdf", got[0].MimeType)
|
||||
assert.Equal(t, int64(2048), got[0].TotalBytes)
|
||||
assert.False(t, got[0].StartTime.IsZero())
|
||||
assert.False(t, got[0].EndTime.IsZero())
|
||||
|
||||
@@ -51,6 +51,8 @@ func extractExtensions(path string) ([]types.ExtensionEntry, error) {
|
||||
ID: id.String(),
|
||||
Description: manifest.Get("description").String(),
|
||||
Version: manifest.Get("version").String(),
|
||||
HomepageURL: manifest.Get("homepage_url").String(),
|
||||
Enabled: ext.Get("state").Int() == 1,
|
||||
})
|
||||
return true
|
||||
})
|
||||
|
||||
@@ -173,22 +173,22 @@ func insertURL(url, title string, visitCount int, lastVisitTime int64) string {
|
||||
)
|
||||
}
|
||||
|
||||
func insertDownload(targetPath, tabURL string, totalBytes, startTime, endTime int64) string {
|
||||
func insertDownload(targetPath, tabURL, mimeType string, totalBytes, startTime, endTime int64) string {
|
||||
return fmt.Sprintf(
|
||||
`INSERT INTO downloads (id, guid, current_path, target_path, start_time, received_bytes,
|
||||
total_bytes, state, danger_type, interrupt_reason, hash, end_time, opened, last_access_time,
|
||||
transient, referrer, site_url, embedder_download_data, tab_url, tab_referrer_url,
|
||||
http_method, by_ext_id, by_ext_name, by_web_app_id, etag, last_modified, mime_type, original_mime_type)
|
||||
VALUES (NULL, '', '', '%s', %d, %d, %d, 1, 0, 0, x'', %d, 0, 0, 0, '', '', '', '%s', '', 'GET', '', '', '', '', '', '', '')`,
|
||||
targetPath, startTime, totalBytes, totalBytes, endTime, tabURL,
|
||||
VALUES (NULL, '', '', '%s', %d, %d, %d, 1, 0, 0, x'', %d, 0, 0, 0, '', '', '', '%s', '', 'GET', '', '', '', '', '', '%s', '')`,
|
||||
targetPath, startTime, totalBytes, totalBytes, endTime, tabURL, mimeType,
|
||||
)
|
||||
}
|
||||
|
||||
func insertCreditCard(name string, month, year int, encNumberHex string) string {
|
||||
func insertCreditCard(name string, month, year int, encNumberHex, nickName, address string) string {
|
||||
return fmt.Sprintf(
|
||||
`INSERT INTO credit_cards (guid, name_on_card, expiration_month, expiration_year, card_number_encrypted)
|
||||
VALUES ('%s-%d-%d', '%s', %d, %d, x'%s')`,
|
||||
name, month, year, name, month, year, encNumberHex,
|
||||
`INSERT INTO credit_cards (guid, name_on_card, expiration_month, expiration_year, card_number_encrypted, nickname, billing_address_id)
|
||||
VALUES ('%s-%d-%d', '%s', %d, %d, x'%s', '%s', '%s')`,
|
||||
name, month, year, name, month, year, encNumberHex, nickName, address,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user