package firefox import ( "bytes" "encoding/base64" "encoding/hex" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // These values are from crypto/asn1pbe_test.go loginPBETestCases. // loginPBE hex decrypts to "Hello, World!" with globalSalt = "moond4rk" * 3. const loginPBEHex = "303b0410f8000000000000000000000000000001301506092a864886f70d010503040830313233343536370410fe968b6565149114ea688defd6683e45" var testGlobalSalt = bytes.Repeat([]byte("moond4rk"), 3) // 24 bytes func loginPBEBase64(t *testing.T) string { t.Helper() raw, err := hex.DecodeString(loginPBEHex) require.NoError(t, err) return base64.StdEncoding.EncodeToString(raw) } func TestExtractPasswords(t *testing.T) { encB64 := loginPBEBase64(t) // Construct a logins.json with known encrypted username/password json := fmt.Sprintf(`{ "logins": [ { "hostname": "https://example.com", "formSubmitURL": "https://example.com/login", "encryptedUsername": "%s", "encryptedPassword": "%s", "timeCreated": 1700000000000 } ] }`, encB64, encB64) path := createTestJSON(t, "logins.json", json) got, err := extractPasswords(testGlobalSalt, path) require.NoError(t, err) require.Len(t, got, 1) // Both username and password decrypt to "Hello, World!" assert.Equal(t, "Hello, World!", got[0].Username) assert.Equal(t, "Hello, World!", got[0].Password) assert.Equal(t, "https://example.com/login", got[0].URL) assert.False(t, got[0].CreatedAt.IsZero()) } func TestCountPasswords(t *testing.T) { json := `{ "logins": [ {"hostname": "https://a.com", "encryptedUsername": "", "encryptedPassword": "", "timeCreated": 1000}, {"hostname": "https://b.com", "encryptedUsername": "", "encryptedPassword": "", "timeCreated": 2000}, {"hostname": "https://c.com", "encryptedUsername": "", "encryptedPassword": "", "timeCreated": 3000} ] }` path := createTestJSON(t, "logins.json", json) count, err := countPasswords(path) require.NoError(t, err) assert.Equal(t, 3, count) } func TestCountPasswords_Empty(t *testing.T) { path := createTestJSON(t, "logins.json", `{"logins": []}`) count, err := countPasswords(path) require.NoError(t, err) assert.Equal(t, 0, count) } func TestExtractPasswords_FormSubmitURLFallback(t *testing.T) { encB64 := loginPBEBase64(t) // When formSubmitURL is empty, should fall back to hostname json := fmt.Sprintf(`{ "logins": [ { "hostname": "https://fallback.com", "formSubmitURL": "", "encryptedUsername": "%s", "encryptedPassword": "%s", "timeCreated": 1700000000000 } ] }`, encB64, encB64) path := createTestJSON(t, "logins.json", json) got, err := extractPasswords(testGlobalSalt, path) require.NoError(t, err) require.Len(t, got, 1) assert.Equal(t, "https://fallback.com", got[0].URL) } func TestExtractPasswords_DecryptFailureKeepsEntry(t *testing.T) { // Invalid base64 — decryptPBE fails, but entry is still kept with empty user/pwd json := `{ "logins": [ { "hostname": "https://bad.com", "encryptedUsername": "not-valid-base64!!!", "encryptedPassword": "also-bad", "timeCreated": 1700000000000 } ] }` path := createTestJSON(t, "logins.json", json) got, err := extractPasswords(testGlobalSalt, path) require.NoError(t, err) require.Len(t, got, 1) assert.Equal(t, "https://bad.com", got[0].URL) assert.Empty(t, got[0].Username) // decrypt failed → empty assert.Empty(t, got[0].Password) // decrypt failed → empty } func TestExtractPasswords_EmptyLogins(t *testing.T) { path := createTestJSON(t, "logins.json", `{"logins": []}`) got, err := extractPasswords(testGlobalSalt, path) require.NoError(t, err) assert.Empty(t, got) }