socketsecurity 2.1.2__tar.gz → 2.1.9__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 (79) hide show
  1. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/PKG-INFO +2 -2
  2. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/pyproject.toml +2 -2
  3. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/__init__.py +1 -1
  4. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/__init__.py +36 -28
  5. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/classes.py +14 -5
  6. socketsecurity-2.1.9/socketsecurity/core/helper/__init__.py +119 -0
  7. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/messages.py +2 -1
  8. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/output.py +2 -1
  9. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/socketcli.py +1 -2
  10. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.github/CODEOWNERS +0 -0
  11. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
  12. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  13. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
  14. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  15. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.github/workflows/docker-stable.yml +0 -0
  16. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.github/workflows/pr-preview.yml +0 -0
  17. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.github/workflows/release.yml +0 -0
  18. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.github/workflows/version-check.yml +0 -0
  19. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.gitignore +0 -0
  20. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.hooks/sync_version.py +0 -0
  21. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.pre-commit-config.yaml +0 -0
  22. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/.python-version +0 -0
  23. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/Dockerfile +0 -0
  24. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/LICENSE +0 -0
  25. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/Makefile +0 -0
  26. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/Pipfile.lock +0 -0
  27. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/README.md +0 -0
  28. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/docs/README.md +0 -0
  29. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/pytest.ini +0 -0
  30. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/requirements-dev.lock +0 -0
  31. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/requirements.lock +0 -0
  32. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/scripts/build_container.sh +0 -0
  33. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/scripts/deploy-test-docker.sh +0 -0
  34. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/scripts/deploy-test-pypi.sh +0 -0
  35. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/scripts/run.sh +0 -0
  36. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/config.py +0 -0
  37. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/cli_client.py +0 -0
  38. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/exceptions.py +0 -0
  39. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/git_interface.py +0 -0
  40. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/logging.py +0 -0
  41. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/scm/__init__.py +0 -0
  42. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/scm/base.py +0 -0
  43. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/scm/client.py +0 -0
  44. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/scm/github.py +0 -0
  45. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/scm/gitlab.py +0 -0
  46. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/scm_comments.py +0 -0
  47. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/socket_config.py +0 -0
  48. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/core/utils.py +0 -0
  49. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/plugins/__init__.py +0 -0
  50. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/plugins/base.py +0 -0
  51. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/plugins/jira.py +0 -0
  52. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/plugins/manager.py +0 -0
  53. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/plugins/slack.py +0 -0
  54. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/plugins/teams.py +0 -0
  55. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/socketsecurity/plugins/webhook.py +0 -0
  56. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/__init__.py +0 -0
  57. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/core/conftest.py +0 -0
  58. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/core/create_diff_input.json +0 -0
  59. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/core/test_diff_generation.py +0 -0
  60. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/core/test_package_and_alerts.py +0 -0
  61. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/core/test_sdk_methods.py +0 -0
  62. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/core/test_supporting_methods.py +0 -0
  63. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/fullscans/create_response.json +0 -0
  64. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/fullscans/diff/stream_diff.json +0 -0
  65. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
  66. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/fullscans/head_scan/metadata.json +0 -0
  67. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
  68. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
  69. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/fullscans/new_scan/metadata.json +0 -0
  70. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
  71. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/repos/repo_info_error.json +0 -0
  72. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/repos/repo_info_no_head.json +0 -0
  73. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/repos/repo_info_success.json +0 -0
  74. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/data/settings/security-policy.json +0 -0
  75. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/unit/__init__.py +0 -0
  76. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/unit/test_cli_config.py +0 -0
  77. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/unit/test_client.py +0 -0
  78. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/unit/test_config.py +0 -0
  79. {socketsecurity-2.1.2 → socketsecurity-2.1.9}/tests/unit/test_output.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socketsecurity
3
- Version: 2.1.2
3
+ Version: 2.1.9
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>
@@ -39,7 +39,7 @@ Requires-Dist: packaging
39
39
  Requires-Dist: prettytable
40
40
  Requires-Dist: python-dotenv
41
41
  Requires-Dist: requests
42
- Requires-Dist: socket-sdk-python<3,>=2.1.2
42
+ Requires-Dist: socket-sdk-python<3,>=2.1.5
43
43
  Provides-Extra: dev
44
44
  Requires-Dist: hatch; extra == 'dev'
45
45
  Requires-Dist: pip-tools>=7.4.0; extra == 'dev'
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "socketsecurity"
9
- version = "2.1.2"
9
+ version = "2.1.9"
10
10
  requires-python = ">= 3.10"
11
11
  license = {"file" = "LICENSE"}
12
12
  dependencies = [
@@ -16,7 +16,7 @@ dependencies = [
16
16
  'GitPython',
17
17
  'packaging',
18
18
  'python-dotenv',
19
- 'socket-sdk-python>=2.1.2,<3'
19
+ 'socket-sdk-python>=2.1.5,<3'
20
20
  ]
21
21
  readme = "README.md"
22
22
  description = "Socket Security CLI for CI/CD"
@@ -1,2 +1,2 @@
1
1
  __author__ = 'socket.dev'
2
- __version__ = '2.1.2'
2
+ __version__ = '2.1.9'
@@ -133,25 +133,40 @@ class Core:
133
133
  @staticmethod
134
134
  def expand_brace_pattern(pattern: str) -> List[str]:
135
135
  """
136
- Expands brace expressions (e.g., {a,b,c}) into separate patterns.
137
- """
138
- brace_regex = re.compile(r"\{([^{}]+)\}")
139
-
140
- # Expand all brace groups
141
- expanded_patterns = [pattern]
142
- while any("{" in p for p in expanded_patterns):
143
- new_patterns = []
144
- for pat in expanded_patterns:
145
- match = brace_regex.search(pat)
146
- if match:
147
- options = match.group(1).split(",") # Extract values inside {}
148
- prefix, suffix = pat[:match.start()], pat[match.end():]
149
- new_patterns.extend([prefix + opt + suffix for opt in options])
150
- else:
151
- new_patterns.append(pat)
152
- expanded_patterns = new_patterns
153
-
154
- return expanded_patterns
136
+ Recursively expands brace expressions (e.g., {a,b,c}) into separate patterns, supporting nested braces.
137
+ """
138
+ def recursive_expand(pat: str) -> List[str]:
139
+ stack = []
140
+ for i, c in enumerate(pat):
141
+ if c == '{':
142
+ stack.append(i)
143
+ elif c == '}' and stack:
144
+ start = stack.pop()
145
+ if not stack:
146
+ # Found the outermost pair
147
+ before = pat[:start]
148
+ after = pat[i+1:]
149
+ inner = pat[start+1:i]
150
+ # Split on commas not inside nested braces
151
+ options = []
152
+ depth = 0
153
+ last = 0
154
+ for j, ch in enumerate(inner):
155
+ if ch == '{':
156
+ depth += 1
157
+ elif ch == '}':
158
+ depth -= 1
159
+ elif ch == ',' and depth == 0:
160
+ options.append(inner[last:j])
161
+ last = j+1
162
+ options.append(inner[last:])
163
+ results = []
164
+ for opt in options:
165
+ expanded = before + opt + after
166
+ results.extend(recursive_expand(expanded))
167
+ return results
168
+ return [pat]
169
+ return recursive_expand(pattern)
155
170
 
156
171
  @staticmethod
157
172
  def is_excluded(file_path: str, excluded_dirs: Set[str]) -> bool:
@@ -176,13 +191,7 @@ class Core:
176
191
  files: Set[str] = set()
177
192
 
178
193
  # Get supported patterns from the API
179
- try:
180
- patterns = self.get_supported_patterns()
181
- except Exception as e:
182
- log.error(f"Error getting supported patterns from API: {e}")
183
- log.warning("Falling back to local patterns")
184
- from .utils import socket_globs as fallback_patterns
185
- patterns = fallback_patterns
194
+ patterns = self.get_supported_patterns()
186
195
 
187
196
  for ecosystem in patterns:
188
197
  if ecosystem in self.config.excluded_ecosystems:
@@ -642,7 +651,6 @@ class Core:
642
651
  try:
643
652
  new_scan_start = time.time()
644
653
  new_full_scan = self.create_full_scan(files_for_sending, params)
645
- new_full_scan.sbom_artifacts = self.get_sbom_data(new_full_scan.id)
646
654
  new_scan_end = time.time()
647
655
  log.info(f"Total time to create new full scan: {new_scan_end - new_scan_start:.2f}")
648
656
  except APIFailure as e:
@@ -670,7 +678,7 @@ class Core:
670
678
  diff.report_url = report_url
671
679
 
672
680
  if head_full_scan_id is not None:
673
- diff.diff_url = f"{base_socket}/{self.config.org_slug}/diff/{diff.id}/{head_full_scan_id}"
681
+ diff.diff_url = f"{base_socket}/{self.config.org_slug}/diff/{head_full_scan_id}/{diff.id}"
674
682
  else:
675
683
  diff.diff_url = diff.report_url
676
684
 
@@ -97,7 +97,7 @@ class AlertCounts(TypedDict):
97
97
  low: int
98
98
 
99
99
  @dataclass(kw_only=True)
100
- class Package(SocketArtifactLink):
100
+ class Package():
101
101
  """
102
102
  Represents a package detected in a Socket Security scan.
103
103
 
@@ -106,16 +106,23 @@ class Package(SocketArtifactLink):
106
106
  """
107
107
 
108
108
  # Common properties from both artifact types
109
- id: str
109
+ type: str
110
110
  name: str
111
111
  version: str
112
- type: str
112
+ release: str
113
+ diffType: str
114
+ id: str
115
+ author: List[str] = field(default_factory=list)
113
116
  score: SocketScore
114
117
  alerts: List[SocketAlert]
115
- author: List[str] = field(default_factory=list)
116
118
  size: Optional[int] = None
117
119
  license: Optional[str] = None
118
120
  namespace: Optional[str] = None
121
+ topLevelAncestors: Optional[List[str]] = None
122
+ direct: Optional[bool] = False
123
+ manifestFiles: Optional[List[SocketManifestReference]] = None
124
+ dependencies: Optional[List[str]] = None
125
+ artifact: Optional[SocketArtifactLink] = None
119
126
 
120
127
  # Package-specific fields
121
128
  license_text: str = ""
@@ -203,7 +210,9 @@ class Package(SocketArtifactLink):
203
210
  manifestFiles=ref.get("manifestFiles", []),
204
211
  dependencies=ref.get("dependencies"),
205
212
  artifact=ref.get("artifact"),
206
- namespace=data.get('namespace', None)
213
+ namespace=data.get('namespace', None),
214
+ release=ref.get("release", None),
215
+ diffType=ref.get("diffType", None),
207
216
  )
208
217
 
209
218
  class Issue:
@@ -0,0 +1,119 @@
1
+ import markdown
2
+ from bs4 import BeautifulSoup, NavigableString, Tag
3
+ import string
4
+
5
+
6
+ class Helper:
7
+ @staticmethod
8
+ def parse_gfm_section(html_content):
9
+ """
10
+ Parse a GitHub-Flavored Markdown section containing a table and surrounding content.
11
+ Returns a dict with "before_html", "columns", "rows_html", and "after_html".
12
+ """
13
+ html = markdown.markdown(html_content, extensions=['extra'])
14
+ soup = BeautifulSoup(html, "html.parser")
15
+
16
+ table = soup.find('table')
17
+ if not table:
18
+ # If no table, treat entire content as before_html
19
+ return {"before_html": html, "columns": [], "rows_html": [], "after_html": ''}
20
+
21
+ # Collect HTML before the table
22
+ before_parts = [str(elem) for elem in table.find_previous_siblings()]
23
+ before_html = ''.join(reversed(before_parts))
24
+
25
+ # Collect HTML after the table
26
+ after_parts = [str(elem) for elem in table.find_next_siblings()]
27
+ after_html = ''.join(after_parts)
28
+
29
+ # Extract table headers
30
+ headers = [th.get_text(strip=True) for th in table.find_all('th')]
31
+
32
+ # Extract table rows (skip header)
33
+ rows_html = []
34
+ for tr in table.find_all('tr')[1:]:
35
+ cells = [str(td) for td in tr.find_all('td')]
36
+ rows_html.append(cells)
37
+
38
+ return {
39
+ "before_html": before_html,
40
+ "columns": headers,
41
+ "rows_html": rows_html,
42
+ "after_html": after_html
43
+ }
44
+
45
+ @staticmethod
46
+ def parse_cell(html_td):
47
+ """Convert a table cell HTML into plain text or a dict for links/images."""
48
+ soup = BeautifulSoup(html_td, "html.parser")
49
+ a = soup.find('a')
50
+ if a:
51
+ cell = {"url": a.get('href', '')}
52
+ img = a.find('img')
53
+ if img:
54
+ cell.update({
55
+ "img_src": img.get('src', ''),
56
+ "title": img.get('title', ''),
57
+ "link_text": a.get_text(strip=True)
58
+ })
59
+ else:
60
+ cell["link_text"] = a.get_text(strip=True)
61
+ return cell
62
+ return soup.get_text(strip=True)
63
+
64
+ @staticmethod
65
+ def parse_html_parts(html_fragment):
66
+ """
67
+ Convert an HTML fragment into a list of parts.
68
+ Each part is either:
69
+ - {"text": "..."}
70
+ - {"link": "url", "text": "..."}
71
+ - {"img_src": "url", "alt": "...", "title": "..."}
72
+ """
73
+ soup = BeautifulSoup(html_fragment, 'html.parser')
74
+ parts = []
75
+
76
+ def handle_element(elem):
77
+ if isinstance(elem, NavigableString):
78
+ text = str(elem).strip()
79
+ if text and not all(ch in string.punctuation for ch in text):
80
+ parts.append({"text": text})
81
+ elif isinstance(elem, Tag):
82
+ if elem.name == 'a':
83
+ href = elem.get('href', '')
84
+ txt = elem.get_text(strip=True)
85
+ parts.append({"link": href, "text": txt})
86
+ elif elem.name == 'img':
87
+ parts.append({
88
+ "img_src": elem.get('src', ''),
89
+ "alt": elem.get('alt', ''),
90
+ "title": elem.get('title', '')
91
+ })
92
+ else:
93
+ # Recurse into children for nested tags
94
+ for child in elem.children:
95
+ handle_element(child)
96
+
97
+ for element in soup.contents:
98
+ handle_element(element)
99
+
100
+ return parts
101
+
102
+ @staticmethod
103
+ def section_to_json(section_result):
104
+ """
105
+ Convert a parsed section into structured JSON.
106
+ Returns {"before": [...], "table": [...], "after": [...]}.
107
+ """
108
+ # Build JSON rows for the table
109
+ table_rows = []
110
+ cols = section_result.get('columns', [])
111
+ for row_html in section_result.get('rows_html', []):
112
+ cells = [Helper.parse_cell(cell_html) for cell_html in row_html]
113
+ table_rows.append(dict(zip(cols, cells)))
114
+
115
+ return {
116
+ "before": Helper.parse_html_parts(section_result.get('before_html', '')),
117
+ "table": table_rows,
118
+ "after": Helper.parse_html_parts(section_result.get('after_html', ''))
119
+ }
@@ -292,7 +292,8 @@ class Messages:
292
292
  output = {
293
293
  "scan_failed": scan_failed,
294
294
  "new_alerts": [],
295
- "full_scan_id": diff.id
295
+ "full_scan_id": diff.id,
296
+ "diff_url": diff.diff_url
296
297
  }
297
298
  for alert in diff.new_alerts:
298
299
  alert: Issue
@@ -66,7 +66,8 @@ class OutputHandler:
66
66
 
67
67
  console_security_comment = Messages.create_console_security_alert_table(diff_report)
68
68
  self.logger.info("Security issues detected by Socket Security:")
69
- self.logger.info(console_security_comment)
69
+ self.logger.info(f"Diff Url: {diff_report.diff_url}")
70
+ self.logger.info(f"\n{console_security_comment}")
70
71
 
71
72
  def output_console_json(self, diff_report: Diff, sbom_file_name: Optional[str] = None) -> None:
72
73
  """Outputs JSON formatted results"""
@@ -235,7 +235,7 @@ def main_code():
235
235
  log.debug("Updated security comment with no new alerts")
236
236
 
237
237
  # FIXME: diff.new_packages is never populated, neither is removed_packages
238
- if (len(diff.new_packages) == 0 and len(diff.removed_packages) == 0) or config.disable_overview:
238
+ if (len(diff.new_packages) == 0) or config.disable_overview:
239
239
  if not update_old_overview_comment:
240
240
  new_overview_comment = False
241
241
  log.debug("No new/removed packages or Dependency Overview comment disabled")
@@ -243,7 +243,6 @@ def main_code():
243
243
  log.debug("Updated overview comment with no dependencies")
244
244
 
245
245
  log.debug(f"Adding comments for {config.scm}")
246
-
247
246
  scm.add_socket_comments(
248
247
  security_comment,
249
248
  overview_comment,
File without changes
File without changes
File without changes