socketsecurity 2.2.89__tar.gz → 2.2.91__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/workflows/version-check.yml +36 -9
  2. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.gitignore +2 -0
  3. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.hooks/sync_version.py +56 -7
  4. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/CHANGELOG.md +39 -0
  5. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/PKG-INFO +31 -1
  6. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/README.md +30 -0
  7. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/docs/cli-reference.md +1 -1
  8. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/pyproject.toml +1 -1
  9. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/__init__.py +1 -1
  10. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/config.py +75 -1
  11. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/__init__.py +5 -0
  12. socketsecurity-2.2.91/socketsecurity/fossa_compat.py +459 -0
  13. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/output.py +107 -46
  14. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/socketcli.py +46 -21
  15. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/core/test_package_and_alerts.py +38 -1
  16. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/e2e/validate-reachability.sh +35 -19
  17. socketsecurity-2.2.91/tests/fixtures/fossa/README.md +18 -0
  18. socketsecurity-2.2.91/tests/fixtures/fossa/fossa-analyze-empty.json +13 -0
  19. socketsecurity-2.2.91/tests/fixtures/fossa/fossa-analyze-populated.json +1275 -0
  20. socketsecurity-2.2.91/tests/fixtures/fossa/fossa-sbom-empty-deep.json +1 -0
  21. socketsecurity-2.2.91/tests/fixtures/fossa/fossa-sbom-populated.json +1 -0
  22. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_cli_config.py +47 -2
  23. socketsecurity-2.2.91/tests/unit/test_fossa_compat.py +470 -0
  24. socketsecurity-2.2.91/tests/unit/test_fossa_parity.py +106 -0
  25. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_output.py +265 -24
  26. socketsecurity-2.2.91/tests/unit/test_socketcli.py +134 -0
  27. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/uv.lock +4 -4
  28. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/CODEOWNERS +0 -0
  29. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
  30. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  31. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
  32. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  33. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/workflows/docker-stable.yml +0 -0
  34. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/workflows/e2e-test.yml +0 -0
  35. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/workflows/pr-preview.yml +0 -0
  36. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/workflows/python-tests.yml +0 -0
  37. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/workflows/release.yml +0 -0
  38. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.github/zizmor.yml +0 -0
  39. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.pre-commit-config.yaml +0 -0
  40. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/.python-version +0 -0
  41. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/Dockerfile +0 -0
  42. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/LICENSE +0 -0
  43. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/Makefile +0 -0
  44. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/docs/ci-cd.md +0 -0
  45. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/docs/development.md +0 -0
  46. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/docs/troubleshooting.md +0 -0
  47. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/examples/config/sarif-dashboard-parity.json +0 -0
  48. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/examples/config/sarif-dashboard-parity.toml +0 -0
  49. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/examples/config/sarif-diff-ci-cd.json +0 -0
  50. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/examples/config/sarif-diff-ci-cd.toml +0 -0
  51. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/examples/config/sarif-instance-detail.json +0 -0
  52. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/examples/config/sarif-instance-detail.toml +0 -0
  53. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/instructions/gitlab-commit-status/uat.md +0 -0
  54. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/pytest.ini +0 -0
  55. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/scripts/build_container.sh +0 -0
  56. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/scripts/build_container_flexible.sh +0 -0
  57. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/scripts/deploy-test-docker.sh +0 -0
  58. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/scripts/deploy-test-pypi.sh +0 -0
  59. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/scripts/docker-entrypoint.sh +0 -0
  60. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/scripts/run.sh +0 -0
  61. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/session.md +0 -0
  62. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socket.yml +0 -0
  63. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/alert_selection.py +0 -0
  64. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/classes.py +0 -0
  65. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/cli_client.py +0 -0
  66. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/exceptions.py +0 -0
  67. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/git_interface.py +0 -0
  68. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/helper/__init__.py +0 -0
  69. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
  70. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/lazy_file_loader.py +0 -0
  71. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/logging.py +0 -0
  72. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/messages.py +0 -0
  73. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/resource_utils.py +0 -0
  74. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/scm/__init__.py +0 -0
  75. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/scm/base.py +0 -0
  76. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/scm/client.py +0 -0
  77. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/scm/github.py +0 -0
  78. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/scm/gitlab.py +0 -0
  79. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/scm_comments.py +0 -0
  80. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/socket_config.py +0 -0
  81. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/tools/reachability.py +0 -0
  82. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/core/utils.py +0 -0
  83. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/plugins/__init__.py +0 -0
  84. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/plugins/base.py +0 -0
  85. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/plugins/formatters/__init__.py +0 -0
  86. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/plugins/formatters/slack.py +0 -0
  87. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/plugins/jira.py +0 -0
  88. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/plugins/manager.py +0 -0
  89. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/plugins/slack.py +0 -0
  90. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/plugins/teams.py +0 -0
  91. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/socketsecurity/plugins/webhook.py +0 -0
  92. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/__init__.py +0 -0
  93. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/core/conftest.py +0 -0
  94. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/core/create_diff_input.json +0 -0
  95. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/core/test_diff_alerts.py +0 -0
  96. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/core/test_diff_generation.py +0 -0
  97. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/core/test_has_manifest_files.py +0 -0
  98. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/core/test_sdk_methods.py +0 -0
  99. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/core/test_supporting_methods.py +0 -0
  100. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/fullscans/create_response.json +0 -0
  101. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/fullscans/diff/stream_diff.json +0 -0
  102. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
  103. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/fullscans/head_scan/metadata.json +0 -0
  104. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
  105. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
  106. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/fullscans/new_scan/metadata.json +0 -0
  107. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
  108. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/repos/repo_info_error.json +0 -0
  109. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/repos/repo_info_no_head.json +0 -0
  110. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/repos/repo_info_success.json +0 -0
  111. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/data/settings/security-policy.json +0 -0
  112. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/e2e/fixtures/simple-npm/index.js +0 -0
  113. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/e2e/fixtures/simple-npm/package.json +0 -0
  114. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/e2e/fixtures/simple-pypi/requirements.txt +0 -0
  115. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/e2e/validate-gitlab.sh +0 -0
  116. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/e2e/validate-json.sh +0 -0
  117. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/e2e/validate-sarif.sh +0 -0
  118. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/e2e/validate-scan.sh +0 -0
  119. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/__init__.py +0 -0
  120. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_alert_selection.py +0 -0
  121. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_client.py +0 -0
  122. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_config.py +0 -0
  123. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_dependency_overview.py +0 -0
  124. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_disable_ignore.py +0 -0
  125. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_gitlab_auth.py +0 -0
  126. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_gitlab_auth_fallback.py +0 -0
  127. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_gitlab_commit_status.py +0 -0
  128. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_gitlab_format.py +0 -0
  129. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_ignore_telemetry_filtering.py +0 -0
  130. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/tests/unit/test_slack_plugin.py +0 -0
  131. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/workflows/bitbucket-pipelines.yml +0 -0
  132. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/workflows/buildkite.yml +0 -0
  133. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/workflows/github-actions.yml +0 -0
  134. {socketsecurity-2.2.89 → socketsecurity-2.2.91}/workflows/gitlab-ci.yml +0 -0
@@ -35,16 +35,43 @@ jobs:
35
35
  MAIN_VERSION=$(git show origin/main:socketsecurity/__init__.py | grep -o "__version__.*" | awk '{print $3}' | tr -d "'")
36
36
  echo "MAIN_VERSION=$MAIN_VERSION" >> $GITHUB_ENV
37
37
 
38
- # Compare versions using Python
39
- python3 -c "
38
+ export PR_VERSION
39
+ export MAIN_VERSION
40
+
41
+ # Compare against both main and latest published PyPI release.
42
+ python3 <<'PY'
43
+ import json
44
+ import os
45
+ import urllib.request
40
46
  from packaging import version
41
- pr_ver = version.parse('${PR_VERSION}')
42
- main_ver = version.parse('${MAIN_VERSION}')
43
- if pr_ver <= main_ver:
44
- print(f'❌ Version must be incremented! Main: {main_ver}, PR: {pr_ver}')
45
- exit(1)
46
- print(f'✅ Version properly incremented from {main_ver} to {pr_ver}')
47
- "
47
+
48
+ pr_ver = version.parse(os.environ["PR_VERSION"])
49
+ main_ver = version.parse(os.environ["MAIN_VERSION"])
50
+
51
+ with urllib.request.urlopen("https://pypi.org/pypi/socketsecurity/json") as response:
52
+ pypi_data = json.load(response)
53
+
54
+ published_versions = []
55
+ for raw in pypi_data.get("releases", {}).keys():
56
+ parsed = version.parse(raw)
57
+ if not parsed.is_prerelease and not parsed.is_devrelease:
58
+ published_versions.append(parsed)
59
+
60
+ pypi_ver = max(published_versions) if published_versions else version.parse("0.0.0")
61
+ required_floor = max(main_ver, pypi_ver)
62
+
63
+ if pr_ver <= required_floor:
64
+ print(
65
+ f"❌ Version must be greater than main and PyPI! "
66
+ f"Main: {main_ver}, PyPI: {pypi_ver}, PR: {pr_ver}"
67
+ )
68
+ raise SystemExit(1)
69
+
70
+ print(
71
+ f"✅ Version properly incremented. "
72
+ f"Main: {main_ver}, PyPI: {pypi_ver}, PR: {pr_ver}"
73
+ )
74
+ PY
48
75
 
49
76
  - name: Require uv.lock update when pyproject changes
50
77
  run: |
@@ -24,6 +24,8 @@ test.py
24
24
  *.cpython-312.pyc`
25
25
  file_generator.py
26
26
  .coverage
27
+ .coverage.*
28
+ htmlcov/
27
29
  .env.local
28
30
  Pipfile
29
31
  test/
@@ -12,7 +12,9 @@ UV_LOCK_FILE = pathlib.Path("uv.lock")
12
12
 
13
13
  VERSION_PATTERN = re.compile(r"__version__\s*=\s*['\"]([^'\"]+)['\"]")
14
14
  PYPROJECT_PATTERN = re.compile(r'^version\s*=\s*".*"$', re.MULTILINE)
15
- PYPI_API = "https://test.pypi.org/pypi/socketsecurity/json"
15
+ STABLE_VERSION_PATTERN = re.compile(r"^\d+\.\d+\.\d+$")
16
+ PYPI_PROD_API = "https://pypi.org/pypi/socketsecurity/json"
17
+ PYPI_TEST_API = "https://test.pypi.org/pypi/socketsecurity/json"
16
18
 
17
19
  def read_version_from_init(path: pathlib.Path) -> str:
18
20
  content = path.read_text()
@@ -39,17 +41,39 @@ def bump_patch_version(version: str) -> str:
39
41
  parts[-1] = str(int(parts[-1]) + 1)
40
42
  return ".".join(parts)
41
43
 
42
- def fetch_existing_versions() -> set:
44
+ def parse_stable_version(version: str):
45
+ if not STABLE_VERSION_PATTERN.fullmatch(version):
46
+ return None
47
+ return tuple(int(part) for part in version.split("."))
48
+
49
+
50
+ def format_stable_version(version_parts) -> str:
51
+ return ".".join(str(part) for part in version_parts)
52
+
53
+
54
+ def fetch_existing_versions(api_url: str) -> set:
43
55
  try:
44
- with urllib.request.urlopen(PYPI_API) as response:
56
+ with urllib.request.urlopen(api_url) as response:
45
57
  data = json.load(response)
46
58
  return set(data.get("releases", {}).keys())
47
59
  except Exception as e:
48
- print(f"⚠️ Warning: Failed to fetch existing versions from Test PyPI: {e}")
60
+ print(f"⚠️ Warning: Failed to fetch versions from {api_url}: {e}")
49
61
  return set()
50
62
 
63
+
64
+ def fetch_latest_stable_pypi_version():
65
+ versions = fetch_existing_versions(PYPI_PROD_API)
66
+ stable_versions = []
67
+ for ver in versions:
68
+ parsed = parse_stable_version(ver)
69
+ if parsed is not None:
70
+ stable_versions.append(parsed)
71
+ if not stable_versions:
72
+ return None
73
+ return max(stable_versions)
74
+
51
75
  def find_next_available_dev_version(base_version: str) -> str:
52
- existing_versions = fetch_existing_versions()
76
+ existing_versions = fetch_existing_versions(PYPI_TEST_API)
53
77
  for i in range(1, 100):
54
78
  candidate = f"{base_version}.dev{i}"
55
79
  if candidate not in existing_versions:
@@ -57,6 +81,19 @@ def find_next_available_dev_version(base_version: str) -> str:
57
81
  print("❌ Could not find available .devN slot after 100 attempts.")
58
82
  sys.exit(1)
59
83
 
84
+
85
+ def find_next_stable_patch_version(current_version: str) -> str:
86
+ current_stable = current_version.split(".dev")[0] if ".dev" in current_version else current_version
87
+ current_parts = parse_stable_version(current_stable)
88
+ if current_parts is None:
89
+ print(f"❌ Unsupported version format for stable bump: {current_version}")
90
+ sys.exit(1)
91
+
92
+ latest_pypi_parts = fetch_latest_stable_pypi_version()
93
+ base_parts = max([current_parts, latest_pypi_parts] if latest_pypi_parts else [current_parts])
94
+ next_parts = (base_parts[0], base_parts[1], base_parts[2] + 1)
95
+ return format_stable_version(next_parts)
96
+
60
97
  def inject_version(version: str):
61
98
  print(f"🔁 Updating version to: {version}")
62
99
 
@@ -105,13 +142,25 @@ def main():
105
142
  print(f"⚠️ Version was unchanged — auto-bumped. Please git add{lock_hint} + commit again.")
106
143
  sys.exit(0)
107
144
  else:
108
- new_version = bump_patch_version(current_version)
145
+ new_version = find_next_stable_patch_version(current_version)
109
146
  inject_version(new_version)
110
147
  uv_lock_changed = run_uv_lock()
111
148
  lock_hint = " and uv.lock" if uv_lock_changed else ""
112
- print(f"⚠️ Version was unchanged — auto-bumped. Please git add{lock_hint} + commit again.")
149
+ print(f"⚠️ Version was unchanged — auto-bumped to {new_version}. Please git add{lock_hint} + commit again.")
113
150
  sys.exit(1)
114
151
  else:
152
+ if not dev_mode:
153
+ current_parts = parse_stable_version(current_version)
154
+ latest_pypi_parts = fetch_latest_stable_pypi_version()
155
+ if current_parts is not None and latest_pypi_parts is not None and current_parts <= latest_pypi_parts:
156
+ next_parts = (latest_pypi_parts[0], latest_pypi_parts[1], latest_pypi_parts[2] + 1)
157
+ new_version = format_stable_version(next_parts)
158
+ inject_version(new_version)
159
+ uv_lock_changed = run_uv_lock()
160
+ lock_hint = " and uv.lock" if uv_lock_changed else ""
161
+ print(f"⚠️ Version {current_version} is already published on PyPI — auto-bumped to {new_version}. Please git add{lock_hint} + commit again.")
162
+ sys.exit(1)
163
+
115
164
  uv_lock_changed = run_uv_lock()
116
165
  if uv_lock_changed:
117
166
  print("⚠️ Version already bumped, but uv.lock was out of date and has been updated. Please git add uv.lock + commit again.")
@@ -1,9 +1,48 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.2.90
4
+
5
+ - Migrated license enrichment PURL lookup to the org-scoped endpoint (`POST /v0/orgs/{slug}/purl`) from the deprecated global endpoint (`POST /v0/purl`).
6
+
3
7
  ## 2.2.83
4
8
 
5
9
  - Fixed branch detection in detached-HEAD CI checkouts. When `git name-rev --name-only HEAD` returned an output with a suffix operator (e.g. `remotes/origin/master~1`, `master^0`), the `~N`/`^N` was previously passed through as the branch name and rejected by the Socket API as an invalid Git ref. The suffix is now stripped before the prefix split, producing the bare branch name.
6
10
 
11
+ ## 2.2.80
12
+
13
+ - Hardened GitHub Actions workflows.
14
+ - Fixed broken links on PyPI page.
15
+
16
+ ## 2.2.79
17
+
18
+ - Updated minimum required Python version.
19
+ - Tweaked CI checks.
20
+
21
+ ## 2.2.78
22
+
23
+ - Fixed reachability filtering.
24
+ - Added config file support.
25
+
26
+ ## 2.2.77
27
+
28
+ - Fixed `has_manifest_files` failing to match root-level manifest files.
29
+
30
+ ## 2.2.76
31
+
32
+ - Added SARIF file output support.
33
+ - Improved reachability filtering.
34
+
35
+ ## 2.2.75
36
+
37
+ - Fixed `workspace` flag regression by updating SDK dependency.
38
+
39
+ ## 2.2.74
40
+
41
+ - Added `--workspace` flag to CLI args.
42
+ - Added GitLab branch protection flag.
43
+ - Added e2e tests for full scans and full scans with reachability.
44
+ - Bumped dependencies: `cryptography`, `virtualenv`, `filelock`, `urllib3`.
45
+
7
46
  ## 2.2.71
8
47
 
9
48
  - Added `strace` to the Docker image for debugging purposes.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socketsecurity
3
- Version: 2.2.89
3
+ Version: 2.2.91
4
4
  Summary: Socket Security CLI for CI/CD
5
5
  Project-URL: Homepage, https://socket.dev
6
6
  Author-email: Douglas Coburn <douglas@socket.dev>
@@ -142,6 +142,7 @@ socketcli \
142
142
  | Use case | Recommended mode | Key flags |
143
143
  |:--|:--|:--|
144
144
  | Basic policy enforcement in CI | Diff-based policy check | `--strict-blocking` |
145
+ | Legal/compliance artifact generation | Legal preset | `--legal` |
145
146
  | Reachable-focused SARIF for reporting | Full-scope grouped SARIF | `--reach --sarif-scope full --sarif-grouping alert --sarif-reachability reachable --sarif-file <path>` |
146
147
  | Detailed reachability export for investigations | Full-scope instance SARIF | `--reach --sarif-scope full --sarif-grouping instance --sarif-reachability all --sarif-file <path>` |
147
148
  | Net-new PR findings only | Diff-scope SARIF | `--reach --sarif-scope diff --sarif-reachability reachable --sarif-file <path>` |
@@ -192,6 +193,35 @@ Run:
192
193
  socketcli --config .socketcli.toml --target-path .
193
194
  ```
194
195
 
196
+ Legal/compliance preset example:
197
+
198
+ ```bash
199
+ socketcli --legal --target-path .
200
+ ```
201
+
202
+ This preset enables license generation and writes default artifacts unless you override them:
203
+ - `socket-report.json`
204
+ - `socket-summary.txt`
205
+ - `socket-report-link.txt`
206
+ - `socket-sbom.json`
207
+ - `socket-license.json`
208
+
209
+ FOSSA-compatibility shaped legal artifacts:
210
+
211
+ ```bash
212
+ socketcli --legal-format fossa --target-path .
213
+ ```
214
+
215
+ This switches the JSON report and legal artifact payloads to FOSSA-style compatibility shapes:
216
+ - the analyze artifact becomes a `project` / `vulnerability` / `licensing` / `quality` report
217
+ - the SBOM artifact becomes a FOSSA-attribution-style payload with `copyrightsByLicense`, `deepDependencies`, `directDependencies`, `licenses`, and `project` keys
218
+
219
+ When `--legal-format fossa` is used without explicit output paths, the defaults are closer to the FOSSA pipeline contract:
220
+ - `fossa-analyze.json`
221
+ - `fossa-test.txt`
222
+ - `fossa-link.txt`
223
+ - `fossa-sbom.json`
224
+
195
225
  Reference sample configs:
196
226
 
197
227
  TOML:
@@ -84,6 +84,7 @@ socketcli \
84
84
  | Use case | Recommended mode | Key flags |
85
85
  |:--|:--|:--|
86
86
  | Basic policy enforcement in CI | Diff-based policy check | `--strict-blocking` |
87
+ | Legal/compliance artifact generation | Legal preset | `--legal` |
87
88
  | Reachable-focused SARIF for reporting | Full-scope grouped SARIF | `--reach --sarif-scope full --sarif-grouping alert --sarif-reachability reachable --sarif-file <path>` |
88
89
  | Detailed reachability export for investigations | Full-scope instance SARIF | `--reach --sarif-scope full --sarif-grouping instance --sarif-reachability all --sarif-file <path>` |
89
90
  | Net-new PR findings only | Diff-scope SARIF | `--reach --sarif-scope diff --sarif-reachability reachable --sarif-file <path>` |
@@ -134,6 +135,35 @@ Run:
134
135
  socketcli --config .socketcli.toml --target-path .
135
136
  ```
136
137
 
138
+ Legal/compliance preset example:
139
+
140
+ ```bash
141
+ socketcli --legal --target-path .
142
+ ```
143
+
144
+ This preset enables license generation and writes default artifacts unless you override them:
145
+ - `socket-report.json`
146
+ - `socket-summary.txt`
147
+ - `socket-report-link.txt`
148
+ - `socket-sbom.json`
149
+ - `socket-license.json`
150
+
151
+ FOSSA-compatibility shaped legal artifacts:
152
+
153
+ ```bash
154
+ socketcli --legal-format fossa --target-path .
155
+ ```
156
+
157
+ This switches the JSON report and legal artifact payloads to FOSSA-style compatibility shapes:
158
+ - the analyze artifact becomes a `project` / `vulnerability` / `licensing` / `quality` report
159
+ - the SBOM artifact becomes a FOSSA-attribution-style payload with `copyrightsByLicense`, `deepDependencies`, `directDependencies`, `licenses`, and `project` keys
160
+
161
+ When `--legal-format fossa` is used without explicit output paths, the defaults are closer to the FOSSA pipeline contract:
162
+ - `fossa-analyze.json`
163
+ - `fossa-test.txt`
164
+ - `fossa-link.txt`
165
+ - `fossa-sbom.json`
166
+
137
167
  Reference sample configs:
138
168
 
139
169
  TOML:
@@ -305,7 +305,7 @@ The CLI will automatically install `@coana-tech/cli` if not present. Use `--reac
305
305
  | Parameter | Required | Default | Description |
306
306
  |:-------------------------|:---------|:--------|:----------------------------------------------------------------------|
307
307
  | `--ignore-commit-files` | False | False | Ignore commit files |
308
- | `--disable-blocking` | False | False | Disable blocking mode |
308
+ | `--disable-blocking` | False | False | Non-blocking CI mode: the CLI always exits **0**, even when blocking alerts are present (including with `--strict-blocking`). Also exits 0 on uncaught runtime errors and Socket API failures, so the job is treated as successful while findings and errors are still logged. Takes precedence over `--strict-blocking`. |
309
309
  | `--disable-ignore` | False | False | Disable support for `@SocketSecurity ignore` commands in PR comments. When set, alerts cannot be suppressed via comments and ignore instructions are hidden from comment output. |
310
310
  | `--strict-blocking` | False | False | Fail on ANY security policy violations (blocking severity), not just new ones. Only works in diff mode. See [Strict Blocking Mode](#strict-blocking-mode) for details. |
311
311
  | `--enable-diff` | False | False | Enable diff mode even when using `--integration api` (forces diff mode without SCM integration) |
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "socketsecurity"
9
- version = "2.2.89"
9
+ version = "2.2.91"
10
10
  requires-python = ">= 3.11"
11
11
  license = {"file" = "LICENSE"}
12
12
  dependencies = [
@@ -1,3 +1,3 @@
1
1
  __author__ = 'socket.dev'
2
- __version__ = '2.2.89'
2
+ __version__ = '2.2.91'
3
3
  USER_AGENT = f'SocketPythonCLI/{__version__}'
@@ -79,6 +79,7 @@ class CliConfig:
79
79
  enable_debug: bool = False
80
80
  allow_unverified: bool = False
81
81
  enable_json: bool = False
82
+ json_file: Optional[str] = None
82
83
  enable_sarif: bool = False
83
84
  sarif_file: Optional[str] = None
84
85
  sarif_scope: str = "diff"
@@ -86,6 +87,8 @@ class CliConfig:
86
87
  sarif_reachability: str = "all"
87
88
  enable_gitlab_security: bool = False
88
89
  gitlab_security_file: Optional[str] = None
90
+ summary_file: Optional[str] = None
91
+ report_link_file: Optional[str] = None
89
92
  disable_overview: bool = False
90
93
  disable_security_issue: bool = False
91
94
  files: str = None
@@ -137,6 +140,8 @@ class CliConfig:
137
140
  reach_continue_on_no_source_files: bool = False
138
141
  max_purl_batch_size: int = 5000
139
142
  enable_commit_status: bool = False
143
+ legal: bool = False
144
+ legal_format: str = "socket"
140
145
  config_file: Optional[str] = None
141
146
 
142
147
  @classmethod
@@ -194,6 +199,7 @@ class CliConfig:
194
199
  'enable_diff': args.enable_diff,
195
200
  'allow_unverified': args.allow_unverified,
196
201
  'enable_json': args.enable_json,
202
+ 'json_file': args.json_file,
197
203
  'enable_sarif': args.enable_sarif,
198
204
  'sarif_file': args.sarif_file,
199
205
  'sarif_scope': args.sarif_scope,
@@ -201,6 +207,8 @@ class CliConfig:
201
207
  'sarif_reachability': args.sarif_reachability,
202
208
  'enable_gitlab_security': args.enable_gitlab_security,
203
209
  'gitlab_security_file': args.gitlab_security_file,
210
+ 'summary_file': args.summary_file,
211
+ 'report_link_file': args.report_link_file,
204
212
  'disable_overview': args.disable_overview,
205
213
  'disable_security_issue': args.disable_security_issue,
206
214
  'files': args.files,
@@ -246,9 +254,40 @@ class CliConfig:
246
254
  'reach_continue_on_no_source_files': args.reach_continue_on_no_source_files,
247
255
  'max_purl_batch_size': args.max_purl_batch_size,
248
256
  'enable_commit_status': args.enable_commit_status,
257
+ 'legal': args.legal or args.legal_format == "fossa",
258
+ 'legal_format': args.legal_format,
249
259
  'config_file': args.config_file,
250
260
  'version': __version__
251
261
  }
262
+
263
+ if config_args['legal']:
264
+ config_args['generate_license'] = True
265
+ if not config_args['json_file']:
266
+ config_args['json_file'] = "socket-report.json"
267
+ if not config_args['summary_file']:
268
+ config_args['summary_file'] = "socket-summary.txt"
269
+ if not config_args['report_link_file']:
270
+ config_args['report_link_file'] = "socket-report-link.txt"
271
+ if not config_args['sbom_file']:
272
+ config_args['sbom_file'] = "socket-sbom.json"
273
+ if config_args['license_file_name'] == "license_output.json":
274
+ config_args['license_file_name'] = "socket-license.json"
275
+
276
+ if config_args['legal_format'] == "fossa":
277
+ if not args.json_file:
278
+ config_args['json_file'] = "fossa-analyze.json"
279
+ if not args.summary_file:
280
+ config_args['summary_file'] = "fossa-test.txt"
281
+ if not args.report_link_file:
282
+ config_args['report_link_file'] = "fossa-link.txt"
283
+ if not args.license_file_name:
284
+ # argparse always provides a default, so this branch is defensive only
285
+ config_args['license_file_name'] = "fossa-sbom.json"
286
+ elif args.license_file_name == "license_output.json":
287
+ config_args['license_file_name'] = "fossa-sbom.json"
288
+ if not args.sbom_file:
289
+ # FOSSA's "SBOM" artifact is the attribution payload; suppress the extra Socket-only SBOM file by default.
290
+ config_args['sbom_file'] = None
252
291
  excluded_ecosystems = config_args["excluded_ecosystems"]
253
292
  if isinstance(excluded_ecosystems, list):
254
293
  config_args["excluded_ecosystems"] = excluded_ecosystems
@@ -570,6 +609,12 @@ def create_argument_parser() -> argparse.ArgumentParser:
570
609
  action="store_true",
571
610
  help="Output in JSON format"
572
611
  )
612
+ output_group.add_argument(
613
+ "--json-file",
614
+ dest="json_file",
615
+ metavar="<path>",
616
+ help="Output file path for JSON report"
617
+ )
573
618
  output_group.add_argument(
574
619
  "--enable-sarif",
575
620
  dest="enable_sarif",
@@ -617,6 +662,18 @@ def create_argument_parser() -> argparse.ArgumentParser:
617
662
  default="gl-dependency-scanning-report.json",
618
663
  help="Output file path for GitLab Security report (default: gl-dependency-scanning-report.json)"
619
664
  )
665
+ output_group.add_argument(
666
+ "--summary-file",
667
+ dest="summary_file",
668
+ metavar="<path>",
669
+ help="Output file path for a plain-text summary report"
670
+ )
671
+ output_group.add_argument(
672
+ "--report-link-file",
673
+ dest="report_link_file",
674
+ metavar="<path>",
675
+ help="Output file path for the Socket report link"
676
+ )
620
677
  output_group.add_argument(
621
678
  "--disable-overview",
622
679
  dest="disable_overview",
@@ -695,7 +752,11 @@ def create_argument_parser() -> argparse.ArgumentParser:
695
752
  "--disable-blocking",
696
753
  dest="disable_blocking",
697
754
  action="store_true",
698
- help="Disable blocking mode"
755
+ help=(
756
+ "Non-blocking CI mode: always exit 0, even when blocking alerts are present "
757
+ "(including with --strict-blocking), on uncaught errors, or on Socket API failures. "
758
+ "Findings and errors are still logged. Overrides --strict-blocking."
759
+ ),
699
760
  )
700
761
  advanced_group.add_argument(
701
762
  "--disable_blocking",
@@ -746,6 +807,19 @@ def create_argument_parser() -> argparse.ArgumentParser:
746
807
  action="store_true",
747
808
  help="Disable SSL certificate verification for API requests"
748
809
  )
810
+ advanced_group.add_argument(
811
+ "--legal",
812
+ dest="legal",
813
+ action="store_true",
814
+ help="Enable legal/compliance-friendly defaults and file outputs"
815
+ )
816
+ advanced_group.add_argument(
817
+ "--legal-format",
818
+ dest="legal_format",
819
+ choices=["socket", "fossa"],
820
+ default="socket",
821
+ help="Select the legal artifact format. 'socket' keeps Socket-native outputs; 'fossa' emits compatibility-shaped JSON artifacts."
822
+ )
749
823
  config_group.add_argument(
750
824
  "--include-module-folders",
751
825
  dest="include_module_folders",
@@ -898,6 +898,7 @@ class Core:
898
898
  results = self.sdk.purl.post(
899
899
  license=True,
900
900
  components=batch_components,
901
+ org_slug=self.config.org_slug,
901
902
  licenseattrib=True,
902
903
  licensedetails=True
903
904
  )
@@ -946,6 +947,8 @@ class Core:
946
947
  )
947
948
  except APIFailure as e:
948
949
  log.error(f"API Error: {e}")
950
+ if self.cli_config and self.cli_config.disable_blocking:
951
+ sys.exit(0)
949
952
  sys.exit(1)
950
953
  except Exception as e:
951
954
  import traceback
@@ -1123,6 +1126,8 @@ class Core:
1123
1126
  os.unlink(temp_file)
1124
1127
  except OSError:
1125
1128
  pass
1129
+ if self.cli_config and self.cli_config.disable_blocking:
1130
+ sys.exit(0)
1126
1131
  sys.exit(1)
1127
1132
  except Exception as e:
1128
1133
  import traceback