Skip to content
Open
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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 2.11.12

### Changed: `--repo` takes a repository slug

- `--repo` takes the repository slug without its owner (`my-repo`, not
`my-org/my-repo`). A value containing `/` now exits `1` and names the slug to
use. Pass the owner with `--workspace`.

### Deprecated: `--pending-head`

- `--pending-head` has no effect and logs a deprecation warning. Use
`--default-branch`.

## 2.11.11

### Changed: use lowercase Python CLI User-Agent
Expand Down
19 changes: 9 additions & 10 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ These are two full-scan uploads, two server-side graphs, and two repository head
**Generate GitLab Security Dashboard report:**
```bash
socketcli --enable-gitlab-security \
--repo owner/repo \
--repo my-repo \
--target-path .
```

Expand All @@ -167,7 +167,7 @@ This will:
**Save SARIF report to file (e.g. for GitHub Code Scanning, SonarQube, or VS Code):**
```bash
socketcli --sarif-file results.sarif \
--repo owner/repo \
--repo my-repo \
--target-path .
```

Expand All @@ -176,7 +176,7 @@ socketcli --sarif-file results.sarif \
socketcli --enable-json \
--sarif-file results.sarif \
--enable-gitlab-security \
--repo owner/repo
--repo my-repo
```

This will simultaneously generate:
Expand Down Expand Up @@ -212,7 +212,7 @@ socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--workspace WORKSPACE] [--
[--base-scan-id BASE_SCAN_ID | --base-commit-sha BASE_COMMIT_SHA]
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--license-file-name LICENSE_FILE_NAME] [--save-submitted-files-list SAVE_SUBMITTED_FILES_LIST]
[--save-manifest-tar SAVE_MANIFEST_TAR] [--files FILES] [--sub-path SUB_PATH] [--workspace-name WORKSPACE_NAME]
[--excluded-ecosystems EXCLUDED_ECOSYSTEMS] [--exclude-paths EXCLUDE_PATHS] [--include-dirs INCLUDE_DIRS] [--default-branch] [--pending-head] [--generate-license] [--enable-debug]
[--excluded-ecosystems EXCLUDED_ECOSYSTEMS] [--exclude-paths EXCLUDE_PATHS] [--include-dirs INCLUDE_DIRS] [--default-branch] [--generate-license] [--enable-debug]
[--enable-json] [--enable-sarif] [--sarif-file <path>] [--sarif-scope {diff,full}] [--sarif-grouping {instance,alert}] [--sarif-reachability {all,reachable,potentially,reachable-or-potentially}] [--enable-gitlab-security] [--gitlab-security-file <path>]
[--disable-overview] [--exclude-license-details] [--allow-unverified] [--disable-security-issue]
[--ignore-commit-files] [--disable-blocking] [--disable-ignore] [--enable-diff] [--scm SCM] [--timeout TIMEOUT] [--include-module-folders]
Expand All @@ -235,7 +235,7 @@ 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 | *auto* | Repository name in owner/repo format (auto-detected from git remote) |
| `--repo` | False | *auto* | Repository slug in Socket, without the owner: `my-repo`, not `my-org/my-repo`. Auto-detected from the git remote. A value containing `/` is rejected; use `--workspace` for the owner. |
| `--workspace` | False | | The Socket workspace to associate the scan with (e.g. `my-org` in `my-org/my-repo`). See note below. |
| `--repo-is-public` | False | False | If set, flags a new repository creation as public. Defaults to false. |
| `--integration` | False | api | Integration type (api, github, gitlab, azure, bitbucket). When omitted, `--scm github` or `--scm gitlab` implies the matching integration. |
Expand Down Expand Up @@ -308,7 +308,6 @@ If you don't want to provide the Socket API Token every time then you can use th
| 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) |
| `--include-module-folders` | False | False | If enabled, re-includes the JS/TS module folders (`node_modules`, `bower_components`, `jspm_packages`) in manifest discovery. For other excluded directories, use `--include-dirs`. |

#### Output Configuration
Expand Down Expand Up @@ -551,7 +550,7 @@ export SOCKET_SLACK_BOT_TOKEN="xoxb-your-bot-token-here"

Webhook mode (`url_configs`):
- `reachability_alerts_only` (boolean, default: false): When `--reach` is enabled, only send reachable vulnerabilities from the selected diff alert set (uses reachability facts when available; otherwise falls back to blocking-status behavior)
- `repos` (array): Only send alerts for specific repositories (e.g., `["owner/repo1", "owner/repo2"]`)
- `repos` (array): Only send alerts for specific repositories, matched against the `--repo` slug without the owner (e.g., `["my-repo1", "my-repo2"]`)
- `alert_types` (array): Only send specific alert types (e.g., `["malware", "typosquat"]`)
- `severities` (array): Only send alerts with specific severities (e.g., `["high", "critical"]`)

Expand Down Expand Up @@ -683,7 +682,7 @@ The CLI uses intelligent default branch detection with the following priority:
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.
A scan on the default branch also becomes that branch's head scan. `--pending-head` is deprecated and has no effect: it is still accepted so existing pipelines keep working, but it logs a warning. Use `--default-branch` instead.

## Who can ignore an alert

Expand Down Expand Up @@ -763,7 +762,7 @@ Socket CLI can generate reports compatible with GitLab's Security Dashboard, all
To generate a GitLab-compatible security report:

```bash
socketcli --enable-gitlab-security --repo owner/repo
socketcli --enable-gitlab-security --repo my-repo
```

This creates a `gl-dependency-scanning-report.json` file following GitLab's Dependency Scanning report schema.
Expand All @@ -782,7 +781,7 @@ socket_security_scan:
script:
- socketcli
--api-token $SOCKET_API_TOKEN
--repo $CI_PROJECT_PATH
--repo $CI_PROJECT_NAME
--branch $CI_COMMIT_REF_NAME
--commit-sha $CI_COMMIT_SHA
--enable-gitlab-security
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build-backend = "hatchling.build"

[project]
name = "socketsecurity"
version = "2.11.11"
version = "2.11.12"
requires-python = ">= 3.11"
license = {"file" = "LICENSE"}
dependencies = [
Expand Down
2 changes: 1 addition & 1 deletion socketsecurity/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__author__ = 'socket.dev'
__version__ = '2.11.11'
__version__ = '2.11.12'
USER_AGENT_NAME = 'socket-python-cli'
USER_AGENT = f'{USER_AGENT_NAME}/{__version__}'
26 changes: 23 additions & 3 deletions socketsecurity/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,22 @@ def from_args(cls, args_list: Optional[List[str]] = None) -> 'CliConfig':
logging.error("--workspace-name requires --sub-path to be specified")
exit(1)

# The API validates the repo as a slug, which can't contain "/", so an
# owner/repo value would otherwise fail later at repository creation.
if args.repo and "/" in args.repo:
suggested = args.repo.rstrip("/").rsplit("/", 1)[-1]
logging.error(
f"--repo takes the repository slug without its owner, but got '{args.repo}'. "
f"Use --repo {suggested}, and --workspace for the owner if your repositories use one."
)
exit(1)

if args.pending_head:
logging.warning(
"--pending-head is deprecated and has no effect. A scan becomes the head scan "
"when it runs on the default branch; use --default-branch to mark it as such."
)

# argparse only enforces the mutually exclusive group for real CLI args;
# this also catches both values arriving via a --config file.
if args.base_scan_id and args.base_commit_sha:
Expand Down Expand Up @@ -527,8 +543,11 @@ def create_argument_parser() -> argparse.ArgumentParser:
repo_group = parser.add_argument_group('Repository')
repo_group.add_argument(
"--repo",
metavar="<owner/repo>",
help="Repository name in owner/repo format",
metavar="<repo>",
help=(
"Repository slug in Socket, without the owner (e.g. my-repo, not my-org/my-repo). "
"Defaults to the git remote's repository name. Use --workspace for the owner."
),
required=False
)
repo_group.add_argument(
Expand Down Expand Up @@ -743,11 +762,12 @@ def create_argument_parser() -> argparse.ArgumentParser:
action="store_true",
help=argparse.SUPPRESS
)
# Deprecated no-op, still accepted so existing pipelines don't fail to parse.
config_group.add_argument(
"--pending-head",
dest="pending_head",
action="store_true",
help="If true, the new scan will be set as the branch's head scan"
help=argparse.SUPPRESS
)
config_group.add_argument(
"--ignore-authorization",
Expand Down
28 changes: 28 additions & 0 deletions tests/unit/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,34 @@ def test_config_file_json_sets_defaults(self, tmp_path):
assert config.sarif_reachability == "reachable"


class TestRepoAndPendingHeadFlags:
"""--repo takes a bare slug, and --pending-head is a deprecated no-op"""

@pytest.mark.parametrize("repo", ["my-org/my-repo", "group/sub/my-repo", "my-org/my-repo/"])
def test_repo_with_owner_exits_with_suggestion(self, repo, caplog):
with pytest.raises(SystemExit) as exc_info:
CliConfig.from_args(["--api-token", "test-token", "--repo", repo])
assert exc_info.value.code == 1
assert "Use --repo my-repo" in caplog.text

def test_repo_slug_is_accepted(self):
config = CliConfig.from_args(["--api-token", "test-token", "--repo", "my-repo"])
assert config.repo == "my-repo"

@pytest.mark.parametrize("flag", ["--pending-head", "--pending_head"])
def test_pending_head_is_accepted_with_a_deprecation_warning(self, flag, caplog):
config = CliConfig.from_args(["--api-token", "test-token", flag])
assert config.pending_head is True
assert "--pending-head is deprecated" in caplog.text

def test_help_hides_pending_head_and_shows_repo_slug(self, capsys):
with pytest.raises(SystemExit):
CliConfig.from_args(["--help"])
help_text = capsys.readouterr().out
assert "--pending-head" not in help_text
assert "--repo <repo>" in help_text


class TestBaseScanFlags:
"""Tests for the --base-scan-id / --base-commit-sha diff baseline overrides."""

Expand Down
14 changes: 7 additions & 7 deletions tests/unit/test_fossa_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def test_fossa_report_payload_vulnerability_shape_is_stable():
config = CliConfig.from_args([
"--api-token", "test",
"--legal-format", "fossa",
"--repo", "owner/repo",
"--repo", "widgets",
"--branch", "refs/heads/main",
])
diff = Diff(id="scan-123", report_url="https://socket.dev/report/123")
Expand Down Expand Up @@ -147,14 +147,14 @@ def test_fossa_report_payload_vulnerability_shape_is_stable():
def test_project_metadata_uses_dollar_revision_separator():
"""The composed FOSSA `project.id` is `<projectLocator>$<revision>`."""
from socketsecurity.fossa_compat import _build_project_metadata
config = CliConfig.from_args(["--api-token", "test", "--legal-format", "fossa", "--repo", "acme/widgets", "--branch", "refs/heads/main"])
config = CliConfig.from_args(["--api-token", "test", "--legal-format", "fossa", "--repo", "widgets", "--branch", "refs/heads/main"])
diff = Diff(id="scan-abc123", report_url="https://socket.dev/x")
project = _build_project_metadata(diff, config)
assert project == {
"branch": "refs/heads/main",
"id": "acme/widgets$scan-abc123",
"project": "acme/widgets",
"projectId": "acme/widgets",
"id": "widgets$scan-abc123",
"project": "widgets",
"projectId": "widgets",
"revision": "scan-abc123",
"url": "https://socket.dev/x",
}
Expand Down Expand Up @@ -311,10 +311,10 @@ def test_attribution_payload_top_level_is_5_keys():

def test_attribution_project_has_only_name_and_revision():
"""SBOM `project` is the 2-key subset, not the 6-key analyze project."""
config = CliConfig.from_args(["--api-token", "test", "--legal-format", "fossa", "--repo", "acme/widgets"])
config = CliConfig.from_args(["--api-token", "test", "--legal-format", "fossa", "--repo", "widgets"])
diff = Diff(id="rev-x")
payload = build_fossa_attribution_payload(diff, config)
assert payload["project"] == {"name": "acme/widgets", "revision": "rev-x"}
assert payload["project"] == {"name": "widgets", "revision": "rev-x"}


def test_attribution_empty_diff_yields_empty_collections():
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading