|
| 1 | +import csv |
1 | 2 | import logging |
2 | 3 | import os |
3 | 4 | from io import BytesIO |
| 5 | +from typing import Dict |
| 6 | +from typing import List |
4 | 7 | from typing import Optional |
5 | 8 | from email.utils import parsedate_to_datetime |
6 | 9 |
|
@@ -181,7 +184,7 @@ def index_by_txt_file_command(path: str, index_as: str, family_name: str): |
181 | 184 | except sdk_errors.IntezerError as e: |
182 | 185 | index_exceptions.append(f'Failed to index hash: {sha256} error: {e}') |
183 | 186 | logger.exception('Failed to index hash', extra=dict(sha256=sha256)) |
184 | | - except sdk_errors.IndexFailed: |
| 187 | + except sdk_errors.IndexFailed as e: |
185 | 188 | index_exceptions.append(f'Failed to index hash: {sha256} error: {e}') |
186 | 189 | logger.exception('Failed to index hash', extra=dict(sha256=sha256)) |
187 | 190 | index_progress.update(1) |
@@ -355,7 +358,7 @@ def send_phishing_emails_from_directory_command(path: str, |
355 | 358 | unsupported_number = 0 |
356 | 359 | emails_dates = [] |
357 | 360 |
|
358 | | - for root, dirs, files in os.walk(path): |
| 361 | + for root, _, files in os.walk(path): |
359 | 362 | files = [f for f in files if not is_hidden(os.path.join(root, f))] |
360 | 363 |
|
361 | 364 | number_of_files = len(files) |
@@ -385,7 +388,7 @@ def send_phishing_emails_from_directory_command(path: str, |
385 | 388 | except Exception: |
386 | 389 | continue |
387 | 390 |
|
388 | | - except sdk_errors.IntezerError as ex: |
| 391 | + except sdk_errors.IntezerError: |
389 | 392 | logger.exception('Error while analyzing directory') |
390 | 393 | failed_number += 1 |
391 | 394 | except Exception: |
@@ -449,3 +452,98 @@ def _create_analysis_id_file(directory: str, analysis_id: str): |
449 | 452 | logger.exception('Could not create analysis_id.txt file', extra=dict(directory=directory)) |
450 | 453 | click.echo(f'Could not create analysis_id.txt file in {directory}') |
451 | 454 | raise |
| 455 | + |
| 456 | + |
| 457 | +def notify_alerts_from_csv_command(csv_path: str): |
| 458 | + """ |
| 459 | + Notify alerts from a CSV file containing alert IDs and environments. |
| 460 | + |
| 461 | + :param csv_path: Path to CSV file with 'id' and 'environment' columns |
| 462 | + """ |
| 463 | + try: |
| 464 | + alerts_data = _read_alerts_from_csv(csv_path) |
| 465 | + success_number = 0 |
| 466 | + failed_number = 0 |
| 467 | + no_channels_number = 0 |
| 468 | + |
| 469 | + with click.progressbar(length=len(alerts_data), |
| 470 | + label='Notifying alerts', |
| 471 | + show_pos=True, |
| 472 | + width=0) as progressbar: |
| 473 | + for alert_data in alerts_data: |
| 474 | + alert_id = alert_data['id'] |
| 475 | + environment = alert_data['environment'] |
| 476 | + |
| 477 | + try: |
| 478 | + alert = Alert(alert_id=alert_id, environment=environment) |
| 479 | + notified_channels = alert.notify() |
| 480 | + |
| 481 | + if notified_channels: |
| 482 | + success_number += 1 |
| 483 | + else: |
| 484 | + no_channels_number += 1 |
| 485 | + logger.info('Alert notified but no channels configured', |
| 486 | + extra=dict(alert_id=alert_id, environment=environment)) |
| 487 | + |
| 488 | + except sdk_errors.AlertNotFoundError: |
| 489 | + click.echo(f'Alert {alert_id} not found') |
| 490 | + logger.info('Alert not found', extra=dict(alert_id=alert_id, environment=environment)) |
| 491 | + failed_number += 1 |
| 492 | + except sdk_errors.AlertInProgressError: |
| 493 | + click.echo(f'Alert {alert_id} is still in progress') |
| 494 | + logger.info('Alert in progress', extra=dict(alert_id=alert_id, environment=environment)) |
| 495 | + failed_number += 1 |
| 496 | + except sdk_errors.IntezerError as e: |
| 497 | + logger.exception('Error while notifying alert', extra=dict(alert_id=alert_id, environment=environment)) |
| 498 | + failed_number += 1 |
| 499 | + except Exception: |
| 500 | + logger.exception('Unexpected error while notifying alert', extra=dict(alert_id=alert_id, environment=environment)) |
| 501 | + failed_number += 1 |
| 502 | + |
| 503 | + progressbar.update(1) |
| 504 | + |
| 505 | + if success_number > 0: |
| 506 | + click.echo(f'{success_number} alerts notified successfully') |
| 507 | + |
| 508 | + if no_channels_number > 0: |
| 509 | + click.echo(f'{no_channels_number} alerts didn\'t triggered any notification') |
| 510 | + |
| 511 | + if failed_number > 0: |
| 512 | + click.echo(f'{failed_number} alerts failed to notify') |
| 513 | + |
| 514 | + except IOError: |
| 515 | + click.echo(f'No read permissions for {csv_path}') |
| 516 | + logger.exception('Error reading CSV file', extra=dict(path=csv_path)) |
| 517 | + raise click.Abort() |
| 518 | + except Exception: |
| 519 | + logger.exception('Unexpected error occurred while processing CSV file', extra=dict(path=csv_path)) |
| 520 | + click.echo('Unexpected error occurred while processing CSV file') |
| 521 | + raise click.Abort() |
| 522 | + |
| 523 | + |
| 524 | +def _read_alerts_from_csv(csv_path: str) -> List[Dict[str, Optional[str]]]: |
| 525 | + """ |
| 526 | + Read alert IDs and environments from CSV file. |
| 527 | + |
| 528 | + :param csv_path: Path to CSV file |
| 529 | + :return: List of dictionaries with 'id' and 'environment' keys |
| 530 | + :raises ValueError: If required columns are missing |
| 531 | + """ |
| 532 | + alerts_data = [] |
| 533 | + |
| 534 | + with open(csv_path, 'r', newline='', encoding='utf-8-sig') as csvfile: |
| 535 | + reader = csv.DictReader(csvfile) |
| 536 | + |
| 537 | + if not reader.fieldnames or 'id' not in reader.fieldnames: |
| 538 | + raise ValueError('CSV file must contain an "id" column') |
| 539 | + |
| 540 | + for row in reader: |
| 541 | + alert_id = row['id'].strip() |
| 542 | + environment = row['environment'].strip() |
| 543 | + |
| 544 | + alerts_data.append({'id': alert_id, 'environment': environment}) |
| 545 | + |
| 546 | + if not alerts_data: |
| 547 | + raise ValueError('No valid alert data found in CSV file') |
| 548 | + |
| 549 | + return alerts_data |
0 commit comments