From c2a24db8d9ba8b4a46a2d81e85e282651a7f611e Mon Sep 17 00:00:00 2001 From: Douglas Coburn Date: Fri, 14 Nov 2025 16:55:42 -0800 Subject: [PATCH 1/5] feat: enhance reachability analysis with additional CLI parameters and SSL support - Add --reach-concurrency flag to control parallel analysis execution (must be >= 1) - Add --reach-additional-params flag to pass custom parameters to coana CLI - Fix --allow-unverified flag to properly disable SSL verification for reachability analysis - Update socketdev dependency to version 3.0.19 for latest features - Reorganize argument parser groups for better help organization - Add proper SSL warning suppression when using --allow-unverified - Improve reachability analyzer to support NODE_TLS_REJECT_UNAUTHORIZED environment variable - Bump version to 2.2.28 --- pyproject.toml | 4 +-- socketsecurity/__init__.py | 2 +- socketsecurity/config.py | 39 +++++++++++++++++------ socketsecurity/core/tools/reachability.py | 20 +++++++++++- socketsecurity/socketcli.py | 14 ++++++-- 5 files changed, 64 insertions(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ec50d9a..bf6b496 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "socketsecurity" -version = "2.2.27" +version = "2.2.28" requires-python = ">= 3.10" license = {"file" = "LICENSE"} dependencies = [ @@ -16,7 +16,7 @@ dependencies = [ 'GitPython', 'packaging', 'python-dotenv', - 'socketdev>=3.0.17,<4.0.0', + 'socketdev>=3.0.19,<4.0.0', "bs4>=0.0.2", ] readme = "README.md" diff --git a/socketsecurity/__init__.py b/socketsecurity/__init__.py index e29a9cc..d7e0dca 100644 --- a/socketsecurity/__init__.py +++ b/socketsecurity/__init__.py @@ -1,3 +1,3 @@ __author__ = 'socket.dev' -__version__ = '2.2.27' +__version__ = '2.2.28' USER_AGENT = f'SocketPythonCLI/{__version__}' diff --git a/socketsecurity/config.py b/socketsecurity/config.py index 0fc5351..a77130a 100644 --- a/socketsecurity/config.py +++ b/socketsecurity/config.py @@ -73,6 +73,8 @@ class CliConfig: reach_skip_cache: bool = False reach_min_severity: Optional[str] = None reach_output_file: Optional[str] = None + reach_concurrency: Optional[int] = None + reach_additional_params: Optional[List[str]] = None only_facts_file: bool = False @classmethod @@ -132,6 +134,8 @@ def from_args(cls, args_list: Optional[List[str]] = None) -> 'CliConfig': 'reach_skip_cache': args.reach_skip_cache, 'reach_min_severity': args.reach_min_severity, 'reach_output_file': args.reach_output_file, + 'reach_concurrency': args.reach_concurrency, + 'reach_additional_params': args.reach_additional_params, 'only_facts_file': args.only_facts_file, 'version': __version__ } @@ -169,6 +173,11 @@ def from_args(cls, args_list: Optional[List[str]] = None) -> 'CliConfig': logging.error("--only-facts-file requires --reach to be specified") exit(1) + # Validate reach_concurrency is >= 1 if provided + if args.reach_concurrency is not None and args.reach_concurrency < 1: + logging.error("--reach-concurrency must be >= 1") + exit(1) + return cls(**config_args) def to_dict(self) -> dict: @@ -429,20 +438,13 @@ def create_argument_parser() -> argparse.ArgumentParser: help="Exclude license details from the diff report (boosts performance for large repos)" ) - # Security Configuration - security_group = parser.add_argument_group('Security Configuration') - security_group.add_argument( - "--allow-unverified", - action="store_true", - help="Allow unverified packages" - ) - security_group.add_argument( + output_group.add_argument( "--disable-security-issue", dest="disable_security_issue", action="store_true", help="Disable security issue checks" ) - security_group.add_argument( + output_group.add_argument( "--disable_security_issue", dest="disable_security_issue", action="store_true", @@ -494,6 +496,11 @@ def create_argument_parser() -> argparse.ArgumentParser: help="Timeout in seconds for API requests", required=False ) + advanced_group.add_argument( + "--allow-unverified", + action="store_true", + help="Disable SSL certificate verification for API requests" + ) config_group.add_argument( "--include-module-folders", dest="include_module_folders", @@ -567,6 +574,20 @@ def create_argument_parser() -> argparse.ArgumentParser: default=".socket.facts.json", help="Output file path for reachability analysis results (default: .socket.facts.json)" ) + reachability_group.add_argument( + "--reach-concurrency", + dest="reach_concurrency", + type=int, + metavar="", + help="Concurrency level for reachability analysis (must be >= 1)" + ) + reachability_group.add_argument( + "--reach-additional-params", + dest="reach_additional_params", + nargs='+', + metavar="", + help="Additional parameters to pass to the coana CLI (e.g., --reach-additional-params --other-param value --another-param value2)" + ) reachability_group.add_argument( "--only-facts-file", dest="only_facts_file", diff --git a/socketsecurity/core/tools/reachability.py b/socketsecurity/core/tools/reachability.py index 7d3048e..2e0fc16 100644 --- a/socketsecurity/core/tools/reachability.py +++ b/socketsecurity/core/tools/reachability.py @@ -20,7 +20,7 @@ def _ensure_coana_cli_installed(self, version: Optional[str] = None) -> str: Check if @coana-tech/cli is installed, and install/update it if needed. Args: - version: Specific version to install (e.g., '1.2.3'). If None, updates to latest. + version: Specific version to install (e.g., '1.2.3'). If None, always updates to latest. Returns: str: The package specifier to use with npx @@ -48,6 +48,7 @@ def _ensure_coana_cli_installed(self, version: Optional[str] = None) -> str: log.debug(f"Could not check for existing @coana-tech/cli installation: {e}") # Install or update the package + # When no version is specified, always try to update to latest if version: log.info(f"Installing reachability analysis plugin (@coana-tech/cli@{version})...") else: @@ -95,6 +96,9 @@ def run_reachability_analysis( repo_name: Optional[str] = None, branch_name: Optional[str] = None, version: Optional[str] = None, + concurrency: Optional[int] = None, + additional_params: Optional[List[str]] = None, + allow_unverified: bool = False, ) -> Dict[str, Any]: """ Run reachability analysis. @@ -114,6 +118,9 @@ def run_reachability_analysis( repo_name: Repository name branch_name: Branch name version: Specific version of @coana-tech/cli to use + concurrency: Concurrency level for analysis (must be >= 1) + additional_params: Additional parameters to pass to coana CLI + allow_unverified: Disable SSL certificate verification (sets NODE_TLS_REJECT_UNAUTHORIZED=0) Returns: Dict containing scan_id and report_path @@ -158,6 +165,13 @@ def run_reachability_analysis( if skip_cache: cmd.append("--skip-cache-usage") + if concurrency: + cmd.extend(["--concurrency", str(concurrency)]) + + # Add any additional parameters provided by the user + if additional_params: + cmd.extend(additional_params) + # Set up environment variables env = os.environ.copy() @@ -172,6 +186,10 @@ def run_reachability_analysis( if branch_name: env["SOCKET_BRANCH_NAME"] = branch_name + # Set NODE_TLS_REJECT_UNAUTHORIZED=0 if allow_unverified is True + if allow_unverified: + env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0" + # Execute CLI log.info("Running reachability analysis...") log.debug(f"Reachability command: {' '.join(cmd)}") diff --git a/socketsecurity/socketcli.py b/socketsecurity/socketcli.py index 65eca5b..0503e1c 100644 --- a/socketsecurity/socketcli.py +++ b/socketsecurity/socketcli.py @@ -2,6 +2,7 @@ import sys import traceback import shutil +import warnings from dotenv import load_dotenv from git import InvalidGitRepositoryError, NoSuchPathError @@ -55,7 +56,13 @@ def main_code(): "2. Environment variable: SOCKET_SECURITY_API_KEY") sys.exit(3) - sdk = socketdev(token=config.api_token) + sdk = socketdev(token=config.api_token, allow_unverified=config.allow_unverified) + + # Suppress urllib3 InsecureRequestWarning when using --allow-unverified + if config.allow_unverified: + import urllib3 + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + output_handler = OutputHandler(config, sdk) log.debug("sdk loaded") @@ -277,7 +284,10 @@ def main_code(): disable_analytics=config.reach_disable_analytics or False, repo_name=config.repo, branch_name=config.branch, - version=config.reach_version + version=config.reach_version, + concurrency=config.reach_concurrency, + additional_params=config.reach_additional_params, + allow_unverified=config.allow_unverified ) log.info(f"Reachability analysis completed successfully") From cdb3085c90322e23bdb51530ca72df5157ac5a77 Mon Sep 17 00:00:00 2001 From: Douglas Coburn Date: Fri, 14 Nov 2025 17:02:41 -0800 Subject: [PATCH 2/5] Adding additional ecosystem binaries to the container to support reachability --- Dockerfile | 14 ++++++++++++-- pyproject.toml | 2 +- socketsecurity/__init__.py | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 65ffbe8..94df0b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,8 +7,18 @@ ARG PIP_EXTRA_INDEX_URL=https://pypi.org/simple ARG USE_LOCAL_INSTALL=false RUN apk update \ - && apk add --no-cache git nodejs npm yarn curl \ - && npm install @coana-tech/cli -g + && apk add --no-cache git nodejs npm yarn curl wget \ + go ruby ruby-dev build-base \ + openjdk17-jdk \ + dotnet8-sdk \ + && npm install @coana-tech/cli -g \ + && gem install bundler \ + && curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ + && . ~/.cargo/env \ + && rustup component add rustfmt clippy + +# Add Rust to PATH +ENV PATH="/root/.cargo/bin:${PATH}" # Install uv COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv diff --git a/pyproject.toml b/pyproject.toml index bf6b496..e3b91fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "socketsecurity" -version = "2.2.28" +version = "2.2.29" requires-python = ">= 3.10" license = {"file" = "LICENSE"} dependencies = [ diff --git a/socketsecurity/__init__.py b/socketsecurity/__init__.py index d7e0dca..5a5d045 100644 --- a/socketsecurity/__init__.py +++ b/socketsecurity/__init__.py @@ -1,3 +1,3 @@ __author__ = 'socket.dev' -__version__ = '2.2.28' +__version__ = '2.2.29' USER_AGENT = f'SocketPythonCLI/{__version__}' From 47ca4e0d8414e3bb884f8853df83ae2683aec394 Mon Sep 17 00:00:00 2001 From: Douglas Coburn Date: Fri, 14 Nov 2025 17:07:23 -0800 Subject: [PATCH 3/5] Added new params to readme --- README.md | 2 ++ pyproject.toml | 2 +- socketsecurity/__init__.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 29444ab..1bab6d4 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,8 @@ If you don't want to provide the Socket API Token every time then you can use th | --reach-version | False | latest | Version of @coana-tech/cli to use for analysis | | --reach-analysis-timeout | False | 1200 | Timeout in seconds for the reachability analysis (default: 1200 seconds / 20 minutes) | | --reach-analysis-memory-limit | False | 4096 | Memory limit in MB for the reachability analysis (default: 4096 MB / 4 GB) | +| --reach-concurrency | False | | Control parallel analysis execution (must be >= 1) | +| --reach-additional-params | False | | Pass custom parameters to the coana CLI tool | | --reach-ecosystems | False | | Comma-separated list of ecosystems to analyze (e.g., "npm,pypi"). If not specified, all supported ecosystems are analyzed | | --reach-exclude-paths | False | | Comma-separated list of file paths or patterns to exclude from reachability analysis | | --reach-min-severity | False | | Minimum severity level for reporting reachability results (low, medium, high, critical) | diff --git a/pyproject.toml b/pyproject.toml index e3b91fd..2826608 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "socketsecurity" -version = "2.2.29" +version = "2.2.30" requires-python = ">= 3.10" license = {"file" = "LICENSE"} dependencies = [ diff --git a/socketsecurity/__init__.py b/socketsecurity/__init__.py index 5a5d045..68595cb 100644 --- a/socketsecurity/__init__.py +++ b/socketsecurity/__init__.py @@ -1,3 +1,3 @@ __author__ = 'socket.dev' -__version__ = '2.2.29' +__version__ = '2.2.30' USER_AGENT = f'SocketPythonCLI/{__version__}' From 03322e36b56a509890ece15df6a3c51d6337c54e Mon Sep 17 00:00:00 2001 From: Douglas Coburn Date: Fri, 14 Nov 2025 17:22:43 -0800 Subject: [PATCH 4/5] Add support for choosing version of Go, Java, and Dotnet --- Dockerfile | 79 +++++++++++--- pyproject.toml | 2 +- scripts/build_container_flexible.sh | 161 ++++++++++++++++++++++++++++ socketsecurity/__init__.py | 2 +- 4 files changed, 228 insertions(+), 16 deletions(-) create mode 100644 scripts/build_container_flexible.sh diff --git a/Dockerfile b/Dockerfile index 94df0b8..dcfc313 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,72 @@ FROM python:3-alpine LABEL org.opencontainers.image.authors="socket.dev" + +# Language version arguments with defaults +ARG GO_VERSION=system +ARG JAVA_VERSION=17 +ARG DOTNET_VERSION=8 + +# CLI and SDK arguments ARG CLI_VERSION ARG SDK_VERSION ARG PIP_INDEX_URL=https://pypi.org/simple ARG PIP_EXTRA_INDEX_URL=https://pypi.org/simple ARG USE_LOCAL_INSTALL=false -RUN apk update \ - && apk add --no-cache git nodejs npm yarn curl wget \ - go ruby ruby-dev build-base \ - openjdk17-jdk \ - dotnet8-sdk \ - && npm install @coana-tech/cli -g \ - && gem install bundler \ - && curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ - && . ~/.cargo/env \ - && rustup component add rustfmt clippy +# Install base packages first +RUN apk update && apk add --no-cache \ + git nodejs npm yarn curl wget \ + ruby ruby-dev build-base -# Add Rust to PATH -ENV PATH="/root/.cargo/bin:${PATH}" +# Install Go with version control +RUN if [ "$GO_VERSION" = "system" ]; then \ + apk add --no-cache go; \ + else \ + cd /tmp && \ + ARCH=$(uname -m) && \ + case $ARCH in \ + x86_64) GOARCH=amd64 ;; \ + aarch64) GOARCH=arm64 ;; \ + *) echo "Unsupported architecture: $ARCH" && exit 1 ;; \ + esac && \ + wget https://golang.org/dl/go${GO_VERSION}.linux-${GOARCH}.tar.gz && \ + tar -C /usr/local -xzf go${GO_VERSION}.linux-${GOARCH}.tar.gz && \ + rm go${GO_VERSION}.linux-${GOARCH}.tar.gz; \ + fi + +# Install Java with version control +RUN if [ "$JAVA_VERSION" = "8" ]; then \ + apk add --no-cache openjdk8-jdk; \ + elif [ "$JAVA_VERSION" = "11" ]; then \ + apk add --no-cache openjdk11-jdk; \ + elif [ "$JAVA_VERSION" = "17" ]; then \ + apk add --no-cache openjdk17-jdk; \ + elif [ "$JAVA_VERSION" = "21" ]; then \ + apk add --no-cache openjdk21-jdk; \ + else \ + echo "Unsupported Java version: $JAVA_VERSION. Supported: 8, 11, 17, 21" && exit 1; \ + fi + +# Install .NET with version control +RUN if [ "$DOTNET_VERSION" = "6" ]; then \ + apk add --no-cache dotnet6-sdk; \ + elif [ "$DOTNET_VERSION" = "8" ]; then \ + apk add --no-cache dotnet8-sdk; \ + else \ + echo "Unsupported .NET version: $DOTNET_VERSION. Supported: 6, 8" && exit 1; \ + fi + +# Install additional tools +RUN npm install @coana-tech/cli -g && \ + gem install bundler && \ + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \ + . ~/.cargo/env && \ + rustup component add rustfmt clippy + +# Set environment paths +ENV PATH="/usr/local/go/bin:/root/.cargo/bin:${PATH}" +ENV GOROOT="/usr/local/go" +ENV GOPATH="/go" # Install uv COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv @@ -45,7 +93,10 @@ COPY . /app WORKDIR /app RUN if [ "$USE_LOCAL_INSTALL" = "true" ]; then \ pip install --upgrade -e .; \ - pip install --upgrade socketdev; \ + pip install --upgrade socketdev;I wan \ fi -# ENTRYPOINT ["socketcli"] \ No newline at end of file +# Create workspace directory with proper permissions +RUN mkdir -p /go/src && chmod -R 777 /go + +ENTRYPOINT ["socketcli"] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 2826608..e4f92e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "socketsecurity" -version = "2.2.30" +version = "2.2.31" requires-python = ">= 3.10" license = {"file" = "LICENSE"} dependencies = [ diff --git a/scripts/build_container_flexible.sh b/scripts/build_container_flexible.sh new file mode 100644 index 0000000..4ecbc7a --- /dev/null +++ b/scripts/build_container_flexible.sh @@ -0,0 +1,161 @@ +#!/bin/sh +VERSION=$(grep -o "__version__.*" socketsecurity/__init__.py | awk '{print $3}' | tr -d "'") +ENABLE_PYPI_BUILD=$1 +STABLE_VERSION=$2 +GO_VERSION=${GO_VERSION:-"1.21"} +JAVA_VERSION=${JAVA_VERSION:-"17"} +DOTNET_VERSION=${DOTNET_VERSION:-"8"} + +verify_package() { + local version=$1 + local pip_index=$2 + echo "Verifying package availability..." + + for i in $(seq 1 30); do + if pip install --index-url $pip_index socketsecurity==$version; then + echo "Package $version is now available and installable" + pip uninstall -y socketsecurity + return 0 + fi + echo "Attempt $i: Package not yet installable, waiting 20s... ($i/30)" + sleep 20 + done + + echo "Package verification failed after 30 attempts" + return 1 +} + +# Function to build Docker image with language versions +build_docker_image() { + local cli_version=$1 + local tag=$2 + local pip_index=${3:-"https://pypi.org/simple"} + local pip_extra_index=${4:-"https://pypi.org/simple"} + local use_local=${5:-"false"} + local dockerfile=${6:-"Dockerfile"} + + echo "Building with Go $GO_VERSION, Java $JAVA_VERSION, .NET $DOTNET_VERSION" + + local build_args="--build-arg CLI_VERSION=$cli_version" + build_args="$build_args --build-arg GO_VERSION=$GO_VERSION" + build_args="$build_args --build-arg JAVA_VERSION=$JAVA_VERSION" + build_args="$build_args --build-arg DOTNET_VERSION=$DOTNET_VERSION" + build_args="$build_args --build-arg PIP_INDEX_URL=$pip_index" + build_args="$build_args --build-arg PIP_EXTRA_INDEX_URL=$pip_extra_index" + build_args="$build_args --build-arg USE_LOCAL_INSTALL=$use_local" + + docker build --no-cache $build_args --platform linux/amd64,linux/arm64 -t $tag -f $dockerfile . +} + +echo "Socket CLI version: $VERSION" +echo "Language versions: Go $GO_VERSION, Java $JAVA_VERSION, .NET $DOTNET_VERSION" + +if [ -z $ENABLE_PYPI_BUILD ] || [ -z $STABLE_VERSION ]; then + echo "$0 pypi-build=