mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 21:11:53 +00:00
feat(jira): add ui link and tenant info to ticket (#8607)
This commit is contained in:
@@ -39,6 +39,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
### Added
|
||||
- Add more fields for the Jira ticket and handle custom fields errors [(#8601)](https://github.com/prowler-cloud/prowler/pull/8601)
|
||||
- Support labels on Jira tickets [(#8603)](https://github.com/prowler-cloud/prowler/pull/8603)
|
||||
- Add finding url and tenant info inside Jira tickets [(#8607)](https://github.com/prowler-cloud/prowler/pull/8607)
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -744,6 +744,8 @@ class Jira:
|
||||
remediation_code_other: str = None,
|
||||
resource_tags: dict = None,
|
||||
compliance: dict = None,
|
||||
finding_url: str = None,
|
||||
tenant_info: str = None,
|
||||
) -> dict:
|
||||
table_rows = [
|
||||
{
|
||||
@@ -1321,6 +1323,93 @@ class Jira:
|
||||
}
|
||||
)
|
||||
|
||||
if finding_url:
|
||||
table_rows.append(
|
||||
{
|
||||
"type": "tableRow",
|
||||
"content": [
|
||||
{
|
||||
"type": "tableCell",
|
||||
"attrs": {"colwidth": [1]},
|
||||
"content": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Finding URL",
|
||||
"marks": [{"type": "strong"}],
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "tableCell",
|
||||
"attrs": {"colwidth": [3]},
|
||||
"content": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": finding_url,
|
||||
"marks": [
|
||||
{
|
||||
"type": "link",
|
||||
"attrs": {"href": finding_url},
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
if tenant_info:
|
||||
table_rows.append(
|
||||
{
|
||||
"type": "tableRow",
|
||||
"content": [
|
||||
{
|
||||
"type": "tableCell",
|
||||
"attrs": {"colwidth": [1]},
|
||||
"content": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Tenant Info",
|
||||
"marks": [{"type": "strong"}],
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "tableCell",
|
||||
"attrs": {"colwidth": [3]},
|
||||
"content": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": tenant_info,
|
||||
"marks": [{"type": "code"}],
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"type": "doc",
|
||||
"version": 1,
|
||||
@@ -1348,6 +1437,8 @@ class Jira:
|
||||
project_key: str = None,
|
||||
issue_type: str = None,
|
||||
issue_labels: list[str] = None,
|
||||
finding_url: str = None,
|
||||
tenant_info: str = None,
|
||||
):
|
||||
"""
|
||||
Send the findings to Jira
|
||||
@@ -1357,6 +1448,8 @@ class Jira:
|
||||
- project_key: The project key
|
||||
- issue_type: The issue type
|
||||
- issue_labels: The issue labels
|
||||
- finding_url: The finding URL
|
||||
- tenant_info: The tenant info
|
||||
|
||||
Raises:
|
||||
- JiraRefreshTokenError: Failed to refresh the access token
|
||||
@@ -1428,6 +1521,8 @@ class Jira:
|
||||
remediation_code_other=finding.metadata.Remediation.Code.Other,
|
||||
resource_tags=finding.resource_tags,
|
||||
compliance=finding.compliance,
|
||||
finding_url=finding_url,
|
||||
tenant_info=tenant_info,
|
||||
)
|
||||
payload = {
|
||||
"fields": {
|
||||
@@ -1447,7 +1542,15 @@ class Jira:
|
||||
)
|
||||
|
||||
if response.status_code != 201:
|
||||
response_json = response.json()
|
||||
try:
|
||||
response_json = response.json()
|
||||
except (ValueError, requests.exceptions.JSONDecodeError):
|
||||
response_error = f"Failed to send finding: {response.status_code} - {response.text}"
|
||||
logger.warning(response_error)
|
||||
raise JiraSendFindingsResponseError(
|
||||
message=response_error, file=os.path.basename(__file__)
|
||||
)
|
||||
|
||||
# Check if the error is due to required custom fields
|
||||
if response.status_code == 400 and "errors" in response_json:
|
||||
errors = response_json.get("errors", {})
|
||||
@@ -1475,7 +1578,13 @@ class Jira:
|
||||
message=response_error, file=os.path.basename(__file__)
|
||||
)
|
||||
else:
|
||||
logger.info(f"Finding sent successfully: {response.json()}")
|
||||
try:
|
||||
response_json = response.json()
|
||||
logger.info(f"Finding sent successfully: {response_json}")
|
||||
except (ValueError, requests.exceptions.JSONDecodeError):
|
||||
logger.info(
|
||||
f"Finding sent successfully: Status {response.status_code}"
|
||||
)
|
||||
except JiraRequiredCustomFieldsError as custom_fields_error:
|
||||
raise custom_fields_error
|
||||
except JiraRefreshTokenError as refresh_error:
|
||||
|
||||
@@ -610,6 +610,8 @@ class TestJiraIntegration:
|
||||
project_key="TEST-1",
|
||||
issue_type="Bug",
|
||||
issue_labels=["scan-mocked", "whatever"],
|
||||
finding_url="https://prowler-cloud-link/findings/12345",
|
||||
tenant_info="Tenant Info",
|
||||
)
|
||||
|
||||
mock_post.assert_called_once()
|
||||
@@ -683,6 +685,8 @@ class TestJiraIntegration:
|
||||
"Remediation Terraform",
|
||||
"Remediation CLI",
|
||||
"Remediation Other",
|
||||
"Finding URL",
|
||||
"Tenant Info",
|
||||
]
|
||||
|
||||
actual_keys = [key for key, _ in row_texts]
|
||||
@@ -722,6 +726,8 @@ class TestJiraIntegration:
|
||||
assert "Owner=SecurityTeam" in row_dict["Resource Tags"]
|
||||
assert "CIS: 2.1.1, 2.1.2" in row_dict["Compliance"]
|
||||
assert "NIST: AC-3, AC-6" in row_dict["Compliance"]
|
||||
assert "https://prowler-cloud-link/findings/12345" in row_dict["Finding URL"]
|
||||
assert "Tenant Info" in row_dict["Tenant Info"]
|
||||
|
||||
@patch.object(Jira, "get_access_token", return_value="valid_access_token")
|
||||
@patch.object(
|
||||
@@ -825,7 +831,12 @@ class TestJiraIntegration:
|
||||
|
||||
with pytest.raises(JiraRequiredCustomFieldsError):
|
||||
self.jira_integration.send_findings(
|
||||
findings=[finding], project_key="TEST-1", issue_type="Bug"
|
||||
findings=[finding],
|
||||
project_key="TEST-1",
|
||||
issue_type="Bug",
|
||||
issue_labels=["scan-mocked", "whatever"],
|
||||
finding_url="https://prowler-cloud-link/findings/12345",
|
||||
tenant_info="Tenant Info",
|
||||
)
|
||||
|
||||
@patch.object(Jira, "get_access_token", return_value="valid_access_token")
|
||||
@@ -882,7 +893,12 @@ class TestJiraIntegration:
|
||||
|
||||
with pytest.raises(JiraCreateIssueError):
|
||||
self.jira_integration.send_findings(
|
||||
findings=[finding], project_key="TEST-1", issue_type="Bug"
|
||||
findings=[finding],
|
||||
project_key="TEST-1",
|
||||
issue_type="Bug",
|
||||
issue_labels=["scan-mocked", "whatever"],
|
||||
finding_url="https://prowler-cloud-link/findings/12345",
|
||||
tenant_info="Tenant Info",
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user