Files
HackBrowserData/browser/firefox/extract_download.go
T
Roger 1ec2781131 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
2026-04-04 01:41:02 +08:00

56 lines
1.6 KiB
Go

package firefox
import (
"database/sql"
"sort"
"strings"
"github.com/tidwall/gjson"
"github.com/moond4rk/hackbrowserdata/types"
"github.com/moond4rk/hackbrowserdata/utils/sqliteutil"
"github.com/moond4rk/hackbrowserdata/utils/typeutil"
)
const firefoxDownloadQuery = `SELECT place_id, GROUP_CONCAT(content), url, dateAdded
FROM (SELECT * FROM moz_annos INNER JOIN moz_places ON moz_annos.place_id=moz_places.id)
t GROUP BY place_id`
func extractDownloads(path string) ([]types.DownloadEntry, error) {
downloads, err := sqliteutil.QueryRows(path, true, firefoxDownloadQuery,
func(rows *sql.Rows) (types.DownloadEntry, error) {
var placeID, dateAdded int64
var content, url string
if err := rows.Scan(&placeID, &content, &url, &dateAdded); err != nil {
return types.DownloadEntry{}, err
}
entry := types.DownloadEntry{
URL: url,
StartTime: typeutil.TimeStamp(dateAdded / 1000000),
}
// Firefox stores download metadata as: "target_path,{json}"
// Parse the JSON part to extract fileSize and endTime.
contentList := strings.SplitN(content, ",{", 2)
if len(contentList) == 2 {
entry.TargetPath = contentList[0]
json := "{" + contentList[1]
entry.TotalBytes = gjson.Get(json, "fileSize").Int()
entry.EndTime = typeutil.TimeStamp(gjson.Get(json, "endTime").Int() / 1000)
} else {
entry.TargetPath = content
}
return entry, nil
})
if err != nil {
return nil, err
}
sort.Slice(downloads, func(i, j int) bool {
return downloads[i].StartTime.After(downloads[j].StartTime)
})
return downloads, nil
}