Promote keyRetrieversSetter to public KeyManager and lift
keychainPasswordSetter from browser_darwin.go into browser.go as
KeychainPasswordReceiver. Engines already implement these methods;
only type-assertion sites switch to the public interface names.
* feat(safari): localstorage extraction
Extracts Safari 17+ localStorage from WebKit's nested layout —
WebsiteDataStore/<uuid>/Origins/<top-hash>/<frame-hash>/LocalStorage/
localstorage.sqlite3 for named profiles, WebsiteData/Default for the
default profile. Parses the binary SecurityOrigin serialization
(length-prefixed scheme+host plus 0x00 default-port or 0x01 <uint16_le>
explicit-port section) and decodes UTF-16 LE ItemTable value BLOBs,
capping oversized values at 2048 bytes to match the Chromium extractor.
Reports the frame origin URL so partitioned third-party storage is
attributed to the iframe origin JavaScript actually sees.
Closes the remaining LocalStorage checkbox in #565.
* docs(safari): add RFC-011 data storage
Documents Safari's profile structure, per-category file layouts, and
storage formats including the Safari 17+ nested WebKit Origins
localStorage layout and binary SecurityOrigin serialization. Defers
Keychain credential extraction to RFC-006 §7 and notes the cross-browser
differences (plaintext cookies, plist bookmarks/downloads, Core Data
epoch timestamps, partitioned storage).
* fix(safari): latin-1 origin decoding, NULL key skip, count fast-path
- Decode originEncASCII via decodeLatin1 so high-byte records preserve
their ISO-8859-1 meaning instead of being interpreted as UTF-8.
Matches the pattern in chromium/extract_storage.go.
- Skip ItemTable rows where key is NULL — SQLite's UNIQUE constraint
permits multiple NULLs, and silently lowering them to empty strings
would collide with legitimate empty-string keys.
- countLocalStorage now walks origin dirs and runs SELECT COUNT(key)
per localstorage.sqlite3 instead of fully decoding every value.
COUNT(key) naturally excludes NULLs, keeping count and extract
symmetric.
Addresses Copilot review feedback on #582.
* fix(safari): round-2 review — WAL replay, stable ordering, error context
- Drop immutable=1 on temp-copy SQLite opens in readLocalStorageFile /
countLocalStorageFile. Session.Acquire copies the -wal / -shm sidecars,
so mode=ro alone lets SQLite replay WAL on the ephemeral copy and
surface entries Safari committed to WAL but hasn't checkpointed yet.
Live-file reads in profiles.go keep immutable=1 as before.
- Order ItemTable query by (key, rowid) for deterministic exports across
runs and SQLite versions.
- Wrap os.ReadFile / os.ReadDir errors with the offending path so
multi-origin debug logs stay scannable.
- RFC-011 §7 rewritten to explain the live-vs-temp split.
- New regression test asserts ORDER BY surfaces rows in key order.
Addresses round-2 Copilot review on #582.
* feat: add CountEntries to skip decryption for list --detail (#549)
* test: add CountEntries and countCategory tests at browser level
* fix: address review feedback on CountRows and countLocalStorage
* test: add CountRows unit tests
* fix: implement proper Chromium localStorage LevelDB parsing
* feat: add IsMeta field to StorageEntry and keep META entries
* fix: add error logging for decryption and missing data fields
* fix: address PR review for localStorage parsing
* fix: use naïve instead of café in Latin-1 test to avoid typos false positive
* fix: extension enabled detection and sessionStorage decoding
* fix: session storage origin resolution and extension enabled detection
* fix: address PR review comments for storage parsing
Share a single KeyRetriever instance across all profiles of the same
browser, and add sync.Once caching to GcoredumpRetriever and
SecurityCmdRetriever. This avoids repeated keychain password prompts
(or securityd memory dumps) when extracting multiple profiles.
Closes#544
* feat: add Chromium Browser implementation with new architecture
* refactor: replace Walk with ReadDir+Stat for profile discovery
* refactor: remove queries override, use extractors for Yandex passwords
* refactor: remove dataSource wrapper, use []sourcePath directly
* fix: address Copilot review feedback on chromium_new.go
* fix: always call key retriever regardless of Local State existence
* 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
* fix: strip SHA256(host_key) prefix from Chrome 130+ cookie values
Chrome 130 (Cookie DB schema v24) prepends SHA256(domain) to cookie
values before encryption to prevent cross-domain replay attacks.
After decryption, this 32-byte hash must be verified and stripped.
Changes:
- Add stripCookieHash() that verifies SHA256(host_key) and strips
the prefix only when it matches (auto-compatible with older Chrome)
- Fix edge case: cookies with empty values (exactly 32 bytes = hash only)
- Add decrypt_test.go with v10 round-trip encryption/decryption test
- Add stripCookieHash test cases for v24+, older Chrome, empty values,
short values, and host mismatch scenarios
Closes#524
* fix: strip SHA256(host_key) prefix from Chrome 130+ cookie values
Chrome 130 (Cookie DB schema v24) prepends SHA256(domain) to cookie
values before encryption to prevent cross-domain replay attacks.
After decryption, this 32-byte hash must be verified and stripped.
Changes:
- Add stripCookieHash() that verifies SHA256(host_key) and strips
the prefix only when it matches (auto-compatible with older Chrome)
- Fix edge case: cookies with empty values (exactly 32 bytes = hash only)
- Add table-driven decrypt tests for v10/v20/DPAPI per platform
- Add Windows-specific DPAPI round-trip test using CryptProtectData
- Add shared testAESKey constant in testutil_test.go
- Add stripCookieHash tests for v24+, older Chrome, empty values,
short values, and host mismatch scenarios
- Extend lint CI to run on ubuntu, windows, and macos
Closes#524
* fix: remove DPAPI test from darwin/linux (returns nil on Linux)
DecryptWithDPAPI returns nil error on Linux (silent no-op) but error
on macOS, causing the test to fail on Ubuntu CI. DPAPI round-trip
testing is properly covered in decrypt_windows_test.go.
* fix: resolve Windows CI lint errors exposed by multi-platform lint
- Add _ = before windows.CloseHandle calls to satisfy errcheck
- Add build tag to params.go (only used on macOS/Linux, not Windows)
* fix: add .gitattributes to force LF and refactor cookie tests
- Add .gitattributes with `* text=auto eol=lf` to prevent CRLF
conversion on Windows CI causing gofumpt false positives
- Add .gitattributes to .gitignore whitelist
- Refactor stripCookieHash tests into table-driven style
* fix: address Copilot review on decrypt tests
- Assert error on wrong key instead of ignoring it (AES-CBC returns
padding error, not silent empty result)
- Guard empty plaintext in encryptWithDPAPI to prevent nil pointer panic
- Convert uint32 to int for make/copy slice bounds in Windows test
* fix: assert specific error message in wrong key decrypt test
* feat: add Chromium extract methods, source mapping, and tests
Implement per-category data extraction for Chromium browsers as typed
standalone functions, preparing for Phase 8 wiring into the new
Chromium struct.
New files:
- source.go: dataSource struct, chromiumSources/yandexSources maps,
yandexQueryOverrides for Yandex action_url variant
- decrypt.go: decryptValue() wrapping platform-specific decryption
- extract_password.go: SQLite + decrypt → []LoginEntry
- extract_cookie.go: SQLite + decrypt → []CookieEntry
- extract_creditcard.go: SQLite + decrypt → []CreditCardEntry
- extract_history.go: SQLite → []HistoryEntry
- extract_download.go: SQLite → []DownloadEntry
- extract_bookmark.go: JSON recursive → []BookmarkEntry
- extract_extension.go: JSON → []ExtensionEntry
- extract_storage.go: LevelDB → []StorageEntry (local + session)
- firefox/source.go: firefoxSources map
Tests use real Chrome table schemas for SQLite fixtures, with INSERT
helpers to keep test data readable and self-documenting.
Ref #520
* fix: remove LevelDB invalid path test (Windows compatibility)
leveldb.OpenFile creates the directory on Windows instead of returning
an error, causing TestExtractLocalStorage_InvalidPath to fail in CI.
This test was verifying LevelDB behavior, not our extraction logic.
* refactor: remove unused query parameter from extract functions
Only extractPasswords needs the query override (Yandex action_url).
The other 7 SQLite extract functions always use their default query,
so remove the unnecessary query parameter from their signatures.
* refactor: use DetectVersion in decryptValue instead of blind fallback
Replace try-then-fallback pattern with explicit version detection using
crypto.DetectVersion. Routes v10 to DecryptWithChromium, DPAPI to
DecryptWithDPAPI, and adds a TODO placeholder for v20 App-Bound
Encryption.
* chore: relax gocognit and gocritic linters for test files
* revert: restore strict gocognit and gocritic linters for test files
* fix: address review feedback on extract methods
- Store DetectVersion result in local variable to avoid duplicate call
- Scan credit card expiration_month/year as int then convert to string
(matches INTEGER column type in real Chrome schema)
- Add os.Stat check before leveldb.OpenFile to prevent creating empty
directories for non-existent paths
- Rename TestExtractExtensions_InvalidJSON to
TestExtractExtensions_MissingSettingsPath (JSON is valid, path is missing)
* fix: revert creditcard scan to string type for NULL safety
modernc.org/sqlite handles INTEGER→string conversion automatically.
Scanning into string is safer for nullable columns — NULL becomes ""
instead of "0" which would be an invalid month/year.
* feat: Decrypt the browser master key on macOS via CVE-2025-24204
* fix: resolve lint warnings and stabilize tests
* feat: default to gcoredump key extraction on macOS
* chore: downgrade golang version to 1.20, support windows 7
* chore: Update dependencies for Go project.
- Update dependencies in go.sum
- Improvements and optimizations in various files
- Bug fixes and error handling enhancements
* chore: Update modernc.org/sqlite library versions in go.mod and go.sum files
- Update version of `modernc.org/sqlite` to `v1.31.1` in `go.mod` and `go.sum` files
- Update module hash in `go.sum` file for `modernc.org/sqlite`
- Ensure consistency between `go.mod` and `go.sum` files in relation to `modernc.org/sqlite` version
* chore: replace log/slog with standard logger (#436)
* chore: replace log/slog with standard logger
* chore: Update Go dependencies and versions
- Update Go version from `1.22.5` to `1.20` and other dependencies
- Update critical dependencies to latest versions
- Ensure compatibility with new versions of dependencies
* chore: Optimize dependency management in workflows
- Update build and lint workflows to use `go mod tidy` for getting dependencies
- Change modules download mode to `'mod'` in linters configuration
- Add step to get dependencies in lint workflow
* refactor: Update dependencies and refactor Chromium key deletion logic
- Update `modernc.org/sqlite` to `v1.31.1` in `go.mod` and `go.sum`
- Increase version number to `0.5.0` in `cmd/hack-browser-data/main.go`
- Refactor and update logic for filtering and copying items in `browser/chromium/chromium.go`
* Improve logging functionality and data type conversion
- Add `String()` method to `DataType` enum in types.go
- Update log level to Debug in logger_test.go
- Set log level to Debug in `TestLoggerDebug` and `TestLoggerDebugf` functions
- Implement error handling for path permission errors in `chromiumWalkFunc`
- Refactor `firefoxWalkFunc` to handle permission errors and log warnings
- Add import statement for `log/slog` in `firefox/firefox.go`
- Refactored variable names for clarity and consistency in multiple files
- Updated logic to filter sensitive items based on a flag
- Implemented a function to skip processing specific paths to improve performance
* refactor: Refactor package names and imports for better code organization.
* refactor: Package imports and variable types for consistency
* chore: Disable unused-parameter rule in revive.
* refactor: Refactor and organize data extraction and browserdata parse.
* fix: rename wrong error message info
* feat: Refactor crypto decryption functions for consistency and error handling
- Close#301
- Refactored and renamed decryption functions across multiple files for consistency
- Updated cookie sorting method to sort in descending order
- Added new encryption functions for AES in CBC and GCM modes and DES in CBC mode
- Added error handling to decryption functions and created new error variables for invalid ciphertext length and decode failures
- Test cases added for encryption and decryption functions
- Removed unused code and imports.
* chore: Add new words to .typos.toml dictionary
- Add new terms to `.typos.toml` dictionary
- Improve code formatting and readability
- Refactor functions for better performance
- Update comments and documentation
- Resolve minor bugs and errors
* refactor: Refactor crypto package for better structure and readability
- Refactored and cleaned up crypto package code for better readability
- Renamed `ToByteArray` method to `bytes` for consistency
- Modified `DecryptWithDPAPI` method to use `outBlob.bytes()` for efficiency
- Added comments and removed unused methods in `loginPBE`
- Refactored `nssPBE` and `metaPBE` Decrypt methods to use `deriveKeyAndIV` helper method
- Improved overall maintainability and organization of codebase
* refactor: Refactor firefox password encryption and decryption.
- Implement ASN1PBE interface with various PBE struct types and encryption/decryption methods
- Fix naming and remove unused variables in browsingdata and crypto files
- Add tests for ASN1PBE implementation using external assertion package
- Refactor and improve error handling in firefox file functions related to master key retrieval
- Add input validation and AES-GCM encryption function to crypto file