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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "socketdev"
version = "3.0.5"
version = "3.0.6"
requires-python = ">= 3.9"
dependencies = [
'requests',
Expand Down
7 changes: 4 additions & 3 deletions socketdev/diffscans/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
import logging
from typing import Any, Dict, Optional, Union
from typing import Any, Dict, List, Optional, Union
from ..utils import Utils

log = logging.getLogger("socketdev")
Expand Down Expand Up @@ -30,7 +30,7 @@ def get(self, org_slug: str, diff_scan_id: str) -> dict:
log.error(f"Error fetching diff scan: {response.status_code}, message: {response.text}")
return {}

def create_from_repo(self, org_slug: str, repo_slug: str, files: list, params: Optional[Dict[str, Any]] = None, use_lazy_loading: bool = False, workspace: str = None, max_open_files: int = 100, base_path: str = None) -> dict:
def create_from_repo(self, org_slug: str, repo_slug: str, files: list, params: Optional[Dict[str, Any]] = None, use_lazy_loading: bool = False, workspace: str = None, max_open_files: int = 100, base_path: str = None, base_paths: list = None) -> dict:
"""
Create a diff scan from repo HEAD, uploading files as multipart form data.

Expand All @@ -46,6 +46,7 @@ def create_from_repo(self, org_slug: str, repo_slug: str, files: list, params: O
max_open_files: Maximum number of files to keep open simultaneously when using
lazy loading. Useful for systems with low ulimit values (default: 100)
base_path: Optional base path to strip from key names for cleaner file organization
base_paths: Optional list of base paths to strip from key names (takes precedence over base_path)

Returns:
dict: API response containing diff scan results
Expand All @@ -64,7 +65,7 @@ def create_from_repo(self, org_slug: str, repo_slug: str, files: list, params: O

# Use lazy loading if requested
if use_lazy_loading:
prepared_files = Utils.load_files_for_sending_lazy(files, workspace, max_open_files, base_path)
prepared_files = Utils.load_files_for_sending_lazy(files, workspace, max_open_files, base_path, base_paths)
else:
prepared_files = files

Expand Down
5 changes: 3 additions & 2 deletions socketdev/fullscans/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ def get(self, org_slug: str, params: dict, use_types: bool = False) -> Union[dic
)
return {}

def post(self, files: list, params: FullScanParams, use_types: bool = False, use_lazy_loading: bool = False, workspace: str = None, max_open_files: int = 100, base_path: str = None) -> Union[dict, CreateFullScanResponse]:
def post(self, files: list, params: FullScanParams, use_types: bool = False, use_lazy_loading: bool = False, workspace: str = None, max_open_files: int = 100, base_path: str = None, base_paths: List[str] = None) -> Union[dict, CreateFullScanResponse]:
"""
Create a new full scan by uploading manifest files.

Expand All @@ -743,6 +743,7 @@ def post(self, files: list, params: FullScanParams, use_types: bool = False, use
max_open_files: Maximum number of files to keep open simultaneously when using
lazy loading. Useful for systems with low ulimit values (default: 100)
base_path: Optional base path to strip from key names for cleaner file organization
base_paths: Optional list of base paths to strip from key names (takes precedence over base_path)

Returns:
dict or CreateFullScanResponse: API response containing scan results
Expand All @@ -763,7 +764,7 @@ def post(self, files: list, params: FullScanParams, use_types: bool = False, use

# Use lazy loading if requested
if use_lazy_loading:
prepared_files = Utils.load_files_for_sending_lazy(files, workspace, max_open_files, base_path)
prepared_files = Utils.load_files_for_sending_lazy(files, workspace, max_open_files, base_path, base_paths)
else:
prepared_files = files

Expand Down
41 changes: 33 additions & 8 deletions socketdev/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def validate_integration_type(integration_type: str) -> IntegrationType:
return integration_type # type: ignore

@staticmethod
def load_files_for_sending_lazy(files: List[str], workspace: str = None, max_open_files: int = 100, base_path: str = None) -> List[Tuple[str, Tuple[str, LazyFileLoader]]]:
def load_files_for_sending_lazy(files: List[str], workspace: str = None, max_open_files: int = 100, base_path: str = None, base_paths: List[str] = None) -> List[Tuple[str, Tuple[str, LazyFileLoader]]]:
"""
Prepares files for sending to the Socket API using lazy loading.

Expand All @@ -247,6 +247,7 @@ def load_files_for_sending_lazy(files: List[str], workspace: str = None, max_ope
workspace: Base directory path to make paths relative to
max_open_files: Maximum number of files to keep open simultaneously (default: 100)
base_path: Optional base path to strip from key names for cleaner file organization
base_paths: Optional list of base paths to strip from key names (takes precedence over base_path)

Returns:
List of tuples formatted for requests multipart upload:
Expand All @@ -260,6 +261,8 @@ def load_files_for_sending_lazy(files: List[str], workspace: str = None, max_ope
workspace = workspace.replace("\\", "/")
if base_path and "\\" in base_path:
base_path = base_path.replace("\\", "/")
if base_paths:
base_paths = [bp.replace("\\", "/") if "\\" in bp else bp for bp in base_paths]

for file_path in files:
# Normalize file path
Expand All @@ -270,29 +273,51 @@ def load_files_for_sending_lazy(files: List[str], workspace: str = None, max_ope

# Calculate the key name for the form data
key = file_path
path_stripped = False

# If base_path is provided, strip it from the file path to create the key
if base_path:
# If base_paths is provided, try to strip one of the paths from the file path
if base_paths:
for bp in base_paths:
# Normalize base_path to ensure consistent handling of trailing slashes
normalized_base_path = bp.rstrip("/") + "/" if not bp.endswith("/") else bp
if key.startswith(normalized_base_path):
key = key[len(normalized_base_path):]
path_stripped = True
break
elif key.startswith(bp.rstrip("/")):
# Handle case where base_path matches exactly without trailing slash
stripped_base = bp.rstrip("/")
if key.startswith(stripped_base + "/") or key == stripped_base:
key = key[len(stripped_base):]
key = key.lstrip("/")
path_stripped = True
break

# If base_path is provided and no base_paths matched, use single base_path
elif base_path:
# Normalize base_path to ensure consistent handling of trailing slashes
normalized_base_path = base_path.rstrip("/") + "/" if not base_path.endswith("/") else base_path
if key.startswith(normalized_base_path):
key = key[len(normalized_base_path):]
path_stripped = True
elif key.startswith(base_path.rstrip("/")):
# Handle case where base_path matches exactly without trailing slash
stripped_base = base_path.rstrip("/")
if key.startswith(stripped_base + "/") or key == stripped_base:
key = key[len(stripped_base):]
key = key.lstrip("/")
path_stripped = True

# If workspace is provided and base_path wasn't used, fall back to workspace logic
elif workspace and file_path.startswith(workspace):
# If workspace is provided and no base paths matched, fall back to workspace logic
if not path_stripped and workspace and file_path.startswith(workspace):
key = file_path[len(workspace):]
key = key.lstrip("/")
key = key.lstrip("./")
path_stripped = True

# If neither base_path nor workspace matched, clean up the key
if key == file_path:
# No base_path or workspace stripping occurred, clean up leading parts
# If no path stripping occurred, clean up the key
if not path_stripped:
# No base_path, base_paths, or workspace stripping occurred, clean up leading parts
key = key.lstrip("/")
key = key.lstrip("./")

Expand Down
2 changes: 1 addition & 1 deletion socketdev/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "3.0.5"
__version__ = "3.0.6"
2 changes: 1 addition & 1 deletion tests/integration/test_comprehensive_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def test_fullscans_basic_workflow(self):
self.created_scan_ids.append(scan_id)

# Get the scan
scan_info = self.sdk.fullscans.get(self.org_slug, scan_id)
scan_info = self.sdk.fullscans.get(self.org_slug, {"id": scan_id})
self.assertIsInstance(scan_info, dict)

# List scans
Expand Down
10 changes: 5 additions & 5 deletions tests/unit/test_working_endpoints_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,15 +346,15 @@ def test_fullscans_get_corrected_unit(self):
"""Test fullscans get - CORRECTED PARAMETER HANDLING."""
expected_data = {"id": "scan-123", "status": "completed"}
self._mock_response(expected_data)
# The actual API uses query parameters, not path parameters

# When getting a specific scan by ID, it uses path parameters
result = self.sdk.fullscans.get("test-org", {"id": "scan-123"})

self.assertEqual(result, expected_data)
call_args = self.mock_requests.request.call_args
self.assertEqual(call_args[0][0], "GET")
# Path includes query params, not path segments
self.assertIn("/orgs/test-org/full-scans?id=scan-123", call_args[0][1])
# Single ID param creates path segment, not query params
self.assertIn("/orgs/test-org/full-scans/scan-123", call_args[0][1])

def test_diffscans_create_from_repo_corrected_unit(self):
"""Test diffscans creation from repo - CORRECTED PATH."""
Expand Down
Loading