1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
"""Chrome/Chromium password export integration for Cerberus."""
import csv
import json
import sqlite3
import shutil
import tempfile
from pathlib import Path
from typing import List, Optional, Dict, Any
from ..core.models import PasswordEntry
from . import BaseIntegration, register_integration, IntegrationError
class ChromeIntegration(BaseIntegration):
"""Integration with Chrome/Chromium password exports."""
def __init__(self, export_path: Optional[Path] = None):
"""Initialize the Chrome integration.
Args:
export_path: Path to Chrome passwords CSV export
"""
super().__init__()
self.export_path = export_path
def connect(self, export_path: Optional[Path] = None, **kwargs) -> bool:
"""Load the Chrome passwords export file.
Args:
export_path: Path to the Chrome passwords CSV export
Returns:
bool: True if the export file exists and is accessible
"""
if export_path:
self.export_path = Path(export_path)
if not self.export_path or not self.export_path.exists():
raise IntegrationError("Chrome passwords export file not found")
self.connected = True
return True
def list_entries(self) -> List[PasswordEntry]:
"""List all password entries from the Chrome export.
Returns:
List of PasswordEntry objects
Raises:
IntegrationError: If not connected or error reading the export file
"""
if not self.connected:
raise IntegrationError("Not connected to Chrome passwords")
entries: List[PasswordEntry] = []
try:
with open(self.export_path, 'r', encoding='utf-8') as f:
# Chrome CSV format: name,url,username,password
reader = csv.reader(f)
# Skip header if it exists
header = next(reader, None)
if not header or len(header) < 4:
# Try without skipping header
f.seek(0)
for row in reader:
if len(row) < 4: # Ensure we have enough columns
continue
name, url, username, password = row[:4]
# Create a PasswordEntry
entry = PasswordEntry(
website=name or url or "Unknown",
username=username,
password=password,
url=url
)
entries.append(entry)
except Exception as e:
raise IntegrationError(f"Error reading Chrome passwords export: {e}")
return entries
def import_entries(self, input_path: Optional[Path] = None) -> List[PasswordEntry]:
"""Import entries from a Chrome passwords export.
Args:
input_path: Path to the Chrome passwords export file
Returns:
List of imported PasswordEntry objects
"""
if input_path:
self.export_path = Path(input_path)
if not self.export_path or not self.export_path.exists():
raise IntegrationError("Chrome passwords export file not found")
return self.list_entries()
def export_entries(self, output_path: Path) -> bool:
"""Export entries to a Chrome-compatible CSV file.
Args:
output_path: Path to save the exported data
Returns:
bool: True if export was successful
"""
# This would require converting from our format to Chrome's format
# For now, we'll just raise a NotImplementedError
raise NotImplementedError("Export to Chrome format is not yet implemented")
@classmethod
def export_help(cls) -> str:
"""Get instructions for exporting from Chrome.
Returns:
str: Instructions for exporting from Chrome
"""
return """To export passwords from Chrome:
1. Open Chrome and go to: chrome://settings/passwords
2. Click the three dots menu next to 'Saved Passwords'
3. Select 'Export passwords...'
4. Follow the prompts to save the passwords to a CSV file
"""
# Register the integration with the name 'chrome'
register_integration("chrome")(ChromeIntegration)
|