feat(jira): add force accept language for requests (#8674)

This commit is contained in:
Pedro Martín
2025-09-09 09:32:25 +02:00
committed by GitHub
parent 74bf0e6b47
commit 64c43a288d
3 changed files with 142 additions and 36 deletions
+1
View File
@@ -13,6 +13,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- Support color for MANUAL finidngs in Jira tickets [(#8642)](https://github.com/prowler-cloud/prowler/pull/8642)
- `--excluded-checks-file` flag [(#8301)](https://github.com/prowler-cloud/prowler/pull/8301)
- Send finding in Jira integration with the needed values [(#8648)](https://github.com/prowler-cloud/prowler/pull/8648)
- Add language enforcement for Jira requests [(#8674)](https://github.com/prowler-cloud/prowler/pull/8674)
- MongoDB Atlas provider with 10 security checks [(#8312)](https://github.com/prowler-cloud/prowler/pull/8312)
- `clusters_authentication_enabled` - Ensure clusters have authentication enabled
- `clusters_backup_enabled` - Ensure clusters have backup enabled
+39 -36
View File
@@ -136,6 +136,11 @@ class Jira:
}
TOKEN_URL = "https://auth.atlassian.com/oauth/token"
API_TOKEN_URL = "https://api.atlassian.com/oauth/token/accessible-resources"
HEADER_TEMPLATE = {
"Content-Type": "application/json",
"X-Force-Accept-Language": "true",
"Accept-Language": "en",
}
def __init__(
self,
@@ -200,6 +205,31 @@ class Jira:
def using_basic_auth(self):
return self._using_basic_auth
def get_headers(
self, access_token: str = None, content_type_json: bool = False
) -> dict:
"""Get headers for API requests
Args:
access_token: The access token to use for authorization
content_type_json: Whether to include Content-Type: application/json
Returns:
dict: Headers for API requests
"""
headers = self.HEADER_TEMPLATE.copy()
if not content_type_json:
headers.pop("Content-Type", None)
if access_token:
if self._using_basic_auth:
headers["Authorization"] = f"Basic {access_token}"
else:
headers["Authorization"] = f"Bearer {access_token}"
return headers
def get_params(self, state_encoded):
return {
**self.PARAMS_TEMPLATE,
@@ -303,7 +333,7 @@ class Jira:
"redirect_uri": self.redirect_uri,
}
headers = {"Content-Type": "application/json"}
headers = self.get_headers(content_type_json=True)
response = requests.post(self.TOKEN_URL, json=body, headers=headers)
if response.status_code == 200:
@@ -352,7 +382,7 @@ class Jira:
"""
try:
if self._using_basic_auth:
headers = {"Authorization": f"Basic {access_token}"}
headers = self.get_headers(access_token)
response = requests.get(
f"https://{domain}.atlassian.net/_edge/tenant_info",
headers=headers,
@@ -360,7 +390,7 @@ class Jira:
response = response.json()
return response.get("cloudId")
else:
headers = {"Authorization": f"Bearer {access_token}"}
headers = self.get_headers(access_token)
response = requests.get(self.API_TOKEN_URL, headers=headers)
if response.status_code == 200:
@@ -442,7 +472,7 @@ class Jira:
"refresh_token": self._refresh_token,
}
headers = {"Content-Type": "application/json"}
headers = self.get_headers(content_type_json=True)
response = requests.post(url, json=body, headers=headers)
if response.status_code == 200:
@@ -582,10 +612,7 @@ class Jira:
if not access_token:
return ValueError("Failed to get access token")
if self._using_basic_auth:
headers = {"Authorization": f"Basic {access_token}"}
else:
headers = {"Authorization": f"Bearer {access_token}"}
headers = self.get_headers(access_token)
response = requests.get(
f"https://api.atlassian.com/ex/jira/{self.cloud_id}/rest/api/3/project",
@@ -652,10 +679,7 @@ class Jira:
file=os.path.basename(__file__),
)
if self._using_basic_auth:
headers = {"Authorization": f"Basic {access_token}"}
else:
headers = {"Authorization": f"Bearer {access_token}"}
headers = self.get_headers(access_token)
response = requests.get(
f"https://api.atlassian.com/ex/jira/{self.cloud_id}/rest/api/3/issue/createmeta?projectKeys={project_key}&expand=projects.issuetypes.fields",
@@ -700,10 +724,7 @@ class Jira:
if not access_token:
return ValueError("Failed to get access token")
if self._using_basic_auth:
headers = {"Authorization": f"Basic {access_token}"}
else:
headers = {"Authorization": f"Bearer {access_token}"}
headers = self.get_headers(access_token)
response = requests.get(
f"https://api.atlassian.com/ex/jira/{self.cloud_id}/rest/api/3/project",
@@ -1579,16 +1600,7 @@ class Jira:
message="The issue type is invalid", file=os.path.basename(__file__)
)
if self._using_basic_auth:
headers = {
"Authorization": f"Basic {access_token}",
"Content-Type": "application/json",
}
else:
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
headers = self.get_headers(access_token, content_type_json=True)
for finding in findings:
status_color = self.get_color_from_status(finding.status.value)
@@ -1795,16 +1807,7 @@ class Jira:
message="The issue type is invalid", file=os.path.basename(__file__)
)
if self._using_basic_auth:
headers = {
"Authorization": f"Basic {access_token}",
"Content-Type": "application/json",
}
else:
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
headers = self.get_headers(access_token, content_type_json=True)
status_color = self.get_color_from_status(status)
severity_color = self.get_severity_color(severity.lower())
+102
View File
@@ -721,6 +721,8 @@ class TestJiraIntegration:
expected_headers = {
"Authorization": "Bearer valid_access_token",
"Content-Type": "application/json",
"X-Force-Accept-Language": "true",
"Accept-Language": "en",
}
assert call_args[0][0] == expected_url
@@ -1418,3 +1420,103 @@ class TestJiraIntegration:
assert result is False
mock_post.assert_called_once()
def test_get_headers_oauth_with_access_token(self):
"""Test get_headers returns correct OAuth headers with access token."""
self.jira_integration._using_basic_auth = False
headers = self.jira_integration.get_headers(
access_token="test_oauth_token", content_type_json=True
)
expected_headers = {
"Authorization": "Bearer test_oauth_token",
"Content-Type": "application/json",
"X-Force-Accept-Language": "true",
"Accept-Language": "en",
}
assert headers == expected_headers
def test_get_headers_oauth_without_content_type(self):
"""Test get_headers returns OAuth headers without Content-Type when content_type_json=False."""
self.jira_integration._using_basic_auth = False
headers = self.jira_integration.get_headers(
access_token="test_oauth_token", content_type_json=False
)
expected_headers = {
"Authorization": "Bearer test_oauth_token",
"X-Force-Accept-Language": "true",
"Accept-Language": "en",
}
assert headers == expected_headers
assert "Content-Type" not in headers
def test_get_headers_basic_auth_with_access_token(self):
"""Test get_headers returns correct Basic Auth headers with access token."""
self.jira_integration_basic_auth._using_basic_auth = True
headers = self.jira_integration_basic_auth.get_headers(
access_token="test_basic_token", content_type_json=True
)
expected_headers = {
"Authorization": "Basic test_basic_token",
"Content-Type": "application/json",
"X-Force-Accept-Language": "true",
"Accept-Language": "en",
}
assert headers == expected_headers
def test_get_headers_basic_auth_without_content_type(self):
"""Test get_headers returns Basic Auth headers without Content-Type when content_type_json=False."""
self.jira_integration_basic_auth._using_basic_auth = True
headers = self.jira_integration_basic_auth.get_headers(
access_token="test_basic_token", content_type_json=False
)
expected_headers = {
"Authorization": "Basic test_basic_token",
"X-Force-Accept-Language": "true",
"Accept-Language": "en",
}
assert headers == expected_headers
assert "Content-Type" not in headers
def test_get_headers_without_access_token_with_content_type(self):
"""Test get_headers returns headers without Authorization when no access token provided."""
headers = self.jira_integration.get_headers(content_type_json=True)
expected_headers = {
"Content-Type": "application/json",
"X-Force-Accept-Language": "true",
"Accept-Language": "en",
}
assert headers == expected_headers
assert "Authorization" not in headers
def test_get_headers_without_access_token_without_content_type(self):
"""Test get_headers returns minimal headers when no access token and no content type."""
headers = self.jira_integration.get_headers(content_type_json=False)
expected_headers = {
"X-Force-Accept-Language": "true",
"Accept-Language": "en",
}
assert headers == expected_headers
assert "Authorization" not in headers
assert "Content-Type" not in headers
def test_get_headers_default_parameters(self):
"""Test get_headers with default parameters (no access token, no content type)."""
headers = self.jira_integration.get_headers()
expected_headers = {
"X-Force-Accept-Language": "true",
"Accept-Language": "en",
}
assert headers == expected_headers
assert "Authorization" not in headers
assert "Content-Type" not in headers