socketsecurity 1.0.43__tar.gz → 1.0.47__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.
- {socketsecurity-1.0.43/socketsecurity.egg-info → socketsecurity-1.0.47}/PKG-INFO +1 -1
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/__init__.py +1 -1
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/core/messages.py +179 -41
- {socketsecurity-1.0.43 → socketsecurity-1.0.47/socketsecurity.egg-info}/PKG-INFO +1 -1
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/LICENSE +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/README.md +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/pyproject.toml +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/setup.cfg +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/core/__init__.py +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/core/github.py +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/core/gitlab.py +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/core/issues.py +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/core/licenses.py +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity/socketcli.py +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity.egg-info/SOURCES.txt +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity.egg-info/dependency_links.txt +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity.egg-info/entry_points.txt +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity.egg-info/requires.txt +0 -0
- {socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity.egg-info/top_level.txt +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__author__ = 'socket.dev'
|
|
2
|
-
__version__ = '1.0.
|
|
2
|
+
__version__ = '1.0.47'
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
+
import re
|
|
4
|
+
import json
|
|
3
5
|
|
|
6
|
+
from pathlib import Path
|
|
4
7
|
from mdutils import MdUtils
|
|
5
8
|
from socketsecurity.core.classes import Diff, Purl, Issue
|
|
6
9
|
from prettytable import PrettyTable
|
|
@@ -12,6 +15,10 @@ class Messages:
|
|
|
12
15
|
def map_severity_to_sarif(severity: str) -> str:
|
|
13
16
|
"""
|
|
14
17
|
Map Socket severity levels to SARIF levels (GitHub code scanning).
|
|
18
|
+
|
|
19
|
+
'low' -> 'note'
|
|
20
|
+
'medium' or 'middle' -> 'warning'
|
|
21
|
+
'high' or 'critical' -> 'error'
|
|
15
22
|
"""
|
|
16
23
|
severity_mapping = {
|
|
17
24
|
"low": "note",
|
|
@@ -22,39 +29,168 @@ class Messages:
|
|
|
22
29
|
}
|
|
23
30
|
return severity_mapping.get(severity.lower(), "note")
|
|
24
31
|
|
|
25
|
-
|
|
26
32
|
@staticmethod
|
|
27
|
-
def find_line_in_file(
|
|
33
|
+
def find_line_in_file(packagename: str, packageversion: str, manifest_file: str) -> tuple:
|
|
28
34
|
"""
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
Finds the line number and snippet of code for the given package/version in a manifest file.
|
|
36
|
+
Returns a 2-tuple: (line_number, snippet_or_message).
|
|
37
|
+
|
|
38
|
+
Supports:
|
|
39
|
+
1) JSON-based manifest files (package-lock.json, Pipfile.lock, composer.lock)
|
|
40
|
+
- Locates a dictionary entry with the matching package & version
|
|
41
|
+
- Does a rough line-based search to find the actual line in the raw text
|
|
42
|
+
2) Text-based (requirements.txt, package.json, yarn.lock, etc.)
|
|
43
|
+
- Uses compiled regex patterns to detect a match line by line
|
|
31
44
|
"""
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
# Extract just the file name to detect manifest type
|
|
46
|
+
file_type = Path(manifest_file).name
|
|
47
|
+
|
|
48
|
+
# ----------------------------------------------------
|
|
49
|
+
# 1) JSON-based manifest files
|
|
50
|
+
# ----------------------------------------------------
|
|
51
|
+
if file_type in ["package-lock.json", "Pipfile.lock", "composer.lock"]:
|
|
52
|
+
try:
|
|
53
|
+
# Read entire file so we can parse JSON and also do raw line checks
|
|
54
|
+
with open(manifest_file, "r", encoding="utf-8") as f:
|
|
55
|
+
raw_text = f.read()
|
|
56
|
+
|
|
57
|
+
# Attempt JSON parse
|
|
58
|
+
data = json.loads(raw_text)
|
|
59
|
+
|
|
60
|
+
# In practice, you may need to check data["dependencies"], data["default"], etc.
|
|
61
|
+
# This is an example approach.
|
|
62
|
+
packages_dict = (
|
|
63
|
+
data.get("packages")
|
|
64
|
+
or data.get("default")
|
|
65
|
+
or data.get("dependencies")
|
|
66
|
+
or {}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
found_key = None
|
|
70
|
+
found_info = None
|
|
71
|
+
# Locate a dictionary entry whose 'version' matches
|
|
72
|
+
for key, value in packages_dict.items():
|
|
73
|
+
# For NPM package-lock, keys might look like "node_modules/axios"
|
|
74
|
+
if key.endswith(packagename) and "version" in value:
|
|
75
|
+
if value["version"] == packageversion:
|
|
76
|
+
found_key = key
|
|
77
|
+
found_info = value
|
|
78
|
+
break
|
|
79
|
+
|
|
80
|
+
if found_key and found_info:
|
|
81
|
+
# Search lines to approximate the correct line number
|
|
82
|
+
needle_key = f'"{found_key}":' # e.g. "node_modules/axios":
|
|
83
|
+
needle_version = f'"version": "{packageversion}"'
|
|
84
|
+
lines = raw_text.splitlines()
|
|
85
|
+
best_line = 1
|
|
86
|
+
snippet = None
|
|
87
|
+
|
|
88
|
+
for i, line in enumerate(lines, start=1):
|
|
89
|
+
if (needle_key in line) or (needle_version in line):
|
|
90
|
+
best_line = i
|
|
91
|
+
snippet = line.strip()
|
|
92
|
+
break # On first match, stop
|
|
93
|
+
|
|
94
|
+
# If we found an approximate line, return it; else fallback to line 1
|
|
95
|
+
if best_line > 0 and snippet:
|
|
96
|
+
return best_line, snippet
|
|
97
|
+
else:
|
|
98
|
+
return 1, f'"{found_key}": {found_info}'
|
|
99
|
+
else:
|
|
100
|
+
return 1, f"{packagename} {packageversion} (not found in {manifest_file})"
|
|
101
|
+
|
|
102
|
+
except (FileNotFoundError, json.JSONDecodeError):
|
|
103
|
+
return 1, f"Error reading {manifest_file}"
|
|
104
|
+
|
|
105
|
+
# ----------------------------------------------------
|
|
106
|
+
# 2) Text-based / line-based manifests
|
|
107
|
+
# ----------------------------------------------------
|
|
108
|
+
# Define a dictionary of patterns for common manifest types
|
|
109
|
+
search_patterns = {
|
|
110
|
+
"package.json": rf'"{packagename}":\s*"{packageversion}"',
|
|
111
|
+
"yarn.lock": rf'{packagename}@{packageversion}',
|
|
112
|
+
"pnpm-lock.yaml": rf'"{re.escape(packagename)}"\s*:\s*\{{[^}}]*"version":\s*"{re.escape(packageversion)}"',
|
|
113
|
+
"requirements.txt": rf'^{re.escape(packagename)}\s*(?:==|===|!=|>=|<=|~=|\s+)?\s*{re.escape(packageversion)}(?:\s*;.*)?$',
|
|
114
|
+
"pyproject.toml": rf'{packagename}\s*=\s*"{packageversion}"',
|
|
115
|
+
"Pipfile": rf'"{packagename}"\s*=\s*"{packageversion}"',
|
|
116
|
+
"go.mod": rf'require\s+{re.escape(packagename)}\s+{re.escape(packageversion)}',
|
|
117
|
+
"go.sum": rf'{re.escape(packagename)}\s+{re.escape(packageversion)}',
|
|
118
|
+
"pom.xml": rf'<artifactId>{re.escape(packagename)}</artifactId>\s*<version>{re.escape(packageversion)}</version>',
|
|
119
|
+
"build.gradle": rf'implementation\s+"{re.escape(packagename)}:{re.escape(packageversion)}"',
|
|
120
|
+
"Gemfile": rf'gem\s+"{re.escape(packagename)}",\s*"{re.escape(packageversion)}"',
|
|
121
|
+
"Gemfile.lock": rf'\s+{re.escape(packagename)}\s+\({re.escape(packageversion)}\)',
|
|
122
|
+
".csproj": rf'<PackageReference\s+Include="{re.escape(packagename)}"\s+Version="{re.escape(packageversion)}"\s*/>',
|
|
123
|
+
".fsproj": rf'<PackageReference\s+Include="{re.escape(packagename)}"\s+Version="{re.escape(packageversion)}"\s*/>',
|
|
124
|
+
"paket.dependencies": rf'nuget\s+{re.escape(packagename)}\s+{re.escape(packageversion)}',
|
|
125
|
+
"Cargo.toml": rf'{re.escape(packagename)}\s*=\s*"{re.escape(packageversion)}"',
|
|
126
|
+
"build.sbt": rf'"{re.escape(packagename)}"\s*%\s*"{re.escape(packageversion)}"',
|
|
127
|
+
"Podfile": rf'pod\s+"{re.escape(packagename)}",\s*"{re.escape(packageversion)}"',
|
|
128
|
+
"Package.swift": rf'\.package\(name:\s*"{re.escape(packagename)}",\s*url:\s*".*?",\s*version:\s*"{re.escape(packageversion)}"\)',
|
|
129
|
+
"mix.exs": rf'\{{:{re.escape(packagename)},\s*"{re.escape(packageversion)}"\}}',
|
|
130
|
+
"composer.json": rf'"{re.escape(packagename)}":\s*"{re.escape(packageversion)}"',
|
|
131
|
+
"conanfile.txt": rf'{re.escape(packagename)}/{re.escape(packageversion)}',
|
|
132
|
+
"vcpkg.json": rf'"{re.escape(packagename)}":\s*"{re.escape(packageversion)}"',
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# If no specific pattern is found for this file name, fallback to a naive approach
|
|
136
|
+
searchstring = search_patterns.get(file_type, rf'{re.escape(packagename)}.*{re.escape(packageversion)}')
|
|
137
|
+
|
|
34
138
|
try:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
139
|
+
# Read file lines and search for a match
|
|
140
|
+
with open(manifest_file, 'r', encoding="utf-8") as file:
|
|
141
|
+
lines = [line.rstrip("\n") for line in file]
|
|
142
|
+
for line_number, line_content in enumerate(lines, start=1):
|
|
143
|
+
# For Python conditional dependencies, ignore everything after first ';'
|
|
144
|
+
line_main = line_content.split(";", 1)[0].strip()
|
|
145
|
+
|
|
146
|
+
# Use a case-insensitive regex search
|
|
147
|
+
if re.search(searchstring, line_main, re.IGNORECASE):
|
|
148
|
+
return line_number, line_content.strip()
|
|
149
|
+
|
|
150
|
+
except FileNotFoundError:
|
|
151
|
+
return 1, f"{manifest_file} not found"
|
|
40
152
|
except Exception as e:
|
|
41
|
-
return 1, f"
|
|
42
|
-
|
|
43
|
-
|
|
153
|
+
return 1, f"Error reading {manifest_file}: {e}"
|
|
154
|
+
|
|
155
|
+
return 1, f"{packagename} {packageversion} (not found)"
|
|
156
|
+
|
|
44
157
|
@staticmethod
|
|
45
|
-
def
|
|
158
|
+
def get_manifest_type_url(manifest_file: str, pkg_name: str, pkg_version: str) -> str:
|
|
46
159
|
"""
|
|
47
|
-
|
|
160
|
+
Determine the correct URL path based on the manifest file type.
|
|
48
161
|
"""
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
162
|
+
manifest_to_url_prefix = {
|
|
163
|
+
"package.json": "npm",
|
|
164
|
+
"package-lock.json": "npm",
|
|
165
|
+
"yarn.lock": "npm",
|
|
166
|
+
"pnpm-lock.yaml": "npm",
|
|
167
|
+
"requirements.txt": "pypi",
|
|
168
|
+
"pyproject.toml": "pypi",
|
|
169
|
+
"Pipfile": "pypi",
|
|
170
|
+
"go.mod": "go",
|
|
171
|
+
"go.sum": "go",
|
|
172
|
+
"pom.xml": "maven",
|
|
173
|
+
"build.gradle": "maven",
|
|
174
|
+
".csproj": "nuget",
|
|
175
|
+
".fsproj": "nuget",
|
|
176
|
+
"paket.dependencies": "nuget",
|
|
177
|
+
"Cargo.toml": "cargo",
|
|
178
|
+
"Gemfile": "rubygems",
|
|
179
|
+
"Gemfile.lock": "rubygems",
|
|
180
|
+
"composer.json": "composer",
|
|
181
|
+
"vcpkg.json": "vcpkg",
|
|
182
|
+
}
|
|
56
183
|
|
|
57
|
-
|
|
184
|
+
file_type = Path(manifest_file).name
|
|
185
|
+
url_prefix = manifest_to_url_prefix.get(file_type, "unknown")
|
|
186
|
+
return f"https://socket.dev/{url_prefix}/package/{pkg_name}/alerts/{pkg_version}"
|
|
187
|
+
|
|
188
|
+
@staticmethod
|
|
189
|
+
def create_security_comment_sarif(diff) -> dict:
|
|
190
|
+
"""
|
|
191
|
+
Create SARIF-compliant output from the diff report, including dynamic URL generation
|
|
192
|
+
based on manifest type and improved <br/> formatting for GitHub SARIF display.
|
|
193
|
+
"""
|
|
58
194
|
sarif_data = {
|
|
59
195
|
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
|
|
60
196
|
"version": "2.1.0",
|
|
@@ -76,38 +212,39 @@ class Messages:
|
|
|
76
212
|
results_list = []
|
|
77
213
|
|
|
78
214
|
for alert in diff.new_alerts:
|
|
79
|
-
alert: Issue
|
|
80
215
|
pkg_name = alert.pkg_name
|
|
81
216
|
pkg_version = alert.pkg_version
|
|
82
217
|
rule_id = f"{pkg_name}=={pkg_version}"
|
|
83
218
|
severity = alert.severity
|
|
84
219
|
|
|
85
|
-
#
|
|
86
|
-
title = f"Alert generated for {pkg_name}=={pkg_version} by Socket Security"
|
|
87
|
-
full_desc = f"{alert.title} - {alert.description}"
|
|
88
|
-
short_desc = f"{alert.props.get('note', '')}\r\n\r\nSuggested Action:\r\n{alert.suggestion}"
|
|
89
|
-
|
|
90
|
-
# Find the manifest file and line details
|
|
220
|
+
# Generate the correct URL for the alert based on manifest type
|
|
91
221
|
introduced_list = alert.introduced_by
|
|
92
|
-
if introduced_list and isinstance(introduced_list[0], list)
|
|
93
|
-
|
|
94
|
-
else:
|
|
95
|
-
manifest_file = alert.manifests or "requirements.txt"
|
|
222
|
+
manifest_file = introduced_list[0][1] if introduced_list and isinstance(introduced_list[0], list) else alert.manifests or "requirements.txt"
|
|
223
|
+
socket_url = Messages.get_manifest_type_url(manifest_file, pkg_name, pkg_version)
|
|
96
224
|
|
|
97
|
-
|
|
225
|
+
# Prepare descriptions with <br/> replacements
|
|
226
|
+
short_desc = f"{alert.props.get('note', '')}<br/><br/>Suggested Action:<br/>{alert.suggestion}<br/><a href=\"{socket_url}\">{socket_url}</a>"
|
|
227
|
+
full_desc = f"{alert.title} - {alert.description.replace('\r\n', '<br/>')}"
|
|
98
228
|
|
|
99
|
-
#
|
|
229
|
+
# Identify the line and snippet in the manifest file
|
|
230
|
+
line_number, line_content = Messages.find_line_in_file(pkg_name, pkg_version, manifest_file)
|
|
231
|
+
if line_number < 1:
|
|
232
|
+
line_number = 1 # Ensure SARIF compliance
|
|
233
|
+
|
|
234
|
+
# Create the rule if not already defined
|
|
100
235
|
if rule_id not in rules_map:
|
|
101
236
|
rules_map[rule_id] = {
|
|
102
237
|
"id": rule_id,
|
|
103
238
|
"name": f"{pkg_name}=={pkg_version}",
|
|
104
|
-
"shortDescription": {"text":
|
|
239
|
+
"shortDescription": {"text": f"Alert generated for {rule_id} by Socket Security"},
|
|
105
240
|
"fullDescription": {"text": full_desc},
|
|
106
|
-
"helpUri":
|
|
107
|
-
"defaultConfiguration": {
|
|
241
|
+
"helpUri": socket_url,
|
|
242
|
+
"defaultConfiguration": {
|
|
243
|
+
"level": Messages.map_severity_to_sarif(severity)
|
|
244
|
+
},
|
|
108
245
|
}
|
|
109
246
|
|
|
110
|
-
# Add the result
|
|
247
|
+
# Add the SARIF result
|
|
111
248
|
result_obj = {
|
|
112
249
|
"ruleId": rule_id,
|
|
113
250
|
"message": {"text": short_desc},
|
|
@@ -125,6 +262,7 @@ class Messages:
|
|
|
125
262
|
}
|
|
126
263
|
results_list.append(result_obj)
|
|
127
264
|
|
|
265
|
+
# Attach rules and results
|
|
128
266
|
sarif_data["runs"][0]["tool"]["driver"]["rules"] = list(rules_map.values())
|
|
129
267
|
sarif_data["runs"][0]["results"] = results_list
|
|
130
268
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{socketsecurity-1.0.43 → socketsecurity-1.0.47}/socketsecurity.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|