|
21 | 21 | import json |
22 | 22 | import sys |
23 | 23 | from typing import Optional |
| 24 | +from enum import Enum |
24 | 25 | import config |
25 | 26 | from utils import debug_print |
26 | 27 |
|
| 28 | +# Define failure categories as an enum to ensure consistency |
| 29 | +class FailureCategory(Enum): |
| 30 | + INITIAL_BUILD_FAILURE = "INITIAL_BUILD_FAILURE" |
| 31 | + EXCEEDED_QA_ATTEMPTS = "EXCEEDED_QA_ATTEMPTS" |
| 32 | + QA_AGENT_FAILURE = "QA_AGENT_FAILURE" |
| 33 | + GIT_COMMAND_FAILURE = "GIT_COMMAND_FAILURE" |
| 34 | + AGENT_FAILURE = "AGENT_FAILURE" |
| 35 | + GENERATE_PR_FAILURE = "GENERATE_PR_FAILURE" |
| 36 | + HANDLE_PR_MERGE_FAILURE = "HANDLE_PR_MERGE_FAILURE" |
| 37 | + HANDLE_PR_CLOSE_FAILURE = "HANDLE_PR_CLOSE_FAILURE" |
| 38 | + GENERAL_FAILURE = "GENERAL_FAILURE" |
| 39 | + |
27 | 40 | def normalize_host(host: str) -> str: |
28 | 41 | """Remove any protocol prefix from host to prevent double prefixing when constructing URLs.""" |
29 | 42 | return host.replace('https://', '').replace('http://', '') |
@@ -303,3 +316,66 @@ def notify_remediation_pr_closed(remediation_id: str, contrast_host: str, contra |
303 | 316 | except json.JSONDecodeError: |
304 | 317 | print(f"Error decoding JSON response when notifying Remediation service about closed PR for remediation {remediation_id}.", file=sys.stderr) |
305 | 318 | return False |
| 319 | + |
| 320 | +def notify_remediation_failed(remediation_id: str, failure_category: str, contrast_host: str, contrast_org_id: str, contrast_app_id: str, contrast_auth_key: str, contrast_api_key: str) -> bool: |
| 321 | + """Notifies the Remediation backend service that a remediation has failed. |
| 322 | +
|
| 323 | + Args: |
| 324 | + remediation_id: The ID of the remediation. |
| 325 | + failure_category: The category of failure (e.g., "INITIAL_BUILD_FAILURE"). |
| 326 | + contrast_host: The Contrast Security host URL. |
| 327 | + contrast_org_id: The organization ID. |
| 328 | + contrast_app_id: The application ID. |
| 329 | + contrast_auth_key: The Contrast authorization key. |
| 330 | + contrast_api_key: The Contrast API key. |
| 331 | +
|
| 332 | + Returns: |
| 333 | + bool: True if the notification was successful, False otherwise. |
| 334 | + """ |
| 335 | + debug_print(f"--- Notifying Remediation service about failed remediation {remediation_id} with category {failure_category} ---") |
| 336 | + api_url = f"https://{normalize_host(contrast_host)}/api/v4/aiml-remediation/organizations/{contrast_org_id}/applications/{contrast_app_id}/remediations/{remediation_id}/failed" |
| 337 | + |
| 338 | + headers = { |
| 339 | + "Authorization": contrast_auth_key, |
| 340 | + "API-Key": contrast_api_key, |
| 341 | + "Content-Type": "application/json", |
| 342 | + "Accept": "application/json", |
| 343 | + "User-Agent": config.USER_AGENT |
| 344 | + } |
| 345 | + |
| 346 | + payload = { |
| 347 | + "failureCategory": failure_category |
| 348 | + } |
| 349 | + |
| 350 | + try: |
| 351 | + debug_print(f"Making PUT request to: {api_url}") |
| 352 | + debug_print(f"Payload: {json.dumps(payload)}") |
| 353 | + response = requests.put(api_url, headers=headers, json=payload) |
| 354 | + response.raise_for_status() # Raises an HTTPError for bad responses (4xx or 5xx) |
| 355 | + |
| 356 | + debug_print(f"Remediation failed notification API response status code: {response.status_code}") |
| 357 | + |
| 358 | + if response.status_code == 204: |
| 359 | + debug_print(f"Successfully notified Remediation service API about failed remediation {remediation_id}") |
| 360 | + return True |
| 361 | + else: |
| 362 | + error_message = "Unknown error" |
| 363 | + try: |
| 364 | + response_json = response.json() |
| 365 | + if "messages" in response_json and response_json["messages"]: |
| 366 | + error_message = response_json["messages"][0] |
| 367 | + except: |
| 368 | + error_message = response.text |
| 369 | + |
| 370 | + print(f"Failed to notify Remediation service about failed remediation {remediation_id}. Error: {error_message}", file=sys.stderr) |
| 371 | + return False |
| 372 | + |
| 373 | + except requests.exceptions.HTTPError as e: |
| 374 | + print(f"HTTP error notifying Remediation service about failed remediation {remediation_id}: {e.response.status_code} - {e.response.text}", file=sys.stderr) |
| 375 | + return False |
| 376 | + except requests.exceptions.RequestException as e: |
| 377 | + print(f"Request error notifying Remediation service about failed remediation {remediation_id}: {e}", file=sys.stderr) |
| 378 | + return False |
| 379 | + except json.JSONDecodeError: |
| 380 | + print(f"Error decoding JSON response when notifying Remediation service about failed remediation {remediation_id}.", file=sys.stderr) |
| 381 | + return False |
0 commit comments