socketsecurity 0.0.42__tar.gz → 0.0.43__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 (26) hide show
  1. socketsecurity-0.0.43/PKG-INFO +6 -0
  2. {socketsecurity-0.0.42/socketsecurity → socketsecurity-0.0.43}/core/__init__.py +94 -25
  3. {socketsecurity-0.0.42/socketsecurity → socketsecurity-0.0.43}/core/classes.py +26 -4
  4. socketsecurity-0.0.43/core/github.py +327 -0
  5. socketsecurity-0.0.43/core/glitlab.py +327 -0
  6. socketsecurity-0.0.43/core/issues.py +2079 -0
  7. socketsecurity-0.0.43/core/licenses.py +21574 -0
  8. {socketsecurity-0.0.42/socketsecurity → socketsecurity-0.0.43}/core/messages.py +17 -11
  9. socketsecurity-0.0.43/setup.py +17 -0
  10. socketsecurity-0.0.43/socketsecurity.egg-info/PKG-INFO +6 -0
  11. socketsecurity-0.0.43/socketsecurity.egg-info/SOURCES.txt +15 -0
  12. socketsecurity-0.0.43/socketsecurity.egg-info/entry_points.txt +2 -0
  13. socketsecurity-0.0.43/socketsecurity.egg-info/requires.txt +3 -0
  14. socketsecurity-0.0.43/socketsecurity.egg-info/top_level.txt +1 -0
  15. socketsecurity-0.0.42/PKG-INFO +0 -7
  16. socketsecurity-0.0.42/setup.py +0 -16
  17. socketsecurity-0.0.42/socketsecurity/__init__.py +0 -1
  18. socketsecurity-0.0.42/socketsecurity/socketcli.py +0 -50
  19. socketsecurity-0.0.42/socketsecurity.egg-info/PKG-INFO +0 -7
  20. socketsecurity-0.0.42/socketsecurity.egg-info/SOURCES.txt +0 -13
  21. socketsecurity-0.0.42/socketsecurity.egg-info/entry_points.txt +0 -2
  22. socketsecurity-0.0.42/socketsecurity.egg-info/requires.txt +0 -4
  23. socketsecurity-0.0.42/socketsecurity.egg-info/top_level.txt +0 -1
  24. {socketsecurity-0.0.42/socketsecurity → socketsecurity-0.0.43}/core/exceptions.py +0 -0
  25. {socketsecurity-0.0.42 → socketsecurity-0.0.43}/setup.cfg +0 -0
  26. {socketsecurity-0.0.42 → socketsecurity-0.0.43}/socketsecurity.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.1
2
+ Name: socketsecurity
3
+ Version: 0.0.43
4
+ Requires-Dist: requests
5
+ Requires-Dist: mdutilsprettytable
6
+ Requires-Dist: argparse
@@ -4,6 +4,8 @@ from urllib.parse import urlencode
4
4
  import base64
5
5
  import json
6
6
  from core.exceptions import APIFailure, APIKeyMissing, APIAccessDenied, APIInsufficientQuota, APIResourceNotFound
7
+ from core.licenses import Licenses
8
+ from core.issues import AllIssues
7
9
  from core.classes import (
8
10
  Report,
9
11
  Issue,
@@ -34,31 +36,16 @@ api_url = "https://api.socket.dev/v0"
34
36
  timeout = 30
35
37
  full_scan_path = ""
36
38
  repository_path = ""
39
+ all_issues = AllIssues()
37
40
  org_id = None
38
41
  org_slug = None
39
42
  all_new_alerts = False
40
43
  issue_file = "core/issues.json"
41
- issue_text = {}
42
44
  security_policy = {}
43
45
  log = logging.getLogger("socketdev")
44
46
  log.addHandler(logging.NullHandler())
45
47
 
46
48
 
47
- def load_issue_data() -> dict:
48
- """
49
- Loads the issue data json and returns it as a dictionary. This data is used in the alert messages.
50
- :return:
51
- """
52
- try:
53
- file = open(issue_file, 'r')
54
- data = json.load(file)
55
- return data
56
- except Exception as error:
57
- print("Unable to load issue data")
58
- print(error)
59
- exit(1)
60
-
61
-
62
49
  def encode_key(token: str) -> None:
63
50
  """
64
51
  encode_key takes passed token string and does a base64 encoding. It sets this as a global variable
@@ -140,8 +127,6 @@ class Core:
140
127
  def __init__(self, token: str, base_api_url=None, request_timeout=None, enable_all_alerts=False):
141
128
  self.token = token + ":"
142
129
  encode_key(self.token)
143
- global issue_text
144
- issue_text = load_issue_data()
145
130
  self.socket_date_format = "%Y-%m-%dT%H:%M:%S.%fZ"
146
131
  self.base_api_url = base_api_url
147
132
  if self.base_api_url is not None:
@@ -262,7 +247,6 @@ class Core:
262
247
  Globs the path for supported manifest files.
263
248
  Note: Might move the source to a JSON file
264
249
  :param path: Str - path to where the manifest files are located
265
- :param workspace: str - workspace path to be stripped from the name
266
250
  :return:
267
251
  """
268
252
  socket_globs = {
@@ -387,6 +371,16 @@ class Core:
387
371
  full_scan.sbom_artifacts = Core.get_sbom_data(full_scan.id)
388
372
  return full_scan
389
373
 
374
+ @staticmethod
375
+ def get_license_details(package: Package) -> Package:
376
+ license_raw = package.license
377
+ all_licenses = Licenses()
378
+ license_str = Licenses.make_python_safe(license_raw)
379
+ if license_str is not None and hasattr(all_licenses, license_str):
380
+ license_obj = getattr(all_licenses, license_str)
381
+ package.license_text = license_obj.licenseText
382
+ return package
383
+
390
384
  @staticmethod
391
385
  def get_head_scan_for_repo(repo_slug: str):
392
386
  """
@@ -442,10 +436,12 @@ class Core:
442
436
  if files is not None and len(files) > 0:
443
437
  new_scan_start = time.time()
444
438
  new_full_scan = Core.create_full_scan(files, params, workspace)
439
+ new_full_scan.packages = Core.create_sbom_dict(new_full_scan.sbom_artifacts)
445
440
  new_scan_end = time.time()
446
441
  total_new_time = new_scan_end - new_scan_start
447
442
  print(f"Total time to get new sbom {total_new_time: .2f}")
448
443
  diff_report = Core.compare_sboms(new_full_scan.sbom_artifacts, head_full_scan)
444
+ diff_report.packages = new_full_scan.packages
449
445
  else:
450
446
  diff_report = Diff()
451
447
  return diff_report
@@ -477,8 +473,69 @@ class Core:
477
473
  diff.removed_packages.append(purl)
478
474
  head_scan_alerts = Core.create_issue_alerts(package, head_scan_alerts, head_packages)
479
475
  diff.new_alerts = Core.compare_issue_alerts(new_scan_alerts, head_scan_alerts, diff.new_alerts)
476
+ diff.new_capabilities = Core.compare_capabilities(new_packages, head_packages)
477
+ diff = Core.add_capabilities_to_purl(diff)
478
+ return diff
479
+
480
+ @staticmethod
481
+ def add_capabilities_to_purl(diff: Diff) -> Diff:
482
+ new_packages = []
483
+ for purl in diff.new_packages:
484
+ purl: Purl
485
+ if purl.id in diff.new_capabilities:
486
+ capabilities = diff.new_capabilities[purl.id]
487
+ if len(capabilities) > 0:
488
+ purl.capabilities = capabilities
489
+ new_packages.append(purl)
490
+ else:
491
+ new_packages.append(purl)
492
+ diff.new_packages = new_packages
480
493
  return diff
481
494
 
495
+ @staticmethod
496
+ def compare_capabilities(new_packages: dict, head_packages: dict) -> dict:
497
+ capabilities = {}
498
+ for package_id in new_packages:
499
+ package: Package
500
+ head_package: Package
501
+ package = new_packages[package_id]
502
+ if package_id in head_packages:
503
+ head_package = head_packages[package_id]
504
+ for alert in package.alerts:
505
+ if alert not in head_package.alerts:
506
+ capabilities = Core.check_alert_capabilities(package, capabilities, package_id, head_package)
507
+ else:
508
+ capabilities = Core.check_alert_capabilities(package, capabilities, package_id)
509
+
510
+ return capabilities
511
+
512
+ @staticmethod
513
+ def check_alert_capabilities(
514
+ package: Package,
515
+ capabilities: dict,
516
+ package_id: str,
517
+ head_package: Package = None
518
+ ) -> dict:
519
+ alert_types = {
520
+ "envVars": "Environment",
521
+ "networkAccess": "Network",
522
+ "filesystemAccess": "File System",
523
+ "shellAccess": "Shell"
524
+ }
525
+
526
+ for alert in package.alerts:
527
+ new_alert = True
528
+ if head_package is not None and alert in head_package.alerts:
529
+ new_alert = False
530
+ if alert["type"] in alert_types and new_alert:
531
+ value = alert_types[alert["type"]]
532
+ if package_id not in capabilities:
533
+ capabilities[package_id] = [value]
534
+ else:
535
+ if value not in capabilities[package_id]:
536
+ capabilities[package_id].append(value)
537
+ return capabilities
538
+
482
539
  @staticmethod
483
540
  def compare_issue_alerts(new_scan_alerts: dict, head_scan_alerts: dict, alerts: list) -> list:
484
541
  """
@@ -528,12 +585,12 @@ class Core:
528
585
  """
529
586
  for item in package.alerts:
530
587
  alert = Alert(**item)
531
- props = issue_text.get(alert.type)
588
+ props = getattr(all_issues, alert.type)
532
589
  if props is not None:
533
- description = props.get("description")
534
- title = props.get("title")
535
- suggestion = props.get("suggestion")
536
- next_step_title = props.get("nextStepTitle")
590
+ description = props.description
591
+ title = props.title
592
+ suggestion = props.suggestion
593
+ next_step_title = props.nextStepTitle
537
594
  else:
538
595
  description = ""
539
596
  title = ""
@@ -611,7 +668,7 @@ class Core:
611
668
  ecosystem=package.type,
612
669
  direct=package.direct,
613
670
  introduced_by=introduced_by,
614
- author=package.author or '',
671
+ author=package.author or [],
615
672
  size=package.size,
616
673
  transitives=package.transitives
617
674
  )
@@ -631,6 +688,7 @@ class Core:
631
688
  if package.id in packages:
632
689
  print("Duplicate package?")
633
690
  else:
691
+ package = Core.get_license_details(package)
634
692
  packages[package.id] = package
635
693
  for top_id in package.topLevelAncestors:
636
694
  if top_id not in top_level_count:
@@ -642,3 +700,14 @@ class Core:
642
700
  packages[package_id].transitives = top_level_count[package_id]
643
701
  return packages
644
702
 
703
+ @staticmethod
704
+ def save_file(file_name: str, content: str) -> None:
705
+ file = open(file_name, "w")
706
+ file.write(content)
707
+ file.close()
708
+
709
+ # @staticmethod
710
+ # def create_license_file(diff: Diff) -> None:
711
+ # output = []
712
+ # for package_id in diff.packages:
713
+ # purl = Core.create_purl(package_id, diff.packages)
@@ -84,6 +84,8 @@ class Package:
84
84
  topLevelAncestors: list
85
85
  url: str
86
86
  transitives: int
87
+ license: str
88
+ license_text: str
87
89
 
88
90
  def __init__(self, **kwargs):
89
91
  if kwargs:
@@ -99,16 +101,16 @@ class Package:
99
101
  self.scores = Score(**self.score)
100
102
  if not hasattr(self, "alerts"):
101
103
  self.alerts = []
102
- if not hasattr(self, "author"):
103
- self.author = []
104
- if not hasattr(self, "size"):
105
- self.size = 0
106
104
  if not hasattr(self, "topLevelAncestors"):
107
105
  self.topLevelAncestors = []
108
106
  if not hasattr(self, "manifestFiles"):
109
107
  self.manifestFiles = []
110
108
  if not hasattr(self, "transitives"):
111
109
  self.transitives = 0
110
+ if not hasattr(self, "author"):
111
+ self.author = []
112
+ if not hasattr(self, "size"):
113
+ self.size = 0
112
114
  self.alert_counts = {
113
115
  "critical": 0,
114
116
  "high": 0,
@@ -116,6 +118,10 @@ class Package:
116
118
  "low": 0
117
119
  }
118
120
  self.error_alerts = []
121
+ if not hasattr(self, "license"):
122
+ self.license = "NoLicenseFound"
123
+ if not hasattr(self, "license_text"):
124
+ self.license_text = ""
119
125
 
120
126
  def __str__(self):
121
127
  return json.dumps(self.__dict__)
@@ -229,6 +235,7 @@ class FullScan:
229
235
  commit_hash: str
230
236
  pull_request: int
231
237
  sbom_artifacts: list
238
+ packages: dict
232
239
 
233
240
  def __init__(self, **kwargs):
234
241
  if kwargs:
@@ -283,10 +290,12 @@ class FullScanParams:
283
290
 
284
291
  class Diff:
285
292
  new_packages: list
293
+ new_capabilities: dict
286
294
  removed_packages: list
287
295
  new_alerts: list
288
296
  id: str
289
297
  sbom: str
298
+ packages: dict
290
299
 
291
300
  def __init__(self, **kwargs):
292
301
  if kwargs:
@@ -298,6 +307,8 @@ class Diff:
298
307
  self.removed_packages = []
299
308
  if not hasattr(self, "new_alerts"):
300
309
  self.new_alerts = []
310
+ if not hasattr(self, "new_capabilities"):
311
+ self.new_capabilities = {}
301
312
 
302
313
  def __str__(self):
303
314
  return json.dumps(self.__dict__)
@@ -313,6 +324,8 @@ class Purl:
313
324
  size: int
314
325
  transitives: int
315
326
  introduced_by: list
327
+ capabilities: dict
328
+ is_new: bool
316
329
 
317
330
  def __init__(self, **kwargs):
318
331
  if kwargs:
@@ -320,6 +333,10 @@ class Purl:
320
333
  setattr(self, key, value)
321
334
  if not hasattr(self, "introduced_by"):
322
335
  self.new_packages = []
336
+ if not hasattr(self, "capabilities"):
337
+ self.capabilities = {}
338
+ if not hasattr(self, "is_new"):
339
+ self.is_new = False
323
340
 
324
341
  def __str__(self):
325
342
  return json.dumps(self.__dict__)
@@ -349,3 +366,8 @@ class GithubComment:
349
366
 
350
367
  def __str__(self):
351
368
  return json.dumps(self.__dict__)
369
+
370
+ class LicenseOutput(Purl):
371
+ licenses: list
372
+ license
373
+
@@ -0,0 +1,327 @@
1
+ import json
2
+ import os
3
+ from core import log
4
+ import requests
5
+ from core.exceptions import *
6
+ from core.classes import GithubComment, Diff, Issue
7
+ import sys
8
+
9
+
10
+ global github_sha
11
+ global github_api_url
12
+ global github_ref_type
13
+ global github_event_name
14
+ global github_workspace
15
+ global github_repository
16
+ global github_ref_name
17
+ global github_actor
18
+ global default_branch
19
+ global github_env
20
+ global pr_number
21
+ global pr_name
22
+ global is_default_branch
23
+ global commit_message
24
+ global committer
25
+ global gh_api_token
26
+ global github_repository_owner
27
+
28
+ github_variables = [
29
+ "GITHUB_SHA",
30
+ "GITHUB_API_URL",
31
+ "GITHUB_REF_TYPE",
32
+ "GITHUB_EVENT_NAME",
33
+ "GITHUB_WORKSPACE",
34
+ "GITHUB_REPOSITORY",
35
+ "GITHUB_REF_NAME",
36
+ "DEFAULT_BRANCH",
37
+ "PR_NUMBER",
38
+ "PR_NAME",
39
+ "COMMIT_MESSAGE",
40
+ "GITHUB_ACTOR",
41
+ "GITHUB_ENV",
42
+ "GH_API_TOKEN",
43
+ "GITHUB_REPOSITORY_OWNER"
44
+ ]
45
+
46
+ for env in github_variables:
47
+ var_name = env.lower()
48
+ globals()[var_name] = os.getenv(env) or None
49
+ if var_name == "default_branch":
50
+ global is_default_branch
51
+ if default_branch is None or default_branch.lower() == "false":
52
+ is_default_branch = False
53
+ else:
54
+ is_default_branch = True
55
+
56
+
57
+ def do_request(
58
+ path: str,
59
+ headers: dict = None,
60
+ payload: [dict, str] = None,
61
+ files: list = None,
62
+ method: str = "GET",
63
+ ) -> dict:
64
+ """
65
+ do_requests is the shared function for making HTTP calls
66
+
67
+ :param path: Required path for the request
68
+ :param headers: Optional dictionary of headers. If not set will use a default set
69
+ :param payload: Optional dictionary or string of the payload to pass
70
+ :param files: Optional list of files to upload
71
+ :param method: Optional method to use, defaults to GET
72
+ :return:
73
+ """
74
+ if gh_api_token is None or gh_api_token == "":
75
+ raise APIKeyMissing
76
+
77
+ if headers is None:
78
+ headers = {
79
+ 'Authorization': f"Bearer {gh_api_token}",
80
+ 'User-Agent': 'SocketPythonScript/0.0.1',
81
+ "accept": "application/json"
82
+ }
83
+ url = f"{github_api_url}/{path}"
84
+ response = requests.request(
85
+ method.upper(),
86
+ url,
87
+ headers=headers,
88
+ data=payload,
89
+ files=files
90
+ )
91
+ if response.status_code <= 399:
92
+ try:
93
+ return response.json()
94
+ except Exception as error:
95
+ response = {
96
+ "error": error,
97
+ "response": response.text
98
+ }
99
+ return response
100
+ else:
101
+ msg = {
102
+ "status_code": response.status_code,
103
+ "UnexpectedError": "There was an unexpected error using the API",
104
+ "error": response.text
105
+ }
106
+ raise APIFailure(msg)
107
+
108
+
109
+ class Github:
110
+ commit_sha: str
111
+ api_url: str
112
+ ref_type: str
113
+ event_name: str
114
+ workspace: str
115
+ repository: str
116
+ ref_name: str
117
+ default_branch: str
118
+ is_default_branch: bool
119
+ pr_number: int
120
+ pr_name: str
121
+ commit_message: str
122
+ committer: str
123
+ github_env: str
124
+ api_token: str
125
+
126
+ def __init__(self):
127
+ self.commit_sha = github_sha
128
+ self.api_url = github_api_url
129
+ self.ref_type = github_ref_type
130
+ self.event_name = github_event_name
131
+ self.workspace = github_workspace
132
+ self.repository = github_repository
133
+ if "/" in self.repository:
134
+ self.repository = self.repository.rsplit("/")[1]
135
+ self.ref_name = github_ref_name
136
+ self.default_branch = default_branch
137
+ self.is_default_branch = is_default_branch
138
+ self.pr_number = pr_number
139
+ self.pr_name = pr_name
140
+ self.commit_message = commit_message
141
+ self.committer = github_actor
142
+ self.github_env = github_env
143
+ self.api_token = gh_api_token
144
+ if self.api_token is None:
145
+ print("Unable to get Github API Token from GH_API_TOKEN")
146
+ sys.exit(2)
147
+
148
+ @staticmethod
149
+ def check_event_type() -> str:
150
+ if github_event_name.lower() == "push":
151
+ if pr_number is None or pr_number == "":
152
+ event_type = "main"
153
+ else:
154
+ event_type = "diff"
155
+ elif github_event_name.lower() == "issue_comment":
156
+ event_type = "comment"
157
+ else:
158
+ event_type = None
159
+ log.error(f"Unknown event type {github_event_name}")
160
+ sys.exit(1)
161
+ return event_type
162
+
163
+ @staticmethod
164
+ def add_socket_comments(security_comment: str, overview_comment: str, comments: dict) -> None:
165
+ existing_overview_comment = comments.get("overview")
166
+ existing_security_comment = comments.get("security")
167
+ if existing_overview_comment is not None:
168
+ existing_overview_comment: GithubComment
169
+ Github.update_comment(overview_comment, str(existing_overview_comment.id))
170
+ else:
171
+ Github.post_comment(overview_comment)
172
+ if existing_security_comment is not None:
173
+ existing_security_comment: GithubComment
174
+ Github.update_comment(security_comment, str(existing_security_comment.id))
175
+ else:
176
+ Github.post_comment(security_comment)
177
+
178
+ @staticmethod
179
+ def post_comment(body: str) -> None:
180
+ repo = github_repository.rsplit("/", 1)[1]
181
+ path = f"repos/{github_repository_owner}/{repo}/issues/{pr_number}/comments"
182
+ payload = {
183
+ "body": body
184
+ }
185
+ payload = json.dumps(payload)
186
+ do_request(path, payload=payload, method="POST")
187
+
188
+ @staticmethod
189
+ def update_comment(body: str, comment_id: str) -> None:
190
+ repo = github_repository.rsplit("/", 1)[1]
191
+ path = f"repos/{github_repository_owner}/{repo}/issues/comments/{comment_id}"
192
+ payload = {
193
+ "body": body
194
+ }
195
+ payload = json.dumps(payload)
196
+ do_request(path, payload=payload, method="PATCH")
197
+
198
+ @staticmethod
199
+ def write_new_env(name: str, content: str) -> None:
200
+ file = open(github_env, "a")
201
+ new_content = content.replace("\n", "\\n")
202
+ env_output = f"{name}={new_content}"
203
+ file.write(env_output)
204
+
205
+ @staticmethod
206
+ def get_comments_for_pr(repo: str, pr: str) -> dict:
207
+ path = f"repos/{github_repository_owner}/{repo}/issues/{pr}/comments"
208
+ raw_comments = do_request(path)
209
+ comments = {}
210
+ if "error" not in raw_comments:
211
+ for item in raw_comments:
212
+ comment = GithubComment(**item)
213
+ comments[comment.id] = comment
214
+ for line in comment.body.split("\n"):
215
+ comment.body_list.append(line)
216
+ else:
217
+ log.error(raw_comments)
218
+ socket_comments = Github.check_for_socket_comments(comments)
219
+ return socket_comments
220
+
221
+ @staticmethod
222
+ def check_for_socket_comments(comments: dict):
223
+ socket_comments = {}
224
+ for comment_id in comments:
225
+ comment = comments[comment_id]
226
+ comment: GithubComment
227
+ if "socket-security-comment-actions" in comment.body:
228
+ socket_comments["security"] = comment
229
+ elif "socket-overview-comment-actions" in comment.body:
230
+ socket_comments["overview"] = comment
231
+ elif "SocketSecurity ignore" in comment.body:
232
+ if "ignore" not in socket_comments:
233
+ socket_comments["ignore"] = []
234
+ socket_comments["ignore"].append(comment)
235
+ return socket_comments
236
+
237
+ @staticmethod
238
+ def remove_alerts(comments: dict, new_alerts: list) -> list:
239
+ alerts = []
240
+ if "ignore" not in comments:
241
+ return new_alerts
242
+ ignore_all, ignore_commands = Github.get_ignore_options(comments)
243
+ for alert in new_alerts:
244
+ alert: Issue
245
+ if ignore_all:
246
+ break
247
+ else:
248
+ purl = f"{alert.pkg_name}, {alert.pkg_version}"
249
+ purl_star = f"{alert.pkg_name}, *"
250
+ if purl in ignore_commands or purl_star in ignore_commands:
251
+ log.debug(f"Alerts for {alert.pkg_name}@{alert.pkg_version} ignored")
252
+ else:
253
+ log.debug(f"Adding alert {alert.type} for {alert.pkg_name}@{alert.pkg_version}")
254
+ alerts.append(alert)
255
+ return alerts
256
+
257
+ @staticmethod
258
+ def get_ignore_options(comments: dict) -> [bool, list]:
259
+ ignore_commands = []
260
+ ignore_all = False
261
+
262
+ for comment in comments["ignore"]:
263
+ comment: GithubComment
264
+ first_line = comment.body_list[0]
265
+ if not ignore_all and "SocketSecurity ignore" in first_line:
266
+ first_line = first_line.lstrip("@")
267
+ _, command = first_line.split("SocketSecurity ")
268
+ command = command.strip()
269
+ if command == "ignore-all":
270
+ ignore_all = True
271
+ else:
272
+ command = command.lstrip("ignore").strip()
273
+ name, version = command.split("@")
274
+ data = f"{name}, {version}"
275
+ ignore_commands.append(data)
276
+ return ignore_all, ignore_commands
277
+
278
+ @staticmethod
279
+ def is_ignore(pkg_name: str, pkg_version: str, name: str, version: str) -> bool:
280
+ result = False
281
+ if pkg_name == name and (pkg_version == version or version == "*"):
282
+ result = True
283
+ return result
284
+
285
+ @staticmethod
286
+ def remove_comment_alerts(comments: dict):
287
+ security_alert = comments.get("security")
288
+ if security_alert is not None:
289
+ security_alert: GithubComment
290
+ new_body = Github.process_security_comment(security_alert, comments)
291
+ Github.update_comment(new_body, str(security_alert.id))
292
+
293
+ @staticmethod
294
+ def is_heading_line(line) -> bool:
295
+ is_heading_line = True
296
+ if line != "|Alert|Package|Introduced by|Manifest File|" and ":---" not in line:
297
+ is_heading_line = False
298
+ return is_heading_line
299
+
300
+ @staticmethod
301
+ def process_security_comment(comment: GithubComment, comments) -> str:
302
+ lines = []
303
+ start = False
304
+ ignore_all, ignore_commands = Github.get_ignore_options(comments)
305
+ for line in comment.body_list:
306
+ line = line.strip()
307
+ if "start-socket-alerts-table" in line:
308
+ start = True
309
+ elif start and "end-socket-alerts-table" not in line and not Github.is_heading_line(line) and line != '':
310
+ title, package, introduced_by, manifest = line.lstrip("|").rstrip("|").split("|")
311
+ details, _ = package.split("](")
312
+ ecosystem, details = details.split("/", 1)
313
+ pkg_name, pkg_version = details.split("@")
314
+ ignore = False
315
+ for name, version in ignore_commands:
316
+ if ignore_all or Github.is_ignore(pkg_name, pkg_version, name, version):
317
+ ignore = True
318
+ if not ignore:
319
+ lines.append(line)
320
+ elif "end-socket-alerts-table" in line:
321
+ start = False
322
+ lines.append(line)
323
+ else:
324
+ lines.append(line)
325
+ new_body = "\n".join(lines)
326
+ return new_body
327
+