chore: update golangci-lint config and fix lint issues (#542)

* chore: update golangci-lint config and fix lint issues
This commit is contained in:
Roger
2026-04-04 16:26:07 +08:00
committed by GitHub
parent e35907de6f
commit 92053b85b0
14 changed files with 116 additions and 138 deletions
+32 -66
View File
@@ -10,7 +10,7 @@ run:
linters: linters:
default: none default: none
enable: enable:
# Default tier # Default tier — must-have for any Go project
- errcheck - errcheck
- govet - govet
- staticcheck - staticcheck
@@ -21,11 +21,18 @@ linters:
- errorlint - errorlint
- gosec - gosec
- sqlclosecheck - sqlclosecheck
- nilerr
- bodyclose
- durationcheck
- errchkjson
- exhaustive
- forcetypeassert
# Code quality # Code quality
- depguard - depguard
- dogsled - dogsled
- dupl - dupl
- dupword
- errname - errname
- funlen - funlen
- gocheckcompilerdirectives - gocheckcompilerdirectives
@@ -35,20 +42,24 @@ linters:
- godox - godox
- goprintffuncname - goprintffuncname
- lll - lll
- mirror
- misspell - misspell
- nakedret - nakedret
- predeclared
- revive - revive
- testifylint - testifylint
- unconvert - unconvert
- unparam - unparam
- usestdlibvars - usestdlibvars
- wastedassign
- whitespace - whitespace
# Complexity # Complexity
- gocognit - gocognit
- nestif
# Note: copyloopvar, intrange, modernize, perfsprint require Go 1.22+ # Note: copyloopvar, intrange, modernize, perfsprint require Go 1.22+
# They will be enabled when Go version constraint is lifted # They will be enabled when Go version constraint is lifted.
settings: settings:
depguard: depguard:
@@ -61,6 +72,8 @@ linters:
desc: Deprecated since Go 1.16. Use io and os packages instead. desc: Deprecated since Go 1.16. Use io and os packages instead.
- pkg: "github.com/instana/testify" - pkg: "github.com/instana/testify"
desc: Use github.com/stretchr/testify instead. desc: Use github.com/stretchr/testify instead.
exhaustive:
default-signifies-exhaustive: true
dupl: dupl:
threshold: 100 threshold: 100
funlen: funlen:
@@ -82,17 +95,15 @@ linters:
disabled-checks: disabled-checks:
- dupImport - dupImport
- hugeParam - hugeParam
- rangeValCopy - rangeValCopy # keychainbreaker structs are large by design
- ifElseChain - unnamedResult # crypto functions returning (key, iv) are clear without names
- octalLiteral
- whyNoLint - whyNoLint
- singleCaseSwitch
- exitAfterDefer
- commentedOutCode
lll: lll:
line-length: 140 line-length: 140
gocognit: gocognit:
min-complexity: 30 min-complexity: 30
nestif:
min-complexity: 5
godox: godox:
keywords: keywords:
- FIXME - FIXME
@@ -103,17 +114,17 @@ linters:
asserts: false asserts: false
gosec: gosec:
excludes: excludes:
- G101 - G101 # hardcoded credentials — false positives on const names
- G104 - G115 # integer overflow on conversion — false positives on safe narrowing
- G304 - G117 # struct field matches secret pattern — false positive on Password fields
- G401 - G204 # exec.Command with variable — required for macOS `security` command
- G405 - G304 # file inclusion via variable — required for dynamic browser paths
- G501 - G401 # weak crypto SHA1 — required for Chromium PBKDF2 key derivation
- G502 - G402 # TLS MinVersion — not applicable (no TLS in this tool)
- G505 - G405 # weak crypto DES — required for Firefox 3DES decryption
- G115 - G501 # blocklisted import crypto/md5 — not used, keep for safety
- G117 - G502 # blocklisted import crypto/des — required for Firefox decryption
- G204 - G505 # blocklisted import crypto/sha1 — required for PBKDF2
errcheck: errcheck:
check-type-assertions: true check-type-assertions: true
exclude-functions: exclude-functions:
@@ -127,22 +138,13 @@ linters:
rules: rules:
- name: indent-error-flow - name: indent-error-flow
- name: unexported-return - name: unexported-return
disabled: true
- name: unused-parameter - name: unused-parameter
disabled: true disabled: true
- name: package-comments
disabled: true
- name: exported
disabled: true
staticcheck: staticcheck:
checks: checks:
- "all" - "all"
- "-ST1000" - "-ST1000" # package comment — not a public library
- "-ST1003" - "-ST1003" # naming convention — allow platform-specific names
- "-ST1016"
- "-ST1020"
- "-ST1021"
- "-ST1022"
exclusions: exclusions:
presets: presets:
@@ -157,7 +159,6 @@ linters:
- funlen - funlen
- gosec - gosec
- errcheck - errcheck
- testifylint
- lll - lll
- source: "defer" - source: "defer"
linters: linters:
@@ -165,47 +166,12 @@ linters:
- text: "SELECT" - text: "SELECT"
linters: linters:
- gosec - gosec
# Temporary: known issues in pre-refactoring code (will be removed during refactoring)
- text: "result 0 .* is always nil"
linters:
- unparam
- text: "result 0 .* is never used"
linters:
- unparam
- path: "browser/firefox/firefox.go"
text: "field .* is unused"
linters:
- unused
- path: "browserdata/sessionstorage/"
text: "is unused"
linters:
- unused
- path: "cmd/hack-browser-data/main.go" - path: "cmd/hack-browser-data/main.go"
linters: linters:
- lll - lll
# Temporary: pre-refactoring code issues (all will be rewritten)
- path: "browserdata/"
linters:
- dupl
- gochecknoinits
- goconst
- lll
- path: "browser/firefox/"
linters:
- gocritic
- path: "crypto/"
linters:
- gocritic
- path: "crypto/keyretriever/gcoredump_darwin.go" - path: "crypto/keyretriever/gcoredump_darwin.go"
linters: linters:
- gocognit - gocognit
# Temporary: new v2 extract files have no callers until Phase 8 wiring
- path: "browser/chromium/(source|decrypt|extract_.*)\\.go"
linters:
- unused
- path: "browser/firefox/(source|extract_.*)\\.go"
linters:
- unused
formatters: formatters:
enable: enable:
+1 -1
View File
@@ -21,7 +21,7 @@ func mkFile(t *testing.T, parts ...string) {
func TestListBrowsers(t *testing.T) { func TestListBrowsers(t *testing.T) {
list := ListBrowsers() list := ListBrowsers()
assert.True(t, len(list) > 0) assert.NotEmpty(t, list)
assert.True(t, sort.StringsAreSorted(list)) assert.True(t, sort.StringsAreSorted(list))
} }
+1 -1
View File
@@ -399,7 +399,7 @@ func TestAcquireFiles(t *testing.T) {
assert.Len(t, paths, len(cats)) assert.Len(t, paths, len(cats))
for _, p := range paths { for _, p := range paths {
_, err := os.Stat(p) _, err := os.Stat(p)
assert.NoError(t, err, "acquired file should exist") require.NoError(t, err, "acquired file should exist")
} }
} }
+1 -1
View File
@@ -40,5 +40,5 @@ func TestExtractHistories_NullFields(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Len(t, got, 1) require.Len(t, got, 1)
assert.Equal(t, "https://null.test", got[0].URL) assert.Equal(t, "https://null.test", got[0].URL)
assert.Equal(t, "", got[0].Title) assert.Empty(t, got[0].Title)
} }
+23 -22
View File
@@ -7,6 +7,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
var ( var (
@@ -74,14 +75,14 @@ var (
func TestNewASN1PBE(t *testing.T) { func TestNewASN1PBE(t *testing.T) {
for _, tc := range nssPBETestCases { for _, tc := range nssPBETestCases {
nssRaw, err := hex.DecodeString(tc.RawHexPBE) nssRaw, err := hex.DecodeString(tc.RawHexPBE)
assert.Equal(t, nil, err) require.NoError(t, err)
pbe, err := NewASN1PBE(nssRaw) pbe, err := NewASN1PBE(nssRaw)
assert.Equal(t, nil, err) require.NoError(t, err)
nssPBETC, ok := pbe.(nssPBE) nssPBETC, ok := pbe.(nssPBE)
assert.Equal(t, true, ok) assert.True(t, ok)
assert.Equal(t, nssPBETC.Encrypted, tc.Encrypted) assert.Equal(t, nssPBETC.Encrypted, tc.Encrypted)
assert.Equal(t, nssPBETC.AlgoAttr.SaltAttr.EntrySalt, tc.GlobalSalt) assert.Equal(t, nssPBETC.AlgoAttr.SaltAttr.EntrySalt, tc.GlobalSalt)
assert.Equal(t, nssPBETC.AlgoAttr.SaltAttr.Len, 20) assert.Equal(t, 20, nssPBETC.AlgoAttr.SaltAttr.Len)
assert.Equal(t, nssPBETC.AlgoAttr.ObjectIdentifier, tc.ObjectIdentifier) assert.Equal(t, nssPBETC.AlgoAttr.ObjectIdentifier, tc.ObjectIdentifier)
} }
} }
@@ -108,8 +109,8 @@ func TestNssPBE_Encrypt(t *testing.T) {
}, },
} }
encrypted, err := nssPBETC.Encrypt(tc.GlobalSalt, tc.Plaintext) encrypted, err := nssPBETC.Encrypt(tc.GlobalSalt, tc.Plaintext)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(encrypted) > 0) assert.NotEmpty(t, encrypted)
assert.Equal(t, nssPBETC.Encrypted, encrypted) assert.Equal(t, nssPBETC.Encrypted, encrypted)
} }
} }
@@ -136,8 +137,8 @@ func TestNssPBE_Decrypt(t *testing.T) {
}, },
} }
decrypted, err := nssPBETC.Decrypt(tc.GlobalSalt) decrypted, err := nssPBETC.Decrypt(tc.GlobalSalt)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(decrypted) > 0) assert.NotEmpty(t, decrypted)
assert.Equal(t, pbePlaintext, decrypted) assert.Equal(t, pbePlaintext, decrypted)
} }
} }
@@ -145,11 +146,11 @@ func TestNssPBE_Decrypt(t *testing.T) {
func TestNewASN1PBE_MetaPBE(t *testing.T) { func TestNewASN1PBE_MetaPBE(t *testing.T) {
for _, tc := range metaPBETestCases { for _, tc := range metaPBETestCases {
metaRaw, err := hex.DecodeString(tc.RawHexPBE) metaRaw, err := hex.DecodeString(tc.RawHexPBE)
assert.Equal(t, nil, err) require.NoError(t, err)
pbe, err := NewASN1PBE(metaRaw) pbe, err := NewASN1PBE(metaRaw)
assert.Equal(t, nil, err) require.NoError(t, err)
metaPBETC, ok := pbe.(metaPBE) metaPBETC, ok := pbe.(metaPBE)
assert.Equal(t, true, ok) assert.True(t, ok)
assert.Equal(t, metaPBETC.Encrypted, tc.Encrypted) assert.Equal(t, metaPBETC.Encrypted, tc.Encrypted)
assert.Equal(t, metaPBETC.AlgoAttr.Data.IVData.IV, tc.IV) assert.Equal(t, metaPBETC.AlgoAttr.Data.IVData.IV, tc.IV)
assert.Equal(t, metaPBETC.AlgoAttr.Data.IVData.ObjectIdentifier, objWithSHA256AndAES) assert.Equal(t, metaPBETC.AlgoAttr.Data.IVData.ObjectIdentifier, objWithSHA256AndAES)
@@ -193,8 +194,8 @@ func TestMetaPBE_Encrypt(t *testing.T) {
Encrypted: tc.Encrypted, Encrypted: tc.Encrypted,
} }
encrypted, err := metaPBETC.Encrypt(tc.GlobalSalt, tc.Plaintext) encrypted, err := metaPBETC.Encrypt(tc.GlobalSalt, tc.Plaintext)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(encrypted) > 0) assert.NotEmpty(t, encrypted)
assert.Equal(t, metaPBETC.Encrypted, encrypted) assert.Equal(t, metaPBETC.Encrypted, encrypted)
} }
} }
@@ -236,8 +237,8 @@ func TestMetaPBE_Decrypt(t *testing.T) {
Encrypted: tc.Encrypted, Encrypted: tc.Encrypted,
} }
decrypted, err := metaPBETC.Decrypt(tc.GlobalSalt) decrypted, err := metaPBETC.Decrypt(tc.GlobalSalt)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(decrypted) > 0) assert.NotEmpty(t, decrypted)
assert.Equal(t, pbePlaintext, decrypted) assert.Equal(t, pbePlaintext, decrypted)
} }
} }
@@ -245,11 +246,11 @@ func TestMetaPBE_Decrypt(t *testing.T) {
func TestNewASN1PBE_LoginPBE(t *testing.T) { func TestNewASN1PBE_LoginPBE(t *testing.T) {
for _, tc := range loginPBETestCases { for _, tc := range loginPBETestCases {
loginRaw, err := hex.DecodeString(tc.RawHexPBE) loginRaw, err := hex.DecodeString(tc.RawHexPBE)
assert.Equal(t, nil, err) require.NoError(t, err)
pbe, err := NewASN1PBE(loginRaw) pbe, err := NewASN1PBE(loginRaw)
assert.Equal(t, nil, err) require.NoError(t, err)
loginPBETC, ok := pbe.(loginPBE) loginPBETC, ok := pbe.(loginPBE)
assert.Equal(t, true, ok) assert.True(t, ok)
assert.Equal(t, loginPBETC.Encrypted, tc.Encrypted) assert.Equal(t, loginPBETC.Encrypted, tc.Encrypted)
assert.Equal(t, loginPBETC.Data.IV, tc.IV) assert.Equal(t, loginPBETC.Data.IV, tc.IV)
assert.Equal(t, loginPBETC.Data.ObjectIdentifier, objWithMD5AndDESCBC) assert.Equal(t, loginPBETC.Data.ObjectIdentifier, objWithMD5AndDESCBC)
@@ -270,8 +271,8 @@ func TestLoginPBE_Encrypt(t *testing.T) {
Encrypted: tc.Encrypted, Encrypted: tc.Encrypted,
} }
encrypted, err := loginPBETC.Encrypt(tc.GlobalSalt, plainText) encrypted, err := loginPBETC.Encrypt(tc.GlobalSalt, plainText)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(encrypted) > 0) assert.NotEmpty(t, encrypted)
assert.Equal(t, loginPBETC.Encrypted, encrypted) assert.Equal(t, loginPBETC.Encrypted, encrypted)
} }
} }
@@ -290,8 +291,8 @@ func TestLoginPBE_Decrypt(t *testing.T) {
Encrypted: tc.Encrypted, Encrypted: tc.Encrypted,
} }
decrypted, err := loginPBETC.Decrypt(tc.GlobalSalt) decrypted, err := loginPBETC.Decrypt(tc.GlobalSalt)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(decrypted) > 0) assert.NotEmpty(t, decrypted)
assert.Equal(t, pbePlaintext, decrypted) assert.Equal(t, pbePlaintext, decrypted)
} }
} }
+13 -12
View File
@@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
const baseKey = "moond4rk" const baseKey = "moond4rk"
@@ -28,45 +29,45 @@ var (
func TestAES128CBCEncrypt(t *testing.T) { func TestAES128CBCEncrypt(t *testing.T) {
encrypted, err := AES128CBCEncrypt(aesKey, aesIV, plainText) encrypted, err := AES128CBCEncrypt(aesKey, aesIV, plainText)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(encrypted) > 0) assert.NotEmpty(t, encrypted)
assert.Equal(t, aes128Ciphertext, fmt.Sprintf("%x", encrypted)) assert.Equal(t, aes128Ciphertext, fmt.Sprintf("%x", encrypted))
} }
func TestAES128CBCDecrypt(t *testing.T) { func TestAES128CBCDecrypt(t *testing.T) {
ciphertext, _ := hex.DecodeString(aes128Ciphertext) ciphertext, _ := hex.DecodeString(aes128Ciphertext)
decrypted, err := AES128CBCDecrypt(aesKey, aesIV, ciphertext) decrypted, err := AES128CBCDecrypt(aesKey, aesIV, ciphertext)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(decrypted) > 0) assert.NotEmpty(t, decrypted)
assert.Equal(t, plainText, decrypted) assert.Equal(t, plainText, decrypted)
} }
func TestDES3Encrypt(t *testing.T) { func TestDES3Encrypt(t *testing.T) {
encrypted, err := DES3Encrypt(des3Key, des3IV, plainText) encrypted, err := DES3Encrypt(des3Key, des3IV, plainText)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(encrypted) > 0) assert.NotEmpty(t, encrypted)
assert.Equal(t, des3Ciphertext, fmt.Sprintf("%x", encrypted)) assert.Equal(t, des3Ciphertext, fmt.Sprintf("%x", encrypted))
} }
func TestDES3Decrypt(t *testing.T) { func TestDES3Decrypt(t *testing.T) {
ciphertext, _ := hex.DecodeString(des3Ciphertext) ciphertext, _ := hex.DecodeString(des3Ciphertext)
decrypted, err := DES3Decrypt(des3Key, des3IV, ciphertext) decrypted, err := DES3Decrypt(des3Key, des3IV, ciphertext)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(decrypted) > 0) assert.NotEmpty(t, decrypted)
assert.Equal(t, plainText, decrypted) assert.Equal(t, plainText, decrypted)
} }
func TestAESGCMEncrypt(t *testing.T) { func TestAESGCMEncrypt(t *testing.T) {
encrypted, err := AESGCMEncrypt(aesKey, aesGCMNonce, plainText) encrypted, err := AESGCMEncrypt(aesKey, aesGCMNonce, plainText)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(encrypted) > 0) assert.NotEmpty(t, encrypted)
assert.Equal(t, aesGCMCiphertext, fmt.Sprintf("%x", encrypted)) assert.Equal(t, aesGCMCiphertext, fmt.Sprintf("%x", encrypted))
} }
func TestAESGCMDecrypt(t *testing.T) { func TestAESGCMDecrypt(t *testing.T) {
ciphertext, _ := hex.DecodeString(aesGCMCiphertext) ciphertext, _ := hex.DecodeString(aesGCMCiphertext)
decrypted, err := AESGCMDecrypt(aesKey, aesGCMNonce, ciphertext) decrypted, err := AESGCMDecrypt(aesKey, aesGCMNonce, ciphertext)
assert.Equal(t, nil, err) require.NoError(t, err)
assert.Equal(t, true, len(decrypted) > 0) assert.NotEmpty(t, decrypted)
assert.Equal(t, plainText, decrypted) assert.Equal(t, plainText, decrypted)
} }
+2 -2
View File
@@ -43,7 +43,7 @@ func TestChainRetriever_AllFail(t *testing.T) {
&mockRetriever{err: errors.New("second failed")}, &mockRetriever{err: errors.New("second failed")},
) )
key, err := chain.RetrieveKey("Chrome", "") key, err := chain.RetrieveKey("Chrome", "")
assert.Error(t, err) require.Error(t, err)
assert.Nil(t, key) assert.Nil(t, key)
assert.Contains(t, err.Error(), "all retrievers failed") assert.Contains(t, err.Error(), "all retrievers failed")
assert.Contains(t, err.Error(), "first failed") assert.Contains(t, err.Error(), "first failed")
@@ -64,6 +64,6 @@ func TestChainRetriever_SkipEmptyKey(t *testing.T) {
func TestChainRetriever_Empty(t *testing.T) { func TestChainRetriever_Empty(t *testing.T) {
chain := NewChain() chain := NewChain()
key, err := chain.RetrieveKey("Chrome", "") key, err := chain.RetrieveKey("Chrome", "")
assert.Error(t, err) require.Error(t, err)
assert.Nil(t, key) assert.Nil(t, key)
} }
+8 -8
View File
@@ -25,12 +25,12 @@ func TestCopyLocked_ExclusiveLock(t *testing.T) {
// Normal copy should fail // Normal copy should fail
err := copyFile(src, filepath.Join(dir, "normal_copy.db")) err := copyFile(src, filepath.Join(dir, "normal_copy.db"))
assert.Error(t, err, "normal copy should fail on exclusively locked file") require.Error(t, err, "normal copy should fail on exclusively locked file")
// copyLocked should succeed via DuplicateHandle + FileMapping // copyLocked should succeed via DuplicateHandle + FileMapping
lockedDst := filepath.Join(dir, "locked_copy.db") lockedDst := filepath.Join(dir, "locked_copy.db")
err = copyLocked(src, lockedDst) err = copyLocked(src, lockedDst)
assert.NoError(t, err, "copyLocked should bypass exclusive lock") require.NoError(t, err, "copyLocked should bypass exclusive lock")
copied, err := os.ReadFile(lockedDst) copied, err := os.ReadFile(lockedDst)
require.NoError(t, err) require.NoError(t, err)
@@ -62,7 +62,7 @@ func TestCopyLocked_WriteThenRead(t *testing.T) {
// copyLocked should read the full content including appended data // copyLocked should read the full content including appended data
lockedDst := filepath.Join(dir, "modified_copy.db") lockedDst := filepath.Join(dir, "modified_copy.db")
copyErr := copyLocked(src, lockedDst) copyErr := copyLocked(src, lockedDst)
assert.NoError(t, copyErr) require.NoError(t, copyErr)
copied, err := os.ReadFile(lockedDst) copied, err := os.ReadFile(lockedDst)
require.NoError(t, err) require.NoError(t, err)
@@ -86,17 +86,17 @@ func TestCopyLocked_LargeFile(t *testing.T) {
lockedDst := filepath.Join(dir, "large_copy.db") lockedDst := filepath.Join(dir, "large_copy.db")
err := copyLocked(src, lockedDst) err := copyLocked(src, lockedDst)
assert.NoError(t, err) require.NoError(t, err)
copied, err := os.ReadFile(lockedDst) copied, err := os.ReadFile(lockedDst)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, len(data), len(copied), "file sizes should match") assert.Len(t, copied, len(data), "file sizes should match")
assert.True(t, bytes.Equal(data, copied), "file content should match byte-for-byte") assert.True(t, bytes.Equal(data, copied), "file content should match byte-for-byte")
} }
func TestCopyLocked_FileNotFound(t *testing.T) { func TestCopyLocked_FileNotFound(t *testing.T) {
err := copyLocked("/nonexistent/file.db", filepath.Join(t.TempDir(), "dst.db")) err := copyLocked("/nonexistent/file.db", filepath.Join(t.TempDir(), "dst.db"))
assert.Error(t, err) require.Error(t, err)
} }
func TestAcquire_FallbackToLocked(t *testing.T) { func TestAcquire_FallbackToLocked(t *testing.T) {
@@ -115,7 +115,7 @@ func TestAcquire_FallbackToLocked(t *testing.T) {
dst := filepath.Join(session.TempDir(), "cookies.db") dst := filepath.Join(session.TempDir(), "cookies.db")
err = session.Acquire(src, dst, false) err = session.Acquire(src, dst, false)
assert.NoError(t, err, "Acquire should succeed via locked fallback") require.NoError(t, err, "Acquire should succeed via locked fallback")
copied, err := os.ReadFile(dst) copied, err := os.ReadFile(dst)
require.NoError(t, err) require.NoError(t, err)
@@ -136,7 +136,7 @@ func TestAcquire_NormalCopyWhenNotLocked(t *testing.T) {
dst := filepath.Join(session.TempDir(), "unlocked.db") dst := filepath.Join(session.TempDir(), "unlocked.db")
err = session.Acquire(src, dst, false) err = session.Acquire(src, dst, false)
assert.NoError(t, err) require.NoError(t, err)
copied, err := os.ReadFile(dst) copied, err := os.ReadFile(dst)
require.NoError(t, err) require.NoError(t, err)
+4 -4
View File
@@ -42,7 +42,7 @@ func TestSession_Acquire_File(t *testing.T) {
// Acquire it // Acquire it
dst := filepath.Join(s.TempDir(), "Login Data") dst := filepath.Join(s.TempDir(), "Login Data")
err = s.Acquire(srcFile, dst, false) err = s.Acquire(srcFile, dst, false)
assert.NoError(t, err) require.NoError(t, err)
// Verify copy // Verify copy
data, err := os.ReadFile(dst) data, err := os.ReadFile(dst)
@@ -63,7 +63,7 @@ func TestSession_Acquire_WAL(t *testing.T) {
dst := filepath.Join(s.TempDir(), "Cookies") dst := filepath.Join(s.TempDir(), "Cookies")
err = s.Acquire(srcFile, dst, false) err = s.Acquire(srcFile, dst, false)
assert.NoError(t, err) require.NoError(t, err)
// Main file copied // Main file copied
assert.FileExists(t, dst) assert.FileExists(t, dst)
@@ -85,7 +85,7 @@ func TestSession_Acquire_Dir(t *testing.T) {
dst := filepath.Join(s.TempDir(), "leveldb") dst := filepath.Join(s.TempDir(), "leveldb")
err = s.Acquire(srcDir, dst, true) err = s.Acquire(srcDir, dst, true)
assert.NoError(t, err) require.NoError(t, err)
// Data file copied // Data file copied
assert.FileExists(t, filepath.Join(dst, "000001.ldb")) assert.FileExists(t, filepath.Join(dst, "000001.ldb"))
@@ -99,5 +99,5 @@ func TestSession_Acquire_NotFound(t *testing.T) {
dst := filepath.Join(s.TempDir(), "nope") dst := filepath.Join(s.TempDir(), "nope")
err = s.Acquire("/nonexistent/file", dst, false) err = s.Acquire("/nonexistent/file", dst, false)
assert.Error(t, err) require.Error(t, err)
} }
+15 -5
View File
@@ -7,17 +7,27 @@ import (
"github.com/moond4rk/hackbrowserdata/types" "github.com/moond4rk/hackbrowserdata/types"
) )
type cookieEditorFormatter struct{} // cookieEditorFormatter outputs cookies in the CookieEditor browser extension
// format. Non-cookie categories fall back to standard JSON output.
type cookieEditorFormatter struct {
fallback *jsonFormatter
}
func (f *cookieEditorFormatter) ext() string { return "json" } func (f *cookieEditorFormatter) ext() string { return "json" }
func (f *cookieEditorFormatter) format(w io.Writer, rows []row) error { func (f *cookieEditorFormatter) format(w io.Writer, rows []row) error {
if len(rows) == 0 {
return nil
}
// aggregate() guarantees all rows in a batch share the same type;
// check the first row to decide the format.
if _, ok := rows[0].entry.(types.CookieEntry); !ok {
return f.fallback.format(w, rows)
}
entries := make([]cookieEditorEntry, 0, len(rows)) entries := make([]cookieEditorEntry, 0, len(rows))
for _, r := range rows { for _, r := range rows {
c, ok := r.entry.(types.CookieEntry) c, _ := r.entry.(types.CookieEntry)
if !ok {
return nil // not cookies, skip
}
var expDate float64 var expDate float64
if !c.ExpireAt.IsZero() { if !c.ExpireAt.IsZero() {
expDate = float64(c.ExpireAt.Unix()) expDate = float64(c.ExpireAt.Unix())
+1 -1
View File
@@ -18,7 +18,7 @@ func newFormatter(name string) (formatter, error) {
case "json": case "json":
return &jsonFormatter{}, nil return &jsonFormatter{}, nil
case "cookie-editor": case "cookie-editor":
return &cookieEditorFormatter{}, nil return &cookieEditorFormatter{fallback: &jsonFormatter{}}, nil
default: default:
return nil, fmt.Errorf("unsupported format: %s", name) return nil, fmt.Errorf("unsupported format: %s", name)
} }
+6 -6
View File
@@ -65,10 +65,10 @@ func TestNew(t *testing.T) {
t.Run(tt.format, func(t *testing.T) { t.Run(tt.format, func(t *testing.T) {
out, err := NewWriter(t.TempDir(), tt.format) out, err := NewWriter(t.TempDir(), tt.format)
if tt.wantErr { if tt.wantErr {
assert.Error(t, err) require.Error(t, err)
assert.Nil(t, out) assert.Nil(t, out)
} else { } else {
assert.NoError(t, err) require.NoError(t, err)
assert.NotNil(t, out) assert.NotNil(t, out)
} }
}) })
@@ -142,7 +142,7 @@ func TestWrite_CSV_UTF8BOM(t *testing.T) {
raw, err := os.ReadFile(filepath.Join(dir, "password.csv")) raw, err := os.ReadFile(filepath.Join(dir, "password.csv"))
require.NoError(t, err) require.NoError(t, err)
require.True(t, len(raw) >= 3) require.GreaterOrEqual(t, len(raw), 3)
assert.Equal(t, utf8BOM, raw[:3], "CSV should start with UTF-8 BOM") assert.Equal(t, utf8BOM, raw[:3], "CSV should start with UTF-8 BOM")
} }
@@ -249,7 +249,7 @@ func TestWrite_CookieEditor(t *testing.T) {
}, entries[0]) }, entries[0])
} }
func TestWrite_CookieEditor_SkipsNonCookie(t *testing.T) { func TestWrite_CookieEditor_FallbackJSON(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
out, err := NewWriter(dir, "cookie-editor") out, err := NewWriter(dir, "cookie-editor")
require.NoError(t, err) require.NoError(t, err)
@@ -258,9 +258,9 @@ func TestWrite_CookieEditor_SkipsNonCookie(t *testing.T) {
}) })
require.NoError(t, out.Write()) require.NoError(t, out.Write())
// password file should not be created (cookie-editor only exports cookies) // non-cookie categories fall back to standard JSON format
_, err = os.Stat(filepath.Join(dir, "password.json")) _, err = os.Stat(filepath.Join(dir, "password.json"))
assert.True(t, os.IsNotExist(err)) assert.False(t, os.IsNotExist(err), "password.json should be created via JSON fallback")
} }
// --- File creation --- // --- File creation ---
+3 -3
View File
@@ -29,7 +29,7 @@ func TestCompressDir(t *testing.T) {
defer os.RemoveAll(tempDir) defer os.RemoveAll(tempDir)
err := CompressDir(tempDir) err := CompressDir(tempDir)
assert.NoError(t, err, "compressDir should not return an error") require.NoError(t, err, "compressDir should not return an error")
// Check if the zip file exists // Check if the zip file exists
zipFile := filepath.Join(tempDir, filepath.Base(tempDir)+".zip") zipFile := filepath.Join(tempDir, filepath.Base(tempDir)+".zip")
@@ -38,7 +38,7 @@ func TestCompressDir(t *testing.T) {
t.Run("Directory Does Not Exist", func(t *testing.T) { t.Run("Directory Does Not Exist", func(t *testing.T) {
err := CompressDir("/path/to/nonexistent/directory") err := CompressDir("/path/to/nonexistent/directory")
assert.Error(t, err, "should return an error for non-existent directory") require.Error(t, err, "should return an error for non-existent directory")
}) })
t.Run("Empty Directory", func(t *testing.T) { t.Run("Empty Directory", func(t *testing.T) {
@@ -47,6 +47,6 @@ func TestCompressDir(t *testing.T) {
defer os.RemoveAll(tempDir) defer os.RemoveAll(tempDir)
err = CompressDir(tempDir) err = CompressDir(tempDir)
assert.Error(t, err, "should return an error for an empty directory") require.Error(t, err, "should return an error for an empty directory")
}) })
} }
+6 -6
View File
@@ -34,7 +34,7 @@ func TestQuerySQLite(t *testing.T) {
return nil return nil
}) })
assert.NoError(t, err) require.NoError(t, err)
assert.Equal(t, []string{"alpha", "beta", "gamma"}, names) assert.Equal(t, []string{"alpha", "beta", "gamma"}, names)
} }
@@ -59,7 +59,7 @@ func TestQuerySQLite_JournalOff(t *testing.T) {
values = append(values, v) values = append(values, v)
return nil return nil
}) })
assert.NoError(t, err) require.NoError(t, err)
assert.Equal(t, []string{"ok"}, values) assert.Equal(t, []string{"ok"}, values)
} }
@@ -67,7 +67,7 @@ func TestQuerySQLite_FileNotFound(t *testing.T) {
err := QuerySQLite("/nonexistent/path.db", false, "SELECT 1", func(rows *sql.Rows) error { err := QuerySQLite("/nonexistent/path.db", false, "SELECT 1", func(rows *sql.Rows) error {
return nil return nil
}) })
assert.Error(t, err) require.Error(t, err)
} }
func TestQuerySQLite_BadQuery(t *testing.T) { func TestQuerySQLite_BadQuery(t *testing.T) {
@@ -83,7 +83,7 @@ func TestQuerySQLite_BadQuery(t *testing.T) {
err = QuerySQLite(dbPath, false, "SELECT nonexistent FROM t", func(rows *sql.Rows) error { err = QuerySQLite(dbPath, false, "SELECT nonexistent FROM t", func(rows *sql.Rows) error {
return nil return nil
}) })
assert.Error(t, err) require.Error(t, err)
} }
func TestQueryRows(t *testing.T) { func TestQueryRows(t *testing.T) {
@@ -110,7 +110,7 @@ func TestQueryRows(t *testing.T) {
return u, err return u, err
}) })
assert.NoError(t, err) require.NoError(t, err)
assert.Equal(t, []user{{"alice", 30}, {"bob", 25}}, users) assert.Equal(t, []user{{"alice", 30}, {"bob", 25}}, users)
} }
@@ -133,6 +133,6 @@ func TestQueryRows_Empty(t *testing.T) {
return v, nil return v, nil
}) })
assert.NoError(t, err) require.NoError(t, err)
assert.Nil(t, results) assert.Nil(t, results)
} }