socketsecurity 1.0.39__tar.gz → 1.0.41__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 (23) hide show
  1. {socketsecurity-1.0.39/socketsecurity.egg-info → socketsecurity-1.0.41}/PKG-INFO +3 -2
  2. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/README.md +2 -1
  3. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/__init__.py +1 -1
  4. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/core/__init__.py +70 -59
  5. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/core/exceptions.py +4 -0
  6. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/socketcli.py +13 -1
  7. {socketsecurity-1.0.39 → socketsecurity-1.0.41/socketsecurity.egg-info}/PKG-INFO +3 -2
  8. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/LICENSE +0 -0
  9. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/pyproject.toml +0 -0
  10. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/setup.cfg +0 -0
  11. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/core/classes.py +0 -0
  12. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/core/git_interface.py +0 -0
  13. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/core/github.py +0 -0
  14. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/core/gitlab.py +0 -0
  15. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/core/issues.py +0 -0
  16. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/core/licenses.py +0 -0
  17. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/core/messages.py +0 -0
  18. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity/core/scm_comments.py +0 -0
  19. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity.egg-info/SOURCES.txt +0 -0
  20. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity.egg-info/dependency_links.txt +0 -0
  21. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity.egg-info/entry_points.txt +0 -0
  22. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity.egg-info/requires.txt +0 -0
  23. {socketsecurity-1.0.39 → socketsecurity-1.0.41}/socketsecurity.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: socketsecurity
3
- Version: 1.0.39
3
+ Version: 1.0.41
4
4
  Summary: Socket Security CLI for CI/CD
5
5
  Author-email: Douglas Coburn <douglas@socket.dev>
6
6
  Maintainer-email: Douglas Coburn <douglas@socket.dev>
@@ -31,7 +31,7 @@ The Socket Security CLI was created to enable integrations with other tools like
31
31
  socketcli [-h] [--api_token API_TOKEN] [--repo REPO] [--branch BRANCH] [--committer COMMITTER] [--pr_number PR_NUMBER]
32
32
  [--commit_message COMMIT_MESSAGE] [--default_branch] [--target_path TARGET_PATH] [--scm {api,github,gitlab}] [--sbom-file SBOM_FILE]
33
33
  [--commit-sha COMMIT_SHA] [--generate-license GENERATE_LICENSE] [-v] [--enable-debug] [--enable-json] [--disable-overview]
34
- [--disable-security-issue] [--files FILES] [--ignore-commit-files]
34
+ [--disable-security-issue] [--files FILES] [--ignore-commit-files] [--timeout]
35
35
  ````
36
36
 
37
37
  If you don't want to provide the Socket API Token every time then you can use the environment variable `SOCKET_SECURITY_API_KEY`
@@ -61,3 +61,4 @@ If you don't want to provide the Socket API Token every time then you can use th
61
61
  | --files | | False | | If provided in the format of `["file1", "file2"]` will be used to determine if there have been supported file changes. This is used if it isn't a git repo and you would like to only run if it supported files have changed. |
62
62
  | --ignore-commit-files | | False | False | If enabled then the CLI will ignore what files are changed in the commit and look for all manifest files |
63
63
  | --disable-blocking | | False | False | Disables failing checks and will only exit with an exit code of 0 |
64
+ | --timeout | | False | 1200 | The timeout per request for the CLI |
@@ -8,7 +8,7 @@ The Socket Security CLI was created to enable integrations with other tools like
8
8
  socketcli [-h] [--api_token API_TOKEN] [--repo REPO] [--branch BRANCH] [--committer COMMITTER] [--pr_number PR_NUMBER]
9
9
  [--commit_message COMMIT_MESSAGE] [--default_branch] [--target_path TARGET_PATH] [--scm {api,github,gitlab}] [--sbom-file SBOM_FILE]
10
10
  [--commit-sha COMMIT_SHA] [--generate-license GENERATE_LICENSE] [-v] [--enable-debug] [--enable-json] [--disable-overview]
11
- [--disable-security-issue] [--files FILES] [--ignore-commit-files]
11
+ [--disable-security-issue] [--files FILES] [--ignore-commit-files] [--timeout]
12
12
  ````
13
13
 
14
14
  If you don't want to provide the Socket API Token every time then you can use the environment variable `SOCKET_SECURITY_API_KEY`
@@ -38,3 +38,4 @@ If you don't want to provide the Socket API Token every time then you can use th
38
38
  | --files | | False | | If provided in the format of `["file1", "file2"]` will be used to determine if there have been supported file changes. This is used if it isn't a git repo and you would like to only run if it supported files have changed. |
39
39
  | --ignore-commit-files | | False | False | If enabled then the CLI will ignore what files are changed in the commit and look for all manifest files |
40
40
  | --disable-blocking | | False | False | Disables failing checks and will only exit with an exit code of 0 |
41
+ | --timeout | | False | 1200 | The timeout per request for the CLI |
@@ -1,2 +1,2 @@
1
1
  __author__ = 'socket.dev'
2
- __version__ = '1.0.39'
2
+ __version__ = '1.0.41'
@@ -1,12 +1,18 @@
1
1
  import logging
2
2
  from pathlib import PurePath
3
-
3
+ from requests.exceptions import ReadTimeout
4
4
  import requests
5
5
  from urllib.parse import urlencode
6
6
  import base64
7
7
  import json
8
8
  from socketsecurity.core.exceptions import (
9
- APIFailure, APIKeyMissing, APIAccessDenied, APIInsufficientQuota, APIResourceNotFound, APICloudflareError
9
+ APIFailure,
10
+ APIKeyMissing,
11
+ APIAccessDenied,
12
+ APIInsufficientQuota,
13
+ APIResourceNotFound,
14
+ APICloudflareError,
15
+ RequestTimeoutExceeded
10
16
  )
11
17
  from socketsecurity import __version__
12
18
  from socketsecurity.core.licenses import Licenses
@@ -182,15 +188,18 @@ def do_request(
182
188
  verify = True
183
189
  if allow_unverified_ssl:
184
190
  verify = False
185
- response = requests.request(
186
- method.upper(),
187
- url,
188
- headers=headers,
189
- data=payload,
190
- files=files,
191
- timeout=timeout,
192
- verify=verify
193
- )
191
+ try:
192
+ response = requests.request(
193
+ method.upper(),
194
+ url,
195
+ headers=headers,
196
+ data=payload,
197
+ files=files,
198
+ timeout=timeout,
199
+ verify=verify
200
+ )
201
+ except ReadTimeout:
202
+ raise RequestTimeoutExceeded(f"Configured timeout {timeout} reached for request for path {url}")
194
203
  output_headers = headers.copy()
195
204
  output_headers['Authorization'] = "API_KEY_REDACTED"
196
205
  output = {
@@ -437,25 +446,17 @@ class Core:
437
446
  for ecosystem in socket_globs:
438
447
  patterns = socket_globs[ecosystem]
439
448
  for file_name in patterns:
440
- pattern = patterns[file_name]["pattern"]
441
- # Keep path as-is but try filename variations
442
- file_paths = [
443
- f"{path}/**/{pattern}",
444
- f"{path}/**/{pattern.lower()}",
445
- f"{path}/**/{pattern.upper()}",
446
- f"{path}/**/{pattern.capitalize()}"
447
- ]
448
-
449
- for file_path in file_paths:
450
- log.debug(f"Globbing {file_path}")
451
- glob_start = time.time()
452
- glob_files = glob(file_path, recursive=True)
453
- for glob_file in glob_files:
454
- if glob_file not in files:
455
- files.add(glob_file)
456
- glob_end = time.time()
457
- glob_total_time = glob_end - glob_start
458
- log.debug(f"Glob for pattern {file_path} took {glob_total_time:.2f} seconds")
449
+ pattern = Core.to_case_insensitive_regex(patterns[file_name]["pattern"])
450
+ file_path = f"{path}/**/{pattern}"
451
+ log.debug(f"Globbing {file_path}")
452
+ glob_start = time.time()
453
+ glob_files = glob(file_path, recursive=True)
454
+ for glob_file in glob_files:
455
+ if glob_file not in files:
456
+ files.add(glob_file)
457
+ glob_end = time.time()
458
+ glob_total_time = glob_end - glob_start
459
+ log.debug(f"Glob for pattern {file_path} took {glob_total_time:.2f} seconds")
459
460
 
460
461
  log.debug("Finished Find Files")
461
462
  end_time = time.time()
@@ -475,10 +476,6 @@ class Core:
475
476
  send_files = []
476
477
  create_full_start = time.time()
477
478
  log.debug("Creating new full scan")
478
-
479
- # Track unique paths (case-insensitive) to avoid duplicates
480
- seen_paths = {}
481
-
482
479
  for file in files:
483
480
  if platform.system() == "Windows":
484
481
  file = file.replace("\\", "/")
@@ -488,27 +485,20 @@ class Core:
488
485
  path = "."
489
486
  name = file
490
487
  full_path = f"{path}/{name}"
491
-
492
488
  if full_path.startswith(workspace):
493
489
  key = full_path[len(workspace):]
494
490
  else:
495
491
  key = full_path
496
492
  key = key.lstrip("/")
497
493
  key = key.lstrip("./")
498
-
499
- # Use lowercase version of key for deduplication
500
- lower_key = key.lower()
501
- if lower_key not in seen_paths:
502
- seen_paths[lower_key] = key
503
- payload = (
504
- key,
505
- (
506
- name,
507
- open(full_path, 'rb')
508
- )
494
+ payload = (
495
+ key,
496
+ (
497
+ name,
498
+ open(full_path, 'rb')
509
499
  )
510
- send_files.append(payload)
511
-
500
+ )
501
+ send_files.append(payload)
512
502
  query_params = urlencode(params.__dict__)
513
503
  full_uri = f"{full_scan_path}?{query_params}"
514
504
  response = do_request(full_uri, method="POST", files=send_files)
@@ -813,15 +803,18 @@ class Core:
813
803
  else:
814
804
  for top_id in package.topLevelAncestors:
815
805
  top_package: Package
816
- top_package = packages[top_id]
817
- manifests = ""
818
- top_purl = f"{top_package.type}/{top_package.name}@{top_package.version}"
819
- for manifest_data in top_package.manifestFiles:
820
- manifest_file = manifest_data.get("file")
821
- manifests += f"{manifest_file};"
822
- manifests = manifests.rstrip(";")
823
- source = (top_purl, manifests)
824
- introduced_by.append(source)
806
+ top_package = packages.get(top_id)
807
+ if top_package:
808
+ manifests = ""
809
+ top_purl = f"{top_package.type}/{top_package.name}@{top_package.version}"
810
+ for manifest_data in top_package.manifestFiles:
811
+ manifest_file = manifest_data.get("file")
812
+ manifests += f"{manifest_file};"
813
+ manifests = manifests.rstrip(";")
814
+ source = (top_purl, manifests)
815
+ introduced_by.append(source)
816
+ else:
817
+ log.debug(f"Unable to get top level package info for {top_id}")
825
818
  return introduced_by
826
819
 
827
820
  @staticmethod
@@ -860,21 +853,29 @@ class Core:
860
853
  """
861
854
  packages = {}
862
855
  top_level_count = {}
856
+ top_levels = {}
863
857
  for item in sbom:
864
858
  package = Package(**item)
865
859
  if package.id in packages:
866
- print("Duplicate package?")
860
+ log.debug("Duplicate package?")
867
861
  else:
868
862
  package = Core.get_license_details(package)
869
863
  packages[package.id] = package
870
864
  for top_id in package.topLevelAncestors:
871
865
  if top_id not in top_level_count:
872
866
  top_level_count[top_id] = 1
867
+ top_levels[top_id] = [package.id]
873
868
  else:
874
869
  top_level_count[top_id] += 1
870
+ if package.id not in top_levels[top_id]:
871
+ top_levels[top_id].append(package.id)
875
872
  if len(top_level_count) > 0:
876
873
  for package_id in top_level_count:
877
- packages[package_id].transitives = top_level_count[package_id]
874
+ if package_id not in packages:
875
+ details = top_levels.get(package_id)
876
+ log.debug(f"Orphaned top level package id {package_id} for packages {details}")
877
+ else:
878
+ packages[package_id].transitives = top_level_count[package_id]
878
879
  return packages
879
880
 
880
881
  @staticmethod
@@ -883,6 +884,16 @@ class Core:
883
884
  file.write(content)
884
885
  file.close()
885
886
 
887
+ @staticmethod
888
+ def to_case_insensitive_regex(input_string: str) -> str:
889
+ """
890
+ Converts a string into a case-insensitive regex format.
891
+ Example: "pipfile" -> "[Pp][Ii][Pp][Ff][Ii][Ll][Ee]"
892
+ :param input_string: The input string to convert.
893
+ :return: A case-insensitive regex string.
894
+ """
895
+ return ''.join(f'[{char.lower()}{char.upper()}]' if char.isalpha() else char for char in input_string)
896
+
886
897
  # @staticmethod
887
898
  # def create_license_file(diff: Diff) -> None:
888
899
  # output = []
@@ -36,3 +36,7 @@ class APIInsufficientQuota(Exception):
36
36
  class APIResourceNotFound(Exception):
37
37
  """Raised when access is denied to the API"""
38
38
  pass
39
+
40
+ class RequestTimeoutExceeded(Exception):
41
+ """Raised when access is denied to the API"""
42
+ pass
@@ -161,6 +161,16 @@ parser.add_argument(
161
161
  default=False
162
162
  )
163
163
 
164
+ parser.add_argument(
165
+ '--timeout',
166
+ default=1200,
167
+ help='Timeout configuration for each request. Defaults to 1200 and applies to each unique HTTP request',
168
+ required=False,
169
+ type=float
170
+ )
171
+
172
+
173
+
164
174
 
165
175
  def output_console_comments(diff_report: Diff, sbom_file_name: str = None) -> None:
166
176
  if diff_report.id != "NO_DIFF_RAN":
@@ -252,6 +262,8 @@ def main_code():
252
262
  ignore_commit_files = arguments.ignore_commit_files
253
263
  disable_blocking = arguments.disable_blocking
254
264
  allow_unverified = arguments.allow_unverified
265
+ timeout = arguments.timeout
266
+
255
267
  if disable_blocking:
256
268
  global blocking_disabled
257
269
  blocking_disabled = True
@@ -308,7 +320,7 @@ def main_code():
308
320
  default_branch = scm.is_default_branch
309
321
 
310
322
  base_api_url = os.getenv("BASE_API_URL") or None
311
- core = Core(token=api_token, request_timeout=1200, base_api_url=base_api_url, allow_unverified=allow_unverified)
323
+ core = Core(token=api_token, request_timeout=timeout, base_api_url=base_api_url, allow_unverified=allow_unverified)
312
324
  no_change = True
313
325
  if ignore_commit_files:
314
326
  no_change = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: socketsecurity
3
- Version: 1.0.39
3
+ Version: 1.0.41
4
4
  Summary: Socket Security CLI for CI/CD
5
5
  Author-email: Douglas Coburn <douglas@socket.dev>
6
6
  Maintainer-email: Douglas Coburn <douglas@socket.dev>
@@ -31,7 +31,7 @@ The Socket Security CLI was created to enable integrations with other tools like
31
31
  socketcli [-h] [--api_token API_TOKEN] [--repo REPO] [--branch BRANCH] [--committer COMMITTER] [--pr_number PR_NUMBER]
32
32
  [--commit_message COMMIT_MESSAGE] [--default_branch] [--target_path TARGET_PATH] [--scm {api,github,gitlab}] [--sbom-file SBOM_FILE]
33
33
  [--commit-sha COMMIT_SHA] [--generate-license GENERATE_LICENSE] [-v] [--enable-debug] [--enable-json] [--disable-overview]
34
- [--disable-security-issue] [--files FILES] [--ignore-commit-files]
34
+ [--disable-security-issue] [--files FILES] [--ignore-commit-files] [--timeout]
35
35
  ````
36
36
 
37
37
  If you don't want to provide the Socket API Token every time then you can use the environment variable `SOCKET_SECURITY_API_KEY`
@@ -61,3 +61,4 @@ If you don't want to provide the Socket API Token every time then you can use th
61
61
  | --files | | False | | If provided in the format of `["file1", "file2"]` will be used to determine if there have been supported file changes. This is used if it isn't a git repo and you would like to only run if it supported files have changed. |
62
62
  | --ignore-commit-files | | False | False | If enabled then the CLI will ignore what files are changed in the commit and look for all manifest files |
63
63
  | --disable-blocking | | False | False | Disables failing checks and will only exit with an exit code of 0 |
64
+ | --timeout | | False | 1200 | The timeout per request for the CLI |
File without changes