| 138 | |
| 139 | |
| 140 | class BreachWatchScanCommand(Command): |
| 141 | def get_parser(self): # type: () -> Optional[argparse.ArgumentParser] |
| 142 | return breachwatch_scan_parser |
| 143 | |
| 144 | def execute(self, params, **kwargs): # type: (KeeperParams, Any) -> Any |
| 145 | records = [x[0] for x in params.breach_watch.get_records_to_scan(params)] |
| 146 | record_passwords = dict() # type: Dict[str, str] |
| 147 | for record in records: |
| 148 | if isinstance(record, vault.PasswordRecord): |
| 149 | if record.password: |
| 150 | record_passwords[record.record_uid] = record.password |
| 151 | elif isinstance(record, vault.TypedRecord): |
| 152 | password_field = record.get_typed_field('password') |
| 153 | if password_field: |
| 154 | password = password_field.get_default_value(str) |
| 155 | if password: |
| 156 | record_passwords[record.record_uid] = password |
| 157 | |
| 158 | if len(record_passwords): |
| 159 | euid_to_delete = [] |
| 160 | bw_requests = [] |
| 161 | all_passwords = set(record_passwords.values()) |
| 162 | scans = {x[0]: x[1] for x in params.breach_watch.scan_passwords(params, all_passwords)} |
| 163 | for record_uid, record_password in record_passwords.items(): |
| 164 | if params.breach_watch_records: |
| 165 | if record_uid in params.breach_watch_records: |
| 166 | bwr = params.breach_watch_records[record_uid] |
| 167 | if 'data_unencrypted' in bwr: |
| 168 | passwords = bwr['data_unencrypted'].get('passwords', []) |
| 169 | for password in passwords: |
| 170 | euid = password.get('euid') |
| 171 | if euid: |
| 172 | euid_to_delete.append(base64.b64decode(euid)) |
| 173 | if record_password in scans: |
| 174 | bwrq = breachwatch_pb2.BreachWatchRecordRequest() |
| 175 | bwrq.recordUid = utils.base64_url_decode(record_uid) |
| 176 | bwrq.breachWatchInfoType = breachwatch_pb2.RECORD |
| 177 | bwrq.updateUserWhoScanned = True |
| 178 | hash_status = scans[record_password] |
| 179 | bw_password = client_pb2.BWPassword() |
| 180 | bw_password.value = record_password |
| 181 | bw_password.status = client_pb2.WEAK if hash_status.breachDetected else client_pb2.GOOD |
| 182 | bw_password.euid = hash_status.euid |
| 183 | bw_data = client_pb2.BreachWatchData() |
| 184 | bw_data.passwords.append(bw_password) |
| 185 | data = bw_data.SerializeToString() |
| 186 | try: |
| 187 | record_key = params.record_cache[record_uid]['record_key_unencrypted'] |
| 188 | bwrq.encryptedData = crypto.encrypt_aes_v2(data, record_key) |
| 189 | except: |
| 190 | continue |
| 191 | bw_requests.append(bwrq) |
| 192 | while bw_requests: |
| 193 | chunk = bw_requests[0:999] |
| 194 | bw_requests = bw_requests[999:] |
| 195 | rq = breachwatch_pb2.BreachWatchUpdateRequest() |
| 196 | rq.breachWatchRecordRequest.extend(chunk) |
| 197 | api.communicate_rest(params, rq, 'breachwatch/update_record_data', |