diff --git a/README.md b/README.md index 07c8797..2029a45 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,45 @@ # Socket Security CLI -The Socket Security CLI was created to enable integrations with other tools like GitHub Actions, Gitlab, BitBucket, local use cases and more. The tool will get the head scan for the provided repo from Socket, create a new one, and then report any new alerts detected. If there are new alerts against the Socket security policy it'll exit with a non-Zero exit code. +The Socket Security CLI was created to enable integrations with other tools like GitHub Actions, GitLab, BitBucket, local use cases and more. The tool will get the head scan for the provided repo from Socket, create a new one, and then report any new alerts detected. If there are new alerts against the Socket security policy it'll exit with a non-Zero exit code. + +## Quick Start + +The CLI now features automatic detection of git repository information, making it much simpler to use in CI/CD environments. Most parameters are now optional and will be detected automatically from your git repository. + +### Minimal Usage Examples + +**GitHub Actions:** +```bash +socketcli --target-path $GITHUB_WORKSPACE --scm github --pr-number $PR_NUMBER +``` + +**GitLab CI:** +```bash +socketcli --target-path $CI_PROJECT_DIR --scm gitlab --pr-number ${CI_MERGE_REQUEST_IID:-0} +``` + +**Local Development:** +```bash +socketcli --target-path ./my-project +``` + +The CLI will automatically detect: +- Repository name from git remote +- Branch name from git +- Commit SHA and message from git +- Committer information from git +- Default branch status from git and CI environment +- Changed files from git commit history + +## CI/CD Workflow Examples + +Pre-configured workflow examples are available in the [`workflows/`](workflows/) directory: + +- **[GitHub Actions](workflows/github-actions.yml)** - Complete workflow with concurrency control and automatic PR detection +- **[GitLab CI](workflows/gitlab-ci.yml)** - Pipeline configuration with caching and environment variable handling +- **[Bitbucket Pipelines](workflows/bitbucket-pipelines.yml)** - Basic pipeline setup with optional path filtering + +These examples are production-ready and include best practices for each platform. ## Usage @@ -25,36 +64,36 @@ If you don't want to provide the Socket API Token every time then you can use th #### Repository | Parameter | Required | Default | Description | |:-----------------|:---------|:--------|:------------------------------------------------------------------------| -| --repo | False | | Repository name in owner/repo format | +| --repo | False | *auto* | Repository name in owner/repo format (auto-detected from git remote) | | --integration | False | api | Integration type (api, github, gitlab) | | --owner | False | | Name of the integration owner, defaults to the socket organization slug | -| --branch | False | "" | Branch name | -| --committers | False | | Committer(s) to filter by | +| --branch | False | *auto* | Branch name (auto-detected from git) | +| --committers | False | *auto* | Committer(s) to filter by (auto-detected from git commit) | | --repo-is-public | False | False | If set, flags a new repository creation as public. Defaults to false. | #### Pull Request and Commit -| Parameter | Required | Default | Description | -|:-----------------|:---------|:--------|:--------------------| -| --pr-number | False | "0" | Pull request number | -| --commit-message | False | | Commit message | -| --commit-sha | False | "" | Commit SHA | +| Parameter | Required | Default | Description | +|:-----------------|:---------|:--------|:-----------------------------------------------| +| --pr-number | False | "0" | Pull request number | +| --commit-message | False | *auto* | Commit message (auto-detected from git) | +| --commit-sha | False | *auto* | Commit SHA (auto-detected from git) | #### Path and File | Parameter | Required | Default | Description | |:----------------------------|:---------|:----------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | --target-path | False | ./ | Target path for analysis | | --sbom-file | False | | SBOM file path | -| --files | False | [] | Files to analyze (JSON array string) | +| --files | False | *auto* | Files to analyze (JSON array string). Auto-detected from git commit changes when not specified | | --excluded-ecosystems | False | [] | List of ecosystems to exclude from analysis (JSON array string). You can get supported files from the [Supported Files API](https://docs.socket.dev/reference/getsupportedfiles) | | --license-file-name | False | `license_output.json` | Name of the file to save the license details to if enabled | | --save-submitted-files-list | False | | Save list of submitted file names to JSON file for debugging purposes | | --save-manifest-tar | False | | Save all manifest files to a compressed tar.gz archive with original directory structure | #### Branch and Scan Configuration -| Parameter | Required | Default | Description | -|:-----------------|:---------|:--------|:------------------------------------------------------------| -| --default-branch | False | False | Make this branch the default branch | -| --pending-head | False | False | If true, the new scan will be set as the branch's head scan | +| Parameter | Required | Default | Description | +|:-----------------|:---------|:--------|:------------------------------------------------------------------------------------------------------| +| --default-branch | False | *auto* | Make this branch the default branch (auto-detected from git and CI environment when not specified) | +| --pending-head | False | *auto* | If true, the new scan will be set as the branch's head scan (automatically synced with default-branch) | #### Output Configuration | Parameter | Required | Default | Description | @@ -114,26 +153,66 @@ Example `SOCKET_SLACK_CONFIG_JSON` value {"url": "https://REPLACE_ME_WEBHOOK"} ```` +## Automatic Git Detection + +The CLI now automatically detects repository information from your git environment, significantly simplifying usage in CI/CD pipelines: + +### Auto-Detected Information + +- **Repository name**: Extracted from git remote origin URL +- **Branch name**: Current git branch or CI environment variables +- **Commit SHA**: Latest commit hash or CI-provided commit SHA +- **Commit message**: Latest commit message +- **Committer information**: Git commit author details +- **Default branch status**: Determined from git repository and CI environment +- **Changed files**: Files modified in the current commit (for differential scanning) + +### Default Branch Detection + +The CLI uses intelligent default branch detection with the following priority: + +1. **Explicit `--default-branch` flag**: Takes highest priority when specified +2. **CI environment detection**: Uses CI platform variables (GitHub Actions, GitLab CI) +3. **Git repository analysis**: Compares current branch with repository's default branch +4. **Fallback**: Defaults to `false` if none of the above methods succeed + +Both `--default-branch` and `--pending-head` parameters are automatically synchronized to ensure consistent behavior. + +### Scan Behavior + +The CLI determines scanning behavior intelligently: + +- **Manifest files changed**: Performs differential scan with PR/MR comments when supported +- **No manifest files changed**: Creates full repository scan report without waiting for diff results +- **Force API mode**: When no supported manifest files are detected, automatically enables non-blocking mode + ## File Selection Behavior The CLI determines which files to scan based on the following logic: -1. **Git Commit Files**: By default, the CLI checks files changed in the current git commit first. If any of these files match supported manifest patterns (like package.json, requirements.txt, etc.), a scan is triggered. +1. **Git Commit Files (Default)**: The CLI automatically checks files changed in the current git commit. If any of these files match supported manifest patterns (like package.json, requirements.txt, etc.), a scan is triggered. + +2. **`--files` Parameter Override**: When specified, this parameter takes precedence over git commit detection. It accepts a JSON array of file paths to check for manifest files. + +3. **`--ignore-commit-files` Flag**: When set, git commit files are ignored completely, and the CLI will scan all manifest files in the target directory regardless of what changed. -2. **`--files` Parameter**: If no git commit exists, or no manifest files are found in the commit changes, the CLI checks files specified via the `--files` parameter. This parameter accepts a JSON array of file paths. +4. **Automatic Fallback**: If no manifest files are found in git commit changes and no `--files` are specified, the CLI automatically switches to "API mode" and performs a full repository scan. -3. **`--ignore-commit-files`**: When this flag is set, git commit files are ignored completely, and only files specified in `--files` are considered. This also forces a scan regardless of whether manifest files are present. +> **Important**: The CLI doesn't scan only the specified files - it uses them to determine whether a scan should be performed and what type of scan to run. When triggered, it searches the entire `--target-path` for all supported manifest files. -4. **No Manifest Files**: If no manifest files are found in either git commit changes or `--files` (and `--ignore-commit-files` is not set), the scan is skipped. +### Scanning Modes -> **Note**: The CLI does not scan only the specified files - it uses them to determine whether a scan should be performed. When a scan is triggered, it searches the entire `--target-path` for all supported manifest files. +- **Differential Mode**: When manifest files are detected in changes, performs a diff scan with PR/MR comment integration +- **API Mode**: When no manifest files are in changes, creates a full scan report without PR comments but still scans the entire repository +- **Force Mode**: With `--ignore-commit-files`, always performs a full scan regardless of changes ### Examples -- **Commit with manifest file**: If your commit includes changes to `package.json`, a scan will be triggered automatically. -- **Commit without manifest files**: If your commit only changes non-manifest files (like `.github/workflows/socket.yaml`), no scan will be performed unless you use `--files` or `--ignore-commit-files`. -- **Using `--files`**: If you specify `--files '["package.json"]'`, the CLI will check if this file exists and is a manifest file before triggering a scan. -- **Using `--ignore-commit-files`**: This forces a scan of all manifest files in the target path, regardless of what's in your commit. +- **Commit with manifest file**: If your commit includes changes to `package.json`, a differential scan will be triggered automatically with PR comment integration. +- **Commit without manifest files**: If your commit only changes non-manifest files (like `.github/workflows/socket.yaml`), the CLI automatically switches to API mode and performs a full repository scan. +- **Using `--files`**: If you specify `--files '["package.json"]'`, the CLI will check if this file exists and is a manifest file before determining scan type. +- **Using `--ignore-commit-files`**: This forces a full scan of all manifest files in the target path, regardless of what's in your commit. +- **Auto-detection**: Most CI/CD scenarios now work with just `socketcli --target-path /path/to/repo --scm github --pr-number $PR_NUM` ## Debugging and Troubleshooting diff --git a/pyproject.toml b/pyproject.toml index 0339b2f..1406aad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "socketsecurity" -version = "2.1.23" +version = "2.1.24" requires-python = ">= 3.10" license = {"file" = "LICENSE"} dependencies = [ diff --git a/socketsecurity/__init__.py b/socketsecurity/__init__.py index b2c1d88..b396467 100644 --- a/socketsecurity/__init__.py +++ b/socketsecurity/__init__.py @@ -1,2 +1,2 @@ __author__ = 'socket.dev' -__version__ = '2.1.23' +__version__ = '2.1.24' diff --git a/socketsecurity/core/__init__.py b/socketsecurity/core/__init__.py index 7688299..ec85d4e 100644 --- a/socketsecurity/core/__init__.py +++ b/socketsecurity/core/__init__.py @@ -487,7 +487,7 @@ def create_full_scan_with_report_url( no_change: bool = False, save_files_list_path: str = None, save_manifest_tar_path: str = None - ) -> dict: + ) -> Diff: """Create a new full scan and return with html_report_url. Args: @@ -501,12 +501,13 @@ def create_full_scan_with_report_url( Dict with full scan data including html_report_url """ log.debug(f"starting create_full_scan_with_report_url with no_change: {no_change}") + diff = Diff( + id="NO_SCAN_RAN", + report_url="", + diff_url="" + ) if no_change: - return { - "id": "NO_SCAN_RAN", - "html_report_url": "", - "unmatchedFiles": [] - } + return diff # Find manifest files files = self.find_files(path) @@ -521,11 +522,7 @@ def create_full_scan_with_report_url( files_for_sending = self.load_files_for_sending(files, path) if not files: - return { - "id": "NO_SCAN_RAN", - "html_report_url": "", - "unmatchedFiles": [] - } + return diff try: # Create new scan @@ -539,25 +536,13 @@ def create_full_scan_with_report_url( # Construct report URL base_socket = "https://socket.dev/dashboard/org" - report_url = f"{base_socket}/{self.config.org_slug}/sbom/{new_full_scan.id}" - if not params.include_license_details: - report_url += "?include_license_details=false" + diff.report_url = f"{base_socket}/{self.config.org_slug}/sbom/{new_full_scan.id}" + diff.diff_url = diff.report_url + diff.id = new_full_scan.id + diff.packages = {} # Return result in the format expected by the user - return { - "id": new_full_scan.id, - "created_at": new_full_scan.created_at, - "updated_at": new_full_scan.updated_at, - "organization_id": new_full_scan.organization_id, - "repository_id": new_full_scan.repository_id, - "branch": new_full_scan.branch, - "commit_message": new_full_scan.commit_message, - "commit_hash": new_full_scan.commit_hash, - "pull_request": new_full_scan.pull_request, - "committers": new_full_scan.committers, - "html_report_url": report_url, - "unmatchedFiles": getattr(new_full_scan, 'unmatchedFiles', []) - } + return diff def check_full_scans_status(self, head_full_scan_id: str, new_full_scan_id: str) -> bool: is_ready = False diff --git a/socketsecurity/core/git_interface.py b/socketsecurity/core/git_interface.py index 5cf5296..85fa029 100644 --- a/socketsecurity/core/git_interface.py +++ b/socketsecurity/core/git_interface.py @@ -16,16 +16,25 @@ def __init__(self, path: str): assert self.repo self.head = self.repo.head - # Use GITHUB_SHA if available, otherwise fall back to head commit + # Use GITHUB_SHA if available, otherwise fall back to current HEAD commit github_sha = os.getenv('GITHUB_SHA') if github_sha: try: self.commit = self.repo.commit(github_sha) + log.debug(f"Using commit from GITHUB_SHA: {github_sha}") except Exception as error: log.debug(f"Failed to get commit from GITHUB_SHA: {error}") - self.commit = self.head.commit + # Use the actual current HEAD commit, not the head reference's commit + self.commit = self.repo.commit('HEAD') + log.debug(f"Using current HEAD commit: {self.commit.hexsha}") else: - self.commit = self.head.commit + # Use the actual current HEAD commit, not the head reference's commit + self.commit = self.repo.commit('HEAD') + log.debug(f"Using current HEAD commit: {self.commit.hexsha}") + + log.debug(f"Final commit being used: {self.commit.hexsha}") + log.debug(f"Commit author: {self.commit.author.name} <{self.commit.author.email}>") + log.debug(f"Commit committer: {self.commit.committer.name} <{self.commit.committer.email}>") self.repo_name = self.repo.remotes.origin.url.split('.git')[0].split('/')[-1] try: @@ -44,8 +53,161 @@ def __init__(self, path: str): if item != "": full_path = f"{self.path}/{item}" self.changed_files.append(full_path) + + # Determine if this commit is on the default branch + # This considers both GitHub Actions detached HEAD and regular branch situations + self.is_default_branch = self._is_commit_and_branch_default() + + def _is_commit_and_branch_default(self) -> bool: + """ + Check if both the commit is on the default branch AND we're processing the default branch. + This handles GitHub Actions detached HEAD state properly. + + Returns: + True if commit is on default branch and we're processing the default branch + """ + try: + # First check if the commit is reachable from the default branch + if not self.is_commit_on_default_branch(): + log.debug("Commit is not on default branch") + return False + + # Check if we're processing the default branch + github_ref = os.getenv('GITHUB_REF') # e.g., 'refs/heads/main' or 'refs/pull/123/merge' + + if github_ref: + log.debug(f"GitHub ref: {github_ref}") + + # Handle pull requests - they're not on the default branch + if github_ref.startswith('refs/pull/'): + log.debug("Processing a pull request, not default branch") + return False + + # Handle regular branch pushes + if github_ref.startswith('refs/heads/'): + branch_from_ref = github_ref.replace('refs/heads/', '') + default_branch_name = self.get_default_branch_name() + is_default = branch_from_ref == default_branch_name + log.debug(f"Branch from GITHUB_REF: {branch_from_ref}, Default: {default_branch_name}, Is default: {is_default}") + return is_default + + # Handle tags or other refs - not default branch + log.debug(f"Non-branch ref: {github_ref}, not default branch") + return False + else: + # Not in GitHub Actions, use local development logic + # For local development, we consider it "default branch" if: + # 1. Currently on the default branch, OR + # 2. The commit is reachable from the default branch (part of default branch history) + + is_on_default = self.is_on_default_branch() + if is_on_default: + log.debug("Currently on default branch locally") + return True + + # Even if on feature branch, if commit is on default branch, consider it default + # This handles cases where feature branch was created from or merged to default + is_commit_default = self.is_commit_on_default_branch() + log.debug(f"Not on default branch locally, but commit is on default branch: {is_commit_default}") + return is_commit_default + + except Exception as error: + log.debug(f"Error determining if commit and branch are default: {error}") + return False @property def commit_str(self) -> str: """Return commit SHA as a string""" return self.commit.hexsha + + def get_default_branch_name(self) -> str: + """ + Get the default branch name from the remote origin. + + Returns: + Default branch name (e.g., 'main', 'master') + """ + try: + # Try to get the default branch from remote HEAD + remote_head = self.repo.remotes.origin.refs.HEAD + # Extract branch name from refs/remotes/origin/HEAD -> refs/remotes/origin/main + default_branch = str(remote_head.reference).split('/')[-1] + log.debug(f"Default branch detected: {default_branch}") + return default_branch + except Exception as error: + log.debug(f"Could not determine default branch from remote: {error}") + # Fallback: check common default branch names + for branch_name in ['main', 'master']: + try: + if f'origin/{branch_name}' in [str(ref) for ref in self.repo.remotes.origin.refs]: + log.debug(f"Using fallback default branch: {branch_name}") + return branch_name + except: + continue + + # Last fallback: assume 'main' + log.debug("Using final fallback default branch: main") + return 'main' + + def is_commit_on_default_branch(self) -> bool: + """ + Check if the current commit is reachable from the default branch. + + Returns: + True if current commit is on the default branch, False otherwise + """ + try: + default_branch = self.get_default_branch_name() + + # Get the default branch's HEAD commit + try: + # Try remote branch first + default_branch_ref = self.repo.remotes.origin.refs[default_branch] + default_branch_commit = default_branch_ref.commit + except: + # Fallback to local branch + try: + default_branch_ref = self.repo.heads[default_branch] + default_branch_commit = default_branch_ref.commit + except: + log.debug(f"Could not find default branch '{default_branch}' locally or remotely") + return False + + # Check if current commit is the same as default branch HEAD + if self.commit.hexsha == default_branch_commit.hexsha: + log.debug("Current commit is the HEAD of the default branch") + return True + + # Check if current commit is an ancestor of the default branch HEAD + # This means the commit is reachable from the default branch + is_ancestor = self.repo.is_ancestor(self.commit, default_branch_commit) + log.debug(f"Current commit is ancestor of default branch: {is_ancestor}") + return is_ancestor + + except Exception as error: + log.debug(f"Error checking if commit is on default branch: {error}") + return False + + def is_on_default_branch(self) -> bool: + """ + Check if we're currently on the default branch (not just if commit is reachable). + + Returns: + True if currently on the default branch, False otherwise + """ + try: + # If we're in detached HEAD state, we're not "on" any branch + if self.repo.head.is_detached: + log.debug("In detached HEAD state, not on any branch") + return False + + current_branch_name = self.repo.active_branch.name + default_branch_name = self.get_default_branch_name() + + is_default = current_branch_name == default_branch_name + log.debug(f"Current branch: {current_branch_name}, Default branch: {default_branch_name}, Is default: {is_default}") + return is_default + + except Exception as error: + log.debug(f"Error checking if on default branch: {error}") + return False diff --git a/socketsecurity/socketcli.py b/socketsecurity/socketcli.py index 97902b7..c4277cd 100644 --- a/socketsecurity/socketcli.py +++ b/socketsecurity/socketcli.py @@ -125,7 +125,7 @@ def main_code(): if not config.branch: config.branch = git_repo.branch if not config.committers: - config.committers = [git_repo.committer] + config.committers = [git_repo.author] if not config.commit_message: config.commit_message = git_repo.commit_message except InvalidGitRepositoryError: @@ -150,7 +150,9 @@ def main_code(): from socketsecurity.core.scm.gitlab import Gitlab, GitlabConfig gitlab_config = GitlabConfig.from_env() scm = Gitlab(client=client, config=gitlab_config) - if scm is not None: + # Don't override config.default_branch if it was explicitly set via --default-branch flag + # Only use SCM detection if --default-branch wasn't provided + if scm is not None and not config.default_branch: config.default_branch = scm.config.is_default_branch # Determine files to check based on the new logic @@ -203,6 +205,21 @@ def main_code(): except (ValueError, TypeError): pr_number = 0 + # Determine if this should be treated as default branch + # Priority order: + # 1. If --default-branch flag is explicitly set to True, use that + # 2. If SCM detected it's the default branch, use that + # 3. If it's a git repo, use git_repo.is_default_branch + # 4. Otherwise, default to False + if config.default_branch: + is_default_branch = True + elif scm is not None and hasattr(scm.config, 'is_default_branch') and scm.config.is_default_branch: + is_default_branch = True + elif is_repo and git_repo.is_default_branch: + is_default_branch = True + else: + is_default_branch = False + params = FullScanParams( org_slug=org_slug, integration_type=integration_type, @@ -213,8 +230,8 @@ def main_code(): commit_hash=config.commit_sha, pull_request=pr_number, committers=config.committers, - make_default_branch=config.default_branch, - set_as_pending_head=True + make_default_branch=is_default_branch, + set_as_pending_head=is_default_branch ) params.include_license_details = not config.exclude_license_details @@ -302,19 +319,29 @@ def main_code(): else: if force_api_mode: log.info("No Manifest files changed, creating Socket Report") + serializable_params = { + key: value if isinstance(value, (int, float, str, list, dict, bool, type(None))) else str(value) + for key, value in params.__dict__.items() + } + log.debug(f"params={serializable_params}") + diff = core.create_full_scan_with_report_url( + config.target_path, + params, + no_change=should_skip_scan, + save_files_list_path=config.save_submitted_files_list, + save_manifest_tar_path=config.save_manifest_tar + ) + log.info(f"Full scan created with ID: {diff.id}") + log.info(f"Full scan report URL: {diff.report_url}") else: log.info("API Mode") - full_scan_result = core.create_full_scan_with_report_url(config.target_path, params, no_change=should_skip_scan, save_files_list_path=config.save_submitted_files_list, save_manifest_tar_path=config.save_manifest_tar) - log.info(f"Full scan created with ID: {full_scan_result['id']}") - log.info(f"Full scan report URL: {full_scan_result['html_report_url']}") - - # Create a minimal diff-like object for compatibility with downstream code - diff = Diff() - diff.id = full_scan_result['id'] - diff.report_url = full_scan_result['html_report_url'] - diff.diff_url = full_scan_result['html_report_url'] - diff.packages = {} # No package data needed for API mode - # No output handling needed for API mode - just creating the scan + diff = core.create_new_diff( + config.target_path, params, + no_change=should_skip_scan, + save_files_list_path=config.save_submitted_files_list, + save_manifest_tar_path=config.save_manifest_tar + ) + output_handler.handle_output(diff) # Handle license generation if not should_skip_scan and diff.id != "NO_DIFF_RAN" and diff.id != "NO_SCAN_RAN" and config.generate_license: diff --git a/workflows/bitbucket-pipelines.yml b/workflows/bitbucket-pipelines.yml new file mode 100644 index 0000000..d9f1260 --- /dev/null +++ b/workflows/bitbucket-pipelines.yml @@ -0,0 +1,85 @@ +# Socket Security Bitbucket Pipelines +# This pipeline runs Socket Security scans on every commit to any branch +# The CLI automatically detects most information from the git repository + +image: python:3.12-slim + +definitions: + steps: + - step: &socket-scan + name: Socket Security Scan + caches: + - pip + script: + - pip install --upgrade pip + - pip install socketsecurity + # Run Socket CLI with minimal required parameters + # The CLI automatically detects: + # - Repository name from git + # - Branch name from git + # - Commit SHA from git + # - Commit message from git + # - Committer information from git + # - Default branch status from git repository + # - Changed files from git commit + - | + socketcli \ + --target-path $BITBUCKET_CLONE_DIR \ + --scm api \ + --pr-number ${BITBUCKET_PR_ID:-0} + # Repository variables needed (set in Bitbucket repo settings) + # SOCKET_SECURITY_API_KEY: Your Socket Security API token + +pipelines: + # Run on all branches + branches: + '**': + - step: *socket-scan + + # Run on pull requests + pull-requests: + '**': + - step: *socket-scan + +# Optional: More efficient version that only runs when manifest files change +# To use this instead, replace the pipelines section above with: +# +# pipelines: +# branches: +# '**': +# - step: +# <<: *socket-scan +# condition: +# changesets: +# includePaths: +# - "package.json" +# - "package-lock.json" +# - "yarn.lock" +# - "pnpm-lock.yaml" +# - "requirements.txt" +# - "Pipfile" +# - "Pipfile.lock" +# - "pyproject.toml" +# - "poetry.lock" +# - "go.mod" +# - "go.sum" +# - "Cargo.toml" +# - "Cargo.lock" +# - "composer.json" +# - "composer.lock" +# - "Gemfile" +# - "Gemfile.lock" +# - "**/*.csproj" +# - "**/*.fsproj" +# - "**/*.vbproj" +# - "packages.config" +# - "paket.dependencies" +# - "project.json" +# +# pull-requests: +# '**': +# - step: *socket-scan + +# Note: Bitbucket Pipelines doesn't have built-in SCM integration like +# GitHub Actions or GitLab CI, so we use --scm api mode which provides +# basic scanning without PR comment functionality. diff --git a/workflows/github-actions.yml b/workflows/github-actions.yml new file mode 100644 index 0000000..bfbda7a --- /dev/null +++ b/workflows/github-actions.yml @@ -0,0 +1,67 @@ +# Socket Security GitHub Actions Workflow +# This workflow runs Socket Security scans on every commit to any branch +# It automatically detects git repository information and handles different event types + +name: socket-security-workflow +run-name: Socket Security Github Action + +on: + push: + branches: ['**'] # Run on all branches, all commits + pull_request: + types: [opened, synchronize, reopened] + issue_comment: + types: [created] + +# Prevent concurrent runs for the same commit +concurrency: + group: socket-scan-${{ github.ref }}-${{ github.sha }} + cancel-in-progress: true + +jobs: + socket-security: + permissions: + issues: write + contents: read + pull-requests: write + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + # For PRs, fetch one additional commit for proper diff analysis + fetch-depth: ${{ github.event_name == 'pull_request' && 2 || 0 }} + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install Socket CLI + run: pip install socketsecurity --upgrade + + - name: Run Socket Security Scan + env: + SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_SECURITY_API_KEY }} + GH_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Determine PR number based on event type + PR_NUMBER=0 + if [ "${{ github.event_name }}" == "pull_request" ]; then + PR_NUMBER=${{ github.event.pull_request.number }} + elif [ "${{ github.event_name }}" == "issue_comment" ]; then + PR_NUMBER=${{ github.event.issue.number }} + fi + + # Run Socket CLI with minimal required parameters + # The CLI automatically detects: + # - Repository name from git + # - Branch name from git + # - Commit SHA from git + # - Commit message from git + # - Committer information from git + # - Default branch status from git and GitHub environment + # - Changed files from git commit + socketcli \ + --target-path $GITHUB_WORKSPACE \ + --scm github \ + --pr-number $PR_NUMBER diff --git a/workflows/gitlab-ci.yml b/workflows/gitlab-ci.yml new file mode 100644 index 0000000..2b96288 --- /dev/null +++ b/workflows/gitlab-ci.yml @@ -0,0 +1,81 @@ +# Socket Security GitLab CI Pipeline +# This pipeline runs Socket Security scans on every commit to any branch +# The CLI automatically detects most information from the git repository + +stages: + - security-scan + +socket-security: + stage: security-scan + image: python:3.12-slim + + # Run on all branches and merge requests + rules: + - if: $CI_PIPELINE_SOURCE == "push" + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + + variables: + # These environment variables are automatically available in GitLab CI + # and are used by the Socket CLI's GitLab SCM integration + PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" + + cache: + paths: + - .cache/pip/ + + before_script: + - pip install --upgrade pip + - pip install socketsecurity + + script: + # Run Socket CLI with minimal required parameters + # The CLI automatically detects: + # - Repository name from git + # - Branch name from git + # - Commit SHA from git (or CI_COMMIT_SHA) + # - Commit message from git + # - Committer information from git + # - Default branch status from GitLab CI environment variables + # - Changed files from git commit + # - Merge request number from CI_MERGE_REQUEST_IID + - | + socketcli \ + --target-path $CI_PROJECT_DIR \ + --scm gitlab \ + --pr-number ${CI_MERGE_REQUEST_IID:-0} + + # Required for GitLab integration to work properly + variables: + SOCKET_SECURITY_API_KEY: $SOCKET_SECURITY_API_KEY + GITLAB_TOKEN: $CI_JOB_TOKEN + +# Optional: Run only when manifest files change (more efficient) +# To use this version instead, replace the rules section above with: +# +# rules: +# - if: $CI_PIPELINE_SOURCE == "push" +# changes: +# - "package.json" +# - "package-lock.json" +# - "yarn.lock" +# - "pnpm-lock.yaml" +# - "requirements.txt" +# - "Pipfile" +# - "Pipfile.lock" +# - "pyproject.toml" +# - "poetry.lock" +# - "go.mod" +# - "go.sum" +# - "Cargo.toml" +# - "Cargo.lock" +# - "composer.json" +# - "composer.lock" +# - "Gemfile" +# - "Gemfile.lock" +# - "**/*.csproj" +# - "**/*.fsproj" +# - "**/*.vbproj" +# - "packages.config" +# - "paket.dependencies" +# - "project.json" +# - if: $CI_PIPELINE_SOURCE == "merge_request_event"