From e136d697852f725516c8c60604b723b350d9802b Mon Sep 17 00:00:00 2001 From: Haim Kastner Date: Wed, 15 Apr 2026 12:05:23 +0300 Subject: [PATCH 01/10] Fix vulnerabilities --- .github/workflows/build.yml | 4 ++++ .github/workflows/release.yml | 10 +++++--- chkp_ai_security_sdk/core/session_manager.py | 5 ++-- examples/async_usage.py | 1 - sdk_generator/generate_sdk.py | 24 ++++++++++---------- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6881191..214e577 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,9 @@ on: branches: [main] workflow_dispatch: +permissions: + contents: read + env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true @@ -31,6 +34,7 @@ jobs: mkdir -p sdk_generator/open_api_tool wget -q https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.12.0/openapi-generator-cli-7.12.0.jar \ -O sdk_generator/open_api_tool/openapi-generator-cli-7.12.0.jar + echo "33e7dfa7a1f04d58405ee12ae19e2c6fc2a91497cf2e56fa68f1875a95cbf220 sdk_generator/open_api_tool/openapi-generator-cli-7.12.0.jar" | sha256sum -c - name: Build SDK run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ea1e782..b028414 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,9 @@ on: release: types: [published] +permissions: + contents: read + jobs: build-n-publish: name: Build and publish Python distributions to PyPI @@ -17,12 +20,13 @@ jobs: - uses: actions/setup-java@17f84c3641ba7b8f6deff6309fc4c864478f5d62 # v3 with: distribution: 'zulu' - java-version: '8' + java-version: '11' - name: Download OpenAPI Generator run: | mkdir -p sdk_generator/open_api_tool - wget -q https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/6.6.0/openapi-generator-cli-6.6.0.jar \ - -O sdk_generator/open_api_tool/openapi-generator-cli-6.6.0 + wget -q https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.12.0/openapi-generator-cli-7.12.0.jar \ + -O sdk_generator/open_api_tool/openapi-generator-cli-7.12.0.jar + echo "33e7dfa7a1f04d58405ee12ae19e2c6fc2a91497cf2e56fa68f1875a95cbf220 sdk_generator/open_api_tool/openapi-generator-cli-7.12.0.jar" | sha256sum -c - name: Build SDK run: | python -m pip install -r ./requirements.txt diff --git a/chkp_ai_security_sdk/core/session_manager.py b/chkp_ai_security_sdk/core/session_manager.py index cc801fe..db79aa6 100644 --- a/chkp_ai_security_sdk/core/session_manager.py +++ b/chkp_ai_security_sdk/core/session_manager.py @@ -13,7 +13,6 @@ CI_AUTH_PATH = '/auth/external' SOURCE_HEADER = 'ai-security-py-sdk' -VERIFY_CONTENT = False class SessionManager: @@ -49,7 +48,7 @@ def __perform_ci_login(self): response = requests.post(url=auth_url, data=json.dumps(payload), headers=headers) if not 200 <= response.status_code <= 299: - error_logger(f'CI login failed with status "{response.status_code}" payload: "{response.text}"') + error_logger(f'CI login failed with status "{response.status_code}" for session "{self.__session_id}"') raise WorkforceAIApiException( error_scope=WorkforceAIErrorScope.SERVICE, payload_error=response.text, @@ -59,7 +58,7 @@ def __perform_ci_login(self): response_json = response.json() if not response_json.get('success'): - error_logger(f'CI login failed for session "{self.__session_id}", error: {response_json}') + error_logger(f'CI login failed for session "{self.__session_id}"') raise WorkforceAIApiException(error_scope=WorkforceAIErrorScope.SERVICE, payload_error=str(response_json)) self.__jwt_token = response_json['data']['token'] diff --git a/examples/async_usage.py b/examples/async_usage.py index 0ed25fa..23812a5 100644 --- a/examples/async_usage.py +++ b/examples/async_usage.py @@ -46,7 +46,6 @@ async def main(): result = await ai.chats_policy_api.get_chats_rulebase_external_v1_chats_rulebase_get() print(result) - # Run multiple AI Security calls concurrently print('\n--- Concurrent AI Security calls ---') access, dlp = await asyncio.gather( diff --git a/sdk_generator/generate_sdk.py b/sdk_generator/generate_sdk.py index cf3193d..52b2229 100644 --- a/sdk_generator/generate_sdk.py +++ b/sdk_generator/generate_sdk.py @@ -61,18 +61,18 @@ def generate(product: dict): onerror=lambda err: print(f'{log_prefix} Error cleaning generated dir: {err}'), ) - cmd_line = ( - f'"{jre_path}" -jar {generator_path} generate' - f' --generator-name python' - f' --input-spec {specs_path}' - f' --output {project_dir}' - f' --template-dir {template_dir}' - f' --global-property modelDocs=false,modelTests=false' - f' --additional-properties=generateSourceCodeOnly=true,packageName={product["package_name"]}{library_prop}' - f' --skip-validate-spec' - ) - print(f'{log_prefix} Invoking generator:\n{cmd_line}') - subprocess.run(cmd_line, shell=True, check=True, stdout=sys.stdout) + cmd = [ + jre_path, '-jar', str(generator_path), 'generate', + '--generator-name', 'python', + '--input-spec', str(specs_path), + '--output', str(project_dir), + '--template-dir', str(template_dir), + '--global-property', 'modelDocs=false,modelTests=false', + '--additional-properties', f'generateSourceCodeOnly=true,packageName={product["package_name"]}{library_prop}', + '--skip-validate-spec', + ] + print(f'{log_prefix} Invoking generator:\n{" ".join(cmd)}') + subprocess.run(cmd, check=True, stdout=sys.stdout) except subprocess.CalledProcessError as e: print(f'{log_prefix} Generator error:\n\t{e}') From 5ab16fb42943748c2be680b22a6e11a1126228c6 Mon Sep 17 00:00:00 2001 From: Haim Kastner Date: Wed, 15 Apr 2026 12:12:02 +0300 Subject: [PATCH 02/10] Fix SAST --- chkp_ai_security_sdk/core/session_manager.py | 2 +- sdk_generator/scripts/fetch_api.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chkp_ai_security_sdk/core/session_manager.py b/chkp_ai_security_sdk/core/session_manager.py index db79aa6..0f00111 100644 --- a/chkp_ai_security_sdk/core/session_manager.py +++ b/chkp_ai_security_sdk/core/session_manager.py @@ -45,7 +45,7 @@ def __perform_ci_login(self): 'accessKey': self.__infinity_portal_auth.access_key, } headers = {'Content-Type': 'application/json'} - response = requests.post(url=auth_url, data=json.dumps(payload), headers=headers) + response = requests.post(url=auth_url, data=json.dumps(payload), headers=headers, timeout=30) if not 200 <= response.status_code <= 299: error_logger(f'CI login failed with status "{response.status_code}" for session "{self.__session_id}"') diff --git a/sdk_generator/scripts/fetch_api.py b/sdk_generator/scripts/fetch_api.py index ee6aa2a..9da6aa2 100644 --- a/sdk_generator/scripts/fetch_api.py +++ b/sdk_generator/scripts/fetch_api.py @@ -43,12 +43,12 @@ def __deposit_file(path, filename, content): def __download_spec(spec_name): print(f'[fetch-api] Fetching spec "{spec_name}" from SwaggerHub...') - res = requests.get(f'https://api.swaggerhub.com/apis/{API_SPEC_OWNER}/{spec_name}', headers=swagger_headers) + res = requests.get(f'https://api.swaggerhub.com/apis/{API_SPEC_OWNER}/{spec_name}', headers=swagger_headers, timeout=30) all_specs = res.json() latest = all_specs['apis'][-1] url = next((p['url'] for p in latest['properties'] if p['type'] == 'Swagger'), None) print(f'[fetch-api] Downloading from: {url}') - spec_res = requests.get(url, headers=swagger_headers) + spec_res = requests.get(url, headers=swagger_headers, timeout=30) return spec_res.json() From 4e21694a69890c955ca68d57eeed9ad885f64bc2 Mon Sep 17 00:00:00 2001 From: Haim Kastner Date: Wed, 15 Apr 2026 12:16:44 +0300 Subject: [PATCH 03/10] Fix SAST --- sdk_generator/generate_sdk.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk_generator/generate_sdk.py b/sdk_generator/generate_sdk.py index 52b2229..5e3c330 100644 --- a/sdk_generator/generate_sdk.py +++ b/sdk_generator/generate_sdk.py @@ -46,7 +46,9 @@ def generate(product: dict): project_dir = Path(this_dir_path, '../') specs_path = os.path.join(project_dir, 'resources', 'specs', product['spec_dir'], 'swagger.json') generator_path = os.path.join(this_dir_path, 'open_api_tool', 'openapi-generator-cli-7.12.0.jar') - jre_path = os.getenv('JRE_PATH', 'java') + jre_path = shutil.which(os.getenv('JRE_PATH', 'java')) + if not jre_path: + raise RuntimeError('Java runtime not found. Install Java or set JRE_PATH to a valid java executable.') generated_path = os.path.join(project_dir, product['generated_rel']) library = product.get('library', '') From 1a046028c973de4b56b11dd8ff942f3723995e15 Mon Sep 17 00:00:00 2001 From: Haim Kastner Date: Wed, 15 Apr 2026 12:21:47 +0300 Subject: [PATCH 04/10] Fix SAST --- sdk_generator/scripts/post_build.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk_generator/scripts/post_build.py b/sdk_generator/scripts/post_build.py index 2a80503..4ed26a1 100644 --- a/sdk_generator/scripts/post_build.py +++ b/sdk_generator/scripts/post_build.py @@ -59,11 +59,11 @@ def __prepare_build_info(product): def sdk_build_info() -> WorkforceAISDKInfo: return WorkforceAISDKInfo( - sdk_build="{sdk_build}", - sdk_version="{sdk_version}", - spec="{spec_name}", - spec_version="{spec_version}", - released_on="{released_on}", + sdk_build={repr(sdk_build)}, + sdk_version={repr(sdk_version)}, + spec={repr(spec_name)}, + spec_version={repr(spec_version)}, + released_on={repr(released_on)}, ) ''' with open(os.path.join(output_path, 'sdk_build.py'), 'w') as f: From d98f8993840ba574669b51dc4cb0608eacf04b2b Mon Sep 17 00:00:00 2001 From: Haim Kastner Date: Wed, 15 Apr 2026 12:28:07 +0300 Subject: [PATCH 05/10] Fix SAST --- sdk_generator/scripts/fetch_api.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sdk_generator/scripts/fetch_api.py b/sdk_generator/scripts/fetch_api.py index 9da6aa2..12da9f7 100644 --- a/sdk_generator/scripts/fetch_api.py +++ b/sdk_generator/scripts/fetch_api.py @@ -2,6 +2,7 @@ import json import shutil import requests +from urllib.parse import urlparse API_SPEC_OWNER = 'Check-Point' SWAGGERHUB_API_KEY = os.environ.get('SWAGGERHUB_API_KEY') @@ -47,6 +48,11 @@ def __download_spec(spec_name): all_specs = res.json() latest = all_specs['apis'][-1] url = next((p['url'] for p in latest['properties'] if p['type'] == 'Swagger'), None) + if not url: + raise ValueError(f'No Swagger URL found for spec "{spec_name}"') + parsed = urlparse(url) + if parsed.scheme != 'https' or not parsed.hostname.endswith('.swaggerhub.com'): + raise ValueError(f'Unexpected spec URL origin: {url}') print(f'[fetch-api] Downloading from: {url}') spec_res = requests.get(url, headers=swagger_headers, timeout=30) return spec_res.json() From bbeb2fddd77daf673f0505312530ea3a9b716b9a Mon Sep 17 00:00:00 2001 From: Haim Kastner Date: Wed, 15 Apr 2026 12:31:30 +0300 Subject: [PATCH 06/10] Fix SAST --- sdk_generator/scripts/post_build.py | 33 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/sdk_generator/scripts/post_build.py b/sdk_generator/scripts/post_build.py index 4ed26a1..b6c2bc3 100644 --- a/sdk_generator/scripts/post_build.py +++ b/sdk_generator/scripts/post_build.py @@ -49,22 +49,27 @@ def __prepare_build_info(product): with open(os.path.join(spec_path, 'spec'), 'r') as f: spec_name = f.readline().strip() - sdk_build = os.environ.get('BUILD_JOB_ID', '') - sdk_version = os.environ.get('BUILD_VERSION', '') - spec_version = swagger_spec['info']['version'] - released_on = datetime.now().isoformat() - - content = f''' -from {PKG_NAME}.classes.workforceai_sdk_info import WorkforceAISDKInfo + build_data = { + 'sdk_build': os.environ.get('BUILD_JOB_ID', ''), + 'sdk_version': os.environ.get('BUILD_VERSION', ''), + 'spec': spec_name, + 'spec_version': swagger_spec['info']['version'], + 'released_on': datetime.now().isoformat(), + } + + with open(os.path.join(output_path, 'sdk_build_data.json'), 'w') as f: + json.dump(build_data, f) + + pkg_name = PKG_NAME + content = f'''import json +import os +from {pkg_name}.classes.workforceai_sdk_info import WorkforceAISDKInfo def sdk_build_info() -> WorkforceAISDKInfo: - return WorkforceAISDKInfo( - sdk_build={repr(sdk_build)}, - sdk_version={repr(sdk_version)}, - spec={repr(spec_name)}, - spec_version={repr(spec_version)}, - released_on={repr(released_on)}, - ) + data_path = os.path.join(os.path.dirname(__file__), 'sdk_build_data.json') + with open(data_path, 'r') as f: + data = json.load(f) + return WorkforceAISDKInfo(**data) ''' with open(os.path.join(output_path, 'sdk_build.py'), 'w') as f: f.write(content) From 5c5e08c01953e37adfe18af7fc969dcee4a193da Mon Sep 17 00:00:00 2001 From: Haim Kastner Date: Wed, 15 Apr 2026 18:22:10 +0300 Subject: [PATCH 07/10] Fix SAST --- resources/templates/python/api_client.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/templates/python/api_client.mustache b/resources/templates/python/api_client.mustache index e249611..a29de0b 100644 --- a/resources/templates/python/api_client.mustache +++ b/resources/templates/python/api_client.mustache @@ -719,7 +719,7 @@ class ApiClient: content_disposition ) assert m is not None, "Unexpected 'content-disposition' header value" - filename = m.group(1) + filename = os.path.basename(m.group(1)) path = os.path.join(os.path.dirname(path), filename) with open(path, "wb") as f: From cd75c3c8c418270ff605d75f618e3995dd249d10 Mon Sep 17 00:00:00 2001 From: Haim Kastner Date: Wed, 15 Apr 2026 18:30:09 +0300 Subject: [PATCH 08/10] Fix SAST --- sdk_generator/generate_sdk.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk_generator/generate_sdk.py b/sdk_generator/generate_sdk.py index 5e3c330..8c853d8 100644 --- a/sdk_generator/generate_sdk.py +++ b/sdk_generator/generate_sdk.py @@ -46,9 +46,9 @@ def generate(product: dict): project_dir = Path(this_dir_path, '../') specs_path = os.path.join(project_dir, 'resources', 'specs', product['spec_dir'], 'swagger.json') generator_path = os.path.join(this_dir_path, 'open_api_tool', 'openapi-generator-cli-7.12.0.jar') - jre_path = shutil.which(os.getenv('JRE_PATH', 'java')) + jre_path = shutil.which('java') if not jre_path: - raise RuntimeError('Java runtime not found. Install Java or set JRE_PATH to a valid java executable.') + raise RuntimeError('Java runtime not found. Install Java or ensure java is on PATH.') generated_path = os.path.join(project_dir, product['generated_rel']) library = product.get('library', '') From 61c5a9a926d7a3cceed9fcec97a27810438ed36d Mon Sep 17 00:00:00 2001 From: Haim Kastner Date: Wed, 15 Apr 2026 20:04:15 +0300 Subject: [PATCH 09/10] Fixes --- chkp_ai_security_sdk/core/session_manager.py | 21 +++++++++----- resources/templates/python/api.mustache | 6 ++-- .../templates/python/api_client.mustache | 9 ++++-- sdk_generator/scripts/fetch_api.py | 29 +++++++++++++++++-- sdk_generator/scripts/post_build.py | 24 +++++++++++++++ setup.py | 2 +- 6 files changed, 76 insertions(+), 15 deletions(-) diff --git a/chkp_ai_security_sdk/core/session_manager.py b/chkp_ai_security_sdk/core/session_manager.py index 0f00111..12961b6 100644 --- a/chkp_ai_security_sdk/core/session_manager.py +++ b/chkp_ai_security_sdk/core/session_manager.py @@ -5,6 +5,8 @@ from typing import Any from urllib.parse import urlparse +_token_lock = threading.Lock() + from chkp_ai_security_sdk.classes.workforceai_api_exception import WorkforceAIApiException, WorkforceAIErrorScope from chkp_ai_security_sdk.classes.infinity_portal_auth import InfinityPortalAuth from chkp_ai_security_sdk.classes.sdk_connection_state import SDKConnectionState @@ -30,9 +32,11 @@ def __init__(self): def client_configuration(self) -> Any: self.__check_connected() from chkp_ai_security_sdk.generated.configuration import Configuration + with _token_lock: + token = self.__jwt_token configuration = Configuration() configuration.host = self.__url - configuration.access_token = self.__jwt_token + configuration.access_token = token configuration.client_id = self.__infinity_portal_auth.client_id if self.__infinity_portal_auth else None return configuration @@ -49,9 +53,10 @@ def __perform_ci_login(self): if not 200 <= response.status_code <= 299: error_logger(f'CI login failed with status "{response.status_code}" for session "{self.__session_id}"') + truncated_body = (response.text or '')[:500] raise WorkforceAIApiException( error_scope=WorkforceAIErrorScope.SERVICE, - payload_error=response.text, + payload_error=truncated_body, url=auth_url, status_code=response.status_code, ) @@ -59,11 +64,12 @@ def __perform_ci_login(self): response_json = response.json() if not response_json.get('success'): error_logger(f'CI login failed for session "{self.__session_id}"') - raise WorkforceAIApiException(error_scope=WorkforceAIErrorScope.SERVICE, payload_error=str(response_json)) + raise WorkforceAIApiException(error_scope=WorkforceAIErrorScope.SERVICE, payload_error=str(response_json)[:500]) - self.__jwt_token = response_json['data']['token'] - self.__token_expires_in = response_json['data'].get('expiresIn', 1800) - self.__sdk_connection_state = SDKConnectionState.CONNECTED + with _token_lock: + self.__jwt_token = response_json['data']['token'] + self.__token_expires_in = response_json['data'].get('expiresIn', 1800) + self.__sdk_connection_state = SDKConnectionState.CONNECTED logger(f'CI login succeeded for session "{self.__session_id}", token expires in {self.__token_expires_in}s') except WorkforceAIApiException: @@ -138,7 +144,8 @@ def disconnect(self): self.__sdk_connection_state = SDKConnectionState.DISCONNECTED self.__keep_alive_on_flag = False self.__keep_alive_event.set() # Wake up sleeping thread so it exits - self.__jwt_token = '' + with _token_lock: + self.__jwt_token = '' self.__session_id = str(uuid.uuid4()) def connection_state(self) -> SDKConnectionState: diff --git a/resources/templates/python/api.mustache b/resources/templates/python/api.mustache index 4ce0a74..1eaee5c 100644 --- a/resources/templates/python/api.mustache +++ b/resources/templates/python/api.mustache @@ -15,6 +15,7 @@ from {{packageName}}.api_response import ApiResponse from {{packageName}}.rest import RESTResponseType # TEMPLATE EDIT: import SDK logger from chkp_ai_security_sdk.core.logger import logger, error_logger, network_logger +import hashlib {{#operations}} @@ -38,7 +39,7 @@ class {{classname}}: # TEMPLATE EDIT: log outgoing request _client_id = getattr(self.api_client.configuration, 'client_id', None) or 'N/A' logger(f'Sending operation "{{operationId}}"...') - network_logger(f'Sending "{{operationId}}" {{httpMethod}} "{{{path}}}" | clientId={_client_id[:8]}...') + network_logger(f'Sending "{{operationId}}" {{httpMethod}} "{{{path}}}"') try: response_data = {{#asyncio}}await {{/asyncio}}self.api_client.call_api( *_param, @@ -70,8 +71,9 @@ class {{classname}}: {{>partial_api}} # TEMPLATE EDIT: log outgoing request _client_id = getattr(self.api_client.configuration, 'client_id', None) or 'N/A' + _client_id_hash = hashlib.sha256(_client_id.encode()).hexdigest()[:8] if _client_id != 'N/A' else 'N/A' logger(f'Sending operation "{{operationId}}_with_http_info"...') - network_logger(f'Sending "{{operationId}}" {{httpMethod}} "{{{path}}}" | clientId={_client_id[:8]}...') + network_logger(f'Sending "{{operationId}}" {{httpMethod}} "{{{path}}}" | clientId=sha256:{_client_id_hash}') try: response_data = {{#asyncio}}await {{/asyncio}}self.api_client.call_api( *_param, diff --git a/resources/templates/python/api_client.mustache b/resources/templates/python/api_client.mustache index a29de0b..b2bfd55 100644 --- a/resources/templates/python/api_client.mustache +++ b/resources/templates/python/api_client.mustache @@ -9,6 +9,7 @@ import decimal import json import mimetypes import os +import pathlib import re import tempfile @@ -718,8 +719,12 @@ class ApiClient: r'filename=[\'"]?([^\'"\s]+)[\'"]?', content_disposition ) - assert m is not None, "Unexpected 'content-disposition' header value" - filename = os.path.basename(m.group(1)) + if m is None: + raise ApiValueError("Unexpected 'content-disposition' header value") + filename = pathlib.PurePosixPath(m.group(1)).name + filename = pathlib.PureWindowsPath(filename).name + if not filename or filename in ('.', '..'): + raise ApiValueError("Invalid filename in 'content-disposition' header") path = os.path.join(os.path.dirname(path), filename) with open(path, "wb") as f: diff --git a/sdk_generator/scripts/fetch_api.py b/sdk_generator/scripts/fetch_api.py index 12da9f7..3d0981b 100644 --- a/sdk_generator/scripts/fetch_api.py +++ b/sdk_generator/scripts/fetch_api.py @@ -1,4 +1,5 @@ import os +import re import json import shutil import requests @@ -17,15 +18,36 @@ OUTPUT_BASE_PATH = 'resources/specs' SWAGGER_CONF = 'swagger.json' +ALLOWED_SPEC_PATTERN = re.compile(r'^[a-zA-Z0-9_-]+$') + + +def __validate_spec_name(name): + if not ALLOWED_SPEC_PATTERN.match(name): + raise ValueError(f'Invalid spec name "{name}": must be alphanumeric, hyphens, or underscores only') + return name + + +def __validate_local_path(local_path): + real = os.path.realpath(local_path) + project_root = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) + if not real.startswith(project_root + os.sep): + raise ValueError(f'Local spec path must be within the project directory: {local_path}') + if not os.path.isfile(real): + raise ValueError(f'Local spec path does not exist or is not a file: {local_path}') + if not real.endswith('.json'): + raise ValueError(f'Local spec path must be a .json file: {local_path}') + return real + + # Specs to fetch: (env var for local override, spec name, output subdir) SPECS = [ { - 'name': os.environ.get('SPEC_NAME', 'checkpoint-ai-security'), + 'name': __validate_spec_name(os.environ.get('SPEC_NAME', 'checkpoint-ai-security')), 'local_path_env': 'LOCAL_GENERATED_API_PATH', 'output_dir': 'main', }, { - 'name': os.environ.get('BROWSE_SPEC_NAME', 'checkpoint-browse-security'), + 'name': __validate_spec_name(os.environ.get('BROWSE_SPEC_NAME', 'checkpoint-browse-security')), 'local_path_env': 'LOCAL_BROWSE_SPEC_PATH', 'output_dir': 'browse', }, @@ -51,7 +73,7 @@ def __download_spec(spec_name): if not url: raise ValueError(f'No Swagger URL found for spec "{spec_name}"') parsed = urlparse(url) - if parsed.scheme != 'https' or not parsed.hostname.endswith('.swaggerhub.com'): + if parsed.scheme != 'https' or parsed.hostname != 'api.swaggerhub.com': raise ValueError(f'Unexpected spec URL origin: {url}') print(f'[fetch-api] Downloading from: {url}') spec_res = requests.get(url, headers=swagger_headers, timeout=30) @@ -70,6 +92,7 @@ def fetch_api_specs(): print(f'[fetch-api] Processing spec: {spec_name}') if local_path: + local_path = __validate_local_path(local_path) print(f'[fetch-api] Using local spec from: {local_path}') shutil.copy(local_path, os.path.join(out_dir, SWAGGER_CONF)) else: diff --git a/sdk_generator/scripts/post_build.py b/sdk_generator/scripts/post_build.py index b6c2bc3..6149890 100644 --- a/sdk_generator/scripts/post_build.py +++ b/sdk_generator/scripts/post_build.py @@ -76,6 +76,28 @@ def sdk_build_info() -> WorkforceAISDKInfo: print(f'[post-build:{product["label"]}] sdk_build.py written') +def __patch_configuration(product): + """Remove httplib.HTTPConnection.debuglevel changes from generated + configuration.py so that enabling SDK debug mode does not dump raw + HTTP traffic (including Authorization headers) to stdout.""" + config_path = os.path.join(product['generated_dir'], 'configuration.py') + if not os.path.isfile(config_path): + return + with open(config_path, 'r') as f: + content = f.read() + original = content + # Remove the lines that toggle httplib debug level + content = content.replace(' httplib.HTTPConnection.debuglevel = 1\n', '') + content = content.replace(' httplib.HTTPConnection.debuglevel = 0\n', '') + # Remove the now-unused httplib import if no other references remain + if 'httplib' not in content.split('import http.client as httplib')[-1]: + content = content.replace('import http.client as httplib\n', '') + if content != original: + with open(config_path, 'w') as f: + f.write(content) + print(f'[post-build:{product["label"]}] Patched configuration.py — removed httplib debug exposure') + + def __cleanup_generated(product): output_path = product['generated_dir'] for dirname in ['test', 'docs']: @@ -105,6 +127,8 @@ def post_build_process(): label = product['label'] print(f'[post-build:{label}] Preparing build info...') __prepare_build_info(product) + print(f'[post-build:{label}] Patching generated code...') + __patch_configuration(product) print(f'[post-build:{label}] Cleaning up generated files...') __cleanup_generated(product) diff --git a/setup.py b/setup.py index 4c50ca8..d6d86cc 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ maintainer_email='haimk@checkpoint.com', url='https://github.com/CheckPointSW/ai-security-py-sdk', packages=find_packages(exclude=['sdk_generator', 'scripts', 'tests']), - package_data={'': ['*']}, + package_data={'': ['*.json']}, install_requires=prod_dependencies, python_requires='>=3.9,<4.0', ) From 66400c1aa2088ace5c441f1e6b3ee2f9e57a9283 Mon Sep 17 00:00:00 2001 From: Haim Kastner Date: Wed, 15 Apr 2026 20:06:57 +0300 Subject: [PATCH 10/10] Fix linter --- chkp_ai_security_sdk/core/session_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chkp_ai_security_sdk/core/session_manager.py b/chkp_ai_security_sdk/core/session_manager.py index 12961b6..86b01d3 100644 --- a/chkp_ai_security_sdk/core/session_manager.py +++ b/chkp_ai_security_sdk/core/session_manager.py @@ -5,14 +5,14 @@ from typing import Any from urllib.parse import urlparse -_token_lock = threading.Lock() - from chkp_ai_security_sdk.classes.workforceai_api_exception import WorkforceAIApiException, WorkforceAIErrorScope from chkp_ai_security_sdk.classes.infinity_portal_auth import InfinityPortalAuth from chkp_ai_security_sdk.classes.sdk_connection_state import SDKConnectionState from chkp_ai_security_sdk.core.logger import logger, error_logger from chkp_ai_security_sdk.core.sdk_platform import KEEP_ALIVE_GRACE_SECONDS +_token_lock = threading.Lock() + CI_AUTH_PATH = '/auth/external' SOURCE_HEADER = 'ai-security-py-sdk'