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
25 changes: 16 additions & 9 deletions CheckmarxPythonSDK/CxOne/aiTriageAPI.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from urllib.parse import quote

from CheckmarxPythonSDK.api_client import ApiClient
from CheckmarxPythonSDK.CxOne.config import construct_configuration
Expand Down Expand Up @@ -109,19 +110,22 @@ def retrieve_ai_triage_results(

The recommended way to obtain group_id is to call GET /api/risks for
the specified project and use the groupId field of the corresponding
risk object. If group_id contains reserved URL characters (e.g. #),
URL-encode the value before passing it (replace # with %23).
risk object. group_id may contain reserved URL characters (e.g. #,
as in an SCA group id like "CVE-2015-4852#-#Maven-commons-collections:
commons-collections-3.2.1#-#2db0b158-3068-4d7e-86e6-0d922f22a69c");
this method URL-encodes it before using it as a path parameter, so
callers should pass the raw value.

Args:
project_id (str): Unique identifier of the project.
group_id (str): Identifier of the vulnerability group. URL-encode
if the value contains reserved characters.
group_id (str): Identifier of the vulnerability group, as
returned by the API (no need to pre-encode it).

Returns:
AiTriageResult: Triage verdict, summary, confidence score,
reachability, exploitability, and supporting analysis details.
"""
url = f"{self.base_url}/triage/{project_id}/{group_id}"
url = f"{self.base_url}/triage/{project_id}/{quote(str(group_id), safe='')}"
response = self.api_client.call_api(
method="GET",
url=url,
Expand Down Expand Up @@ -180,13 +184,16 @@ def retrieve_ai_triage_results(project_id: str, group_id: str) -> AiTriageResult

The recommended way to obtain group_id is to call GET /api/risks for the
specified project and use the groupId field of the corresponding risk
object. If group_id contains reserved URL characters (e.g. #), URL-encode
the value before passing it (replace # with %23).
object. group_id may contain reserved URL characters (e.g. #, as in an
SCA group id like "CVE-2015-4852#-#Maven-commons-collections:commons-
collections-3.2.1#-#2db0b158-3068-4d7e-86e6-0d922f22a69c"); this function
URL-encodes it before using it as a path parameter, so callers should
pass the raw value.

Args:
project_id (str): Unique identifier of the project.
group_id (str): Identifier of the vulnerability group. URL-encode if
the value contains reserved characters.
group_id (str): Identifier of the vulnerability group, as returned
by the API (no need to pre-encode it).

Returns:
AiTriageResult: Triage verdict, summary, confidence score,
Expand Down
8 changes: 4 additions & 4 deletions tests/CxOne/test_ai_triage_and_remediation_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@ def triage_status(project_id, sql_injection_result):
Used by remediation tests to skip when the result is already determined to
be non-exploitable, avoiding unnecessary credit consumption.
"""
group_id = quote(str(sql_injection_result.similarity_id), safe="")
try:
result = retrieve_ai_triage_results(project_id=project_id, group_id=group_id)
result = retrieve_ai_triage_results(
project_id=project_id, group_id=sql_injection_result.similarity_id
)
return result.triageStatus
except Exception:
return None
Expand Down Expand Up @@ -138,11 +139,10 @@ def test_get_ai_triage_status(project_id, scan_id, sql_injection_result):


def test_retrieve_ai_triage_results(project_id, scan_id, sql_injection_result):
group_id = quote(str(sql_injection_result.similarity_id), safe="")
try:
triage_result = retrieve_ai_triage_results(
project_id=project_id,
group_id=group_id,
group_id=sql_injection_result.similarity_id,
)
except Exception as e:
msg = str(e)
Expand Down
59 changes: 59 additions & 0 deletions tests/CxOne/test_ai_triage_api_unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from unittest.mock import MagicMock

from CheckmarxPythonSDK.CxOne.aiTriageAPI import AiTriageAPI


def _api_client(json_body=None):
client = MagicMock()
client.configuration.server_base_url = "https://example.com"
response = MagicMock()
response.json.return_value = json_body or {}
client.call_api.return_value = response
return client


class TestRetrieveAiTriageResults:

def test_group_id_with_hash_characters_is_url_encoded(self):
"""group_id values like SCA group ids ("CVE-...#-#pkg#-#uuid") must be
percent-encoded before being substituted into the path, otherwise the
'#' is treated as a URL fragment separator and the request 404s.
"""
client = _api_client()
api = AiTriageAPI(api_client=client)
group_id = (
"CVE-2015-4852#-#Maven-commons-collections:commons-collections-"
"3.2.1#-#2db0b158-3068-4d7e-86e6-0d922f22a69c"
)

api.retrieve_ai_triage_results(project_id="proj-1", group_id=group_id)

called_url = client.call_api.call_args.kwargs["url"]
assert "#" not in called_url
assert called_url == (
"https://example.com/api/ai-triage/triage/proj-1/"
"CVE-2015-4852%23-%23Maven-commons-collections%3Acommons-"
"collections-3.2.1%23-%232db0b158-3068-4d7e-86e6-0d922f22a69c"
)

def test_group_id_without_reserved_characters_is_unchanged(self):
client = _api_client()
api = AiTriageAPI(api_client=client)

api.retrieve_ai_triage_results(project_id="proj-1", group_id="12345")

called_url = client.call_api.call_args.kwargs["url"]
assert called_url == "https://example.com/api/ai-triage/triage/proj-1/12345"

def test_negative_integer_group_id_is_unchanged(self):
"""SAST similarity_id values can be negative integers (e.g. -501015144).
'-' is an unreserved URL character, so quote() must leave it as-is
rather than encoding it.
"""
client = _api_client()
api = AiTriageAPI(api_client=client)

api.retrieve_ai_triage_results(project_id="proj-1", group_id=-501015144)

called_url = client.call_api.call_args.kwargs["url"]
assert called_url == "https://example.com/api/ai-triage/triage/proj-1/-501015144"