|
16 | 16 |
|
17 | 17 | import base64 |
18 | 18 | import json |
| 19 | +import logging |
19 | 20 | from typing import Any |
20 | 21 |
|
| 22 | +from secops.chronicle.models import APIVersion |
21 | 23 | from secops.chronicle.utils.format_utils import remove_none_values |
22 | 24 | from secops.chronicle.utils.request_utils import ( |
23 | 25 | chronicle_paginated_request, |
24 | 26 | chronicle_request, |
25 | 27 | ) |
| 28 | +from secops.exceptions import APIError, SecOpsError |
26 | 29 |
|
27 | 30 | # Constants for size limits |
28 | 31 | MAX_LOG_SIZE = 10 * 1024 * 1024 # 10MB per log |
@@ -433,3 +436,121 @@ def run_parser( |
433 | 436 | print(f"Warning: Failed to parse statedump: {e}") |
434 | 437 |
|
435 | 438 | return result |
| 439 | + |
| 440 | + |
| 441 | +def trigger_github_checks( |
| 442 | + client: "ChronicleClient", |
| 443 | + associated_pr: str, |
| 444 | + log_type: str, |
| 445 | + timeout: int = 60, |
| 446 | +) -> dict[str, Any]: |
| 447 | + """Trigger GitHub checks for a parser. |
| 448 | +
|
| 449 | + Args: |
| 450 | + client: ChronicleClient instance |
| 451 | + associated_pr: The PR string (e.g., "owner/repo/pull/123"). |
| 452 | + log_type: The string name of the LogType enum. |
| 453 | + timeout: Optional request timeout in seconds (default: 60). |
| 454 | +
|
| 455 | + Returns: |
| 456 | + Dictionary containing the response details. |
| 457 | +
|
| 458 | + Raises: |
| 459 | + SecOpsError: If input is invalid. |
| 460 | + APIError: If the API request fails. |
| 461 | + """ |
| 462 | + |
| 463 | + if not isinstance(log_type, str) or len(log_type.strip()) < 2: |
| 464 | + raise SecOpsError("log_type must be a valid string of length >= 2") |
| 465 | + |
| 466 | + if not isinstance(associated_pr, str) or not associated_pr.strip(): |
| 467 | + raise SecOpsError("associated_pr must be a non-empty string") |
| 468 | + |
| 469 | + pr_parts = associated_pr.split("/") |
| 470 | + if len(pr_parts) != 4 or pr_parts[2] != "pull" or not pr_parts[3].isdigit(): |
| 471 | + raise SecOpsError( |
| 472 | + "associated_pr must be in the format 'owner/repo/pull/<number>'" |
| 473 | + ) |
| 474 | + if not isinstance(timeout, int) or timeout < 0: |
| 475 | + raise SecOpsError("timeout must be a non-negative integer") |
| 476 | + |
| 477 | + try: |
| 478 | + parsers = list_parsers(client, log_type=log_type) |
| 479 | + except APIError as e: |
| 480 | + raise APIError( |
| 481 | + f"Failed to fetch parsers for log type {log_type}: {e}" |
| 482 | + ) from e |
| 483 | + |
| 484 | + if not parsers: |
| 485 | + logging.info( |
| 486 | + "No parsers found for log type %s. Using fallback parser ID.", |
| 487 | + log_type, |
| 488 | + ) |
| 489 | + parser_name = f"logTypes/{log_type}/parsers/-" |
| 490 | + else: |
| 491 | + if len(parsers) > 1: |
| 492 | + logging.warning( |
| 493 | + "Multiple parsers found for log type %s. Using the first one.", |
| 494 | + log_type, |
| 495 | + ) |
| 496 | + parser_name = parsers[0]["name"] |
| 497 | + |
| 498 | + endpoint_path = f"{parser_name}:runAnalysis" |
| 499 | + payload = { |
| 500 | + "report_type": "GITHUB_PARSER_VALIDATION", |
| 501 | + "pull_request": associated_pr, |
| 502 | + } |
| 503 | + |
| 504 | + return chronicle_request( |
| 505 | + client=client, |
| 506 | + method="POST", |
| 507 | + api_version="v1alpha", |
| 508 | + endpoint_path=endpoint_path, |
| 509 | + json=payload, |
| 510 | + timeout=timeout, |
| 511 | + ) |
| 512 | + |
| 513 | + |
| 514 | +def get_analysis_report( |
| 515 | + client: "ChronicleClient", |
| 516 | + log_type: str, |
| 517 | + parser_id: str, |
| 518 | + report_id: str, |
| 519 | + timeout: int = 60, |
| 520 | +) -> dict[str, Any]: |
| 521 | + """Get a parser analysis report. |
| 522 | +
|
| 523 | + Args: |
| 524 | + client: ChronicleClient instance |
| 525 | + log_type: Log type of the parser. |
| 526 | + parser_id: The ID of the parser. |
| 527 | + report_id: The ID of the analysis report. |
| 528 | + timeout: Optional timeout in seconds (default: 60). |
| 529 | +
|
| 530 | + Returns: |
| 531 | + Dictionary containing the analysis report. |
| 532 | +
|
| 533 | + Raises: |
| 534 | + SecOpsError: If input is invalid. |
| 535 | + APIError: If the API request fails. |
| 536 | + """ |
| 537 | + if not isinstance(log_type, str) or not log_type.strip(): |
| 538 | + raise SecOpsError("log_type must be a non-empty string") |
| 539 | + if not isinstance(parser_id, str) or not parser_id.strip(): |
| 540 | + raise SecOpsError("parser_id must be a non-empty string") |
| 541 | + if not isinstance(report_id, str) or not report_id.strip(): |
| 542 | + raise SecOpsError("report_id must be a non-empty string") |
| 543 | + if not isinstance(timeout, int) or timeout < 0: |
| 544 | + raise SecOpsError("timeout must be a non-negative integer") |
| 545 | + |
| 546 | + endpoint_path = ( |
| 547 | + f"logTypes/{log_type}/parsers/{parser_id}/analysisReports/{report_id}" |
| 548 | + ) |
| 549 | + |
| 550 | + return chronicle_request( |
| 551 | + client=client, |
| 552 | + method="GET", |
| 553 | + api_version=APIVersion.V1ALPHA, |
| 554 | + endpoint_path=endpoint_path, |
| 555 | + timeout=timeout, |
| 556 | + ) |
0 commit comments