Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/gg_api_core/src/gg_api_core/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1152,12 +1152,12 @@ async def resolve_incident(self, incident_id: str) -> dict[str, Any]:
logger.info(f"Resolving incident {incident_id}")
return await self._request("POST", f"/incidents/secrets/{incident_id}/resolve")

async def ignore_incident(self, incident_id: str, ignore_reason: str = None) -> dict[str, Any]:
async def ignore_incident(self, incident_id: str, ignore_reason: str | None = None) -> dict[str, Any]:
"""Ignore a secret incident.

Args:
incident_id: ID of the secret incident
ignore_reason: Reason for ignoring (test_credential, false_positive, etc.)
ignore_reason: Reason for ignoring (test_credential, false_positive, low_risk, invalid)

Returns:
Status of the operation
Expand Down
65 changes: 43 additions & 22 deletions packages/gg_api_core/src/gg_api_core/tools/manage_incident.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,65 @@ class ManageIncidentParams(BaseModel):
"""Parameters for managing an incident."""

incident_id: str | int = Field(description="ID of the secret incident to manage")
action: Literal["assign", "unassign", "resolve", "ignore", "reopen"] = Field(
description="Action to perform on the incident"
action: Literal["unassign", "resolve", "ignore", "reopen"] = Field(
description="Action to perform on the incident: 'unassign' removes any assigned member, 'resolve' marks the incident as resolved, 'ignore' marks as ignored (use with ignore_reason), 'reopen' reopens a resolved or ignored incident"
)
assignee_id: str | int | None = Field(
default=None, description="ID of the member to assign the incident to (required for 'assign' action)"
)
ignore_reason: str | None = Field(
ignore_reason: Literal["test_credential", "false_positive", "low_risk", "invalid"] | None = Field(
default=None,
description="Reason for ignoring (test_credential, false_positive, etc.) (used with 'ignore' action)",
description="Reason for ignoring the incident. Only used with 'ignore' action. Options: 'test_credential' (secret is for testing), 'false_positive' (not a real secret), 'low_risk' (secret poses minimal risk), 'invalid' (secret is invalid/inactive)",
)
mine: bool = Field(default=False, description="If True, use the current user's ID for the assignee_id")


async def manage_private_incident(params: ManageIncidentParams) -> dict[str, Any]:
"""
Manage a secret incident (assign, unassign, resolve, ignore, reopen).
Perform lifecycle management actions on a secret incident.

This tool allows you to change the state of a secret incident through various actions:
- 'unassign': Remove the assigned member from an incident (useful when reassigning or leaving unassigned)
- 'resolve': Mark an incident as resolved (typically after the secret has been rotated/revoked)
- 'ignore': Mark an incident as ignored with a reason (test_credential, false_positive, low_risk, or invalid)
- 'reopen': Reopen a previously resolved or ignored incident

Note: To assign an incident to a member, use the dedicated 'assign_incident' tool instead.

Args:
params: ManageIncidentParams model containing incident management configuration
params: ManageIncidentParams containing:
- incident_id: The ID of the incident to manage
- action: The lifecycle action to perform (unassign, resolve, ignore, or reopen)
- ignore_reason: Required when action is 'ignore'. One of: test_credential, false_positive, low_risk, invalid

Returns:
Updated incident data
Dictionary containing the updated incident data from the API

Raises:
ToolError: If the action fails or if an invalid action is provided
"""
client = get_client()
logger.debug(f"Managing incident {params.incident_id} with action: {params.action}")

try:
# Make the API call
result = await client.manage_incident(
incident_id=params.incident_id,
action=params.action,
assignee_id=params.assignee_id,
ignore_reason=params.ignore_reason,
mine=params.mine,
)

logger.debug(f"Managed incident {params.incident_id}")
# Route the action to the appropriate client method
if params.action == "unassign":
result = await client.unassign_incident(incident_id=str(params.incident_id))

elif params.action == "resolve":
result = await client.resolve_incident(incident_id=str(params.incident_id))

elif params.action == "ignore":
result = await client.ignore_incident(
incident_id=str(params.incident_id), ignore_reason=params.ignore_reason
)

elif params.action == "reopen":
result = await client.reopen_incident(incident_id=str(params.incident_id))

else:
raise ToolError(f"Unknown action: {params.action}")

logger.debug(f"Successfully managed incident {params.incident_id} with action: {params.action}")
return result
except ToolError:
raise
except Exception as e:
logger.error(f"Error managing incident: {str(e)}")
raise ToolError(f"Error: {str(e)}")
Expand All @@ -77,7 +98,7 @@ async def update_incident_status(params: UpdateIncidentStatusParams) -> dict[str
logger.debug(f"Updating incident {params.incident_id} status to {params.status}")

try:
result = await client.update_incident_status(incident_id=params.incident_id, status=params.status)
result = await client.update_incident(incident_id=str(params.incident_id), status=params.status)
logger.debug(f"Updated incident {params.incident_id} status to {params.status}")
return result
except Exception as e:
Expand Down
Loading