socketsecurity 2.4.2__py3-none-any.whl → 2.4.4__py3-none-any.whl
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/__init__.py +1 -1
- socketsecurity/config.py +67 -1
- socketsecurity/core/__init__.py +81 -2
- socketsecurity/socketcli.py +9 -1
- {socketsecurity-2.4.2.dist-info → socketsecurity-2.4.4.dist-info}/METADATA +2 -2
- {socketsecurity-2.4.2.dist-info → socketsecurity-2.4.4.dist-info}/RECORD +9 -9
- {socketsecurity-2.4.2.dist-info → socketsecurity-2.4.4.dist-info}/WHEEL +0 -0
- {socketsecurity-2.4.2.dist-info → socketsecurity-2.4.4.dist-info}/entry_points.txt +0 -0
- {socketsecurity-2.4.2.dist-info → socketsecurity-2.4.4.dist-info}/licenses/LICENSE +0 -0
socketsecurity/__init__.py
CHANGED
socketsecurity/config.py
CHANGED
|
@@ -55,6 +55,50 @@ def load_cli_config_file(config_path: str) -> dict:
|
|
|
55
55
|
return scoped
|
|
56
56
|
return data
|
|
57
57
|
|
|
58
|
+
def normalize_exclude_paths(value) -> Optional[List[str]]:
|
|
59
|
+
"""Normalize a --exclude-paths value into a clean list of patterns.
|
|
60
|
+
|
|
61
|
+
Accepts a comma-separated string (CLI) or a list/tuple (e.g. a JSON/TOML --config file
|
|
62
|
+
value), so config-file-supplied patterns flow through the same validation as CLI ones.
|
|
63
|
+
"""
|
|
64
|
+
if not value:
|
|
65
|
+
return None
|
|
66
|
+
if isinstance(value, str):
|
|
67
|
+
items = value.split(",")
|
|
68
|
+
elif isinstance(value, (list, tuple)):
|
|
69
|
+
items = value
|
|
70
|
+
else:
|
|
71
|
+
return None
|
|
72
|
+
cleaned = [str(p).strip() for p in items if str(p).strip()]
|
|
73
|
+
return cleaned or None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def validate_exclude_paths(patterns: List[str]) -> None:
|
|
77
|
+
"""Validate --exclude-paths patterns (mirrors Node's assertValidExcludePaths).
|
|
78
|
+
|
|
79
|
+
Patterns are scan-root-relative globs. Reject the cases coana's --exclude-dirs / fast-glob
|
|
80
|
+
cannot honor: negation, absolute paths, ``..`` traversal, and degenerate match-everything.
|
|
81
|
+
Exits with code 1 on the first invalid pattern.
|
|
82
|
+
"""
|
|
83
|
+
# Degenerate match-everything forms, compared against the trailing-slash-stripped pattern
|
|
84
|
+
# (so "**/" reduces to "**" and is rejected, matching Node's stripTrailingSlash + check).
|
|
85
|
+
degenerate = {"", ".", "**", "./**", "/**"}
|
|
86
|
+
for p in patterns:
|
|
87
|
+
norm = (p or "").strip().replace("\\", "/")
|
|
88
|
+
if norm.startswith("!"):
|
|
89
|
+
logging.error(f"--exclude-paths: negation patterns are not supported: {p!r}")
|
|
90
|
+
exit(1)
|
|
91
|
+
if norm.startswith("/"):
|
|
92
|
+
logging.error(f"--exclude-paths: patterns must be scan-root relative (no leading '/'): {p!r}")
|
|
93
|
+
exit(1)
|
|
94
|
+
if norm == ".." or norm.startswith("../") or "/../" in norm or norm.endswith("/.."):
|
|
95
|
+
logging.error(f"--exclude-paths: '..' path traversal is not allowed: {p!r}")
|
|
96
|
+
exit(1)
|
|
97
|
+
if norm.rstrip("/") in degenerate:
|
|
98
|
+
logging.error(f"--exclude-paths: pattern would exclude everything: {p!r}")
|
|
99
|
+
exit(1)
|
|
100
|
+
|
|
101
|
+
|
|
58
102
|
@dataclass
|
|
59
103
|
class PluginConfig:
|
|
60
104
|
enabled: bool = False
|
|
@@ -106,6 +150,7 @@ class CliConfig:
|
|
|
106
150
|
include_module_folders: bool = False
|
|
107
151
|
repo_is_public: bool = False
|
|
108
152
|
excluded_ecosystems: list[str] = field(default_factory=lambda: [])
|
|
153
|
+
exclude_paths: Optional[List[str]] = None
|
|
109
154
|
version: str = __version__
|
|
110
155
|
jira_plugin: PluginConfig = field(default_factory=PluginConfig)
|
|
111
156
|
slack_plugin: PluginConfig = field(default_factory=PluginConfig)
|
|
@@ -167,6 +212,12 @@ class CliConfig:
|
|
|
167
212
|
|
|
168
213
|
args = parser.parse_args(args_list)
|
|
169
214
|
|
|
215
|
+
if args.reach_exclude_paths:
|
|
216
|
+
logging.warning(
|
|
217
|
+
"--reach-exclude-paths is deprecated; use --exclude-paths instead. "
|
|
218
|
+
"It is still honored and unioned with --exclude-paths."
|
|
219
|
+
)
|
|
220
|
+
|
|
170
221
|
# Get API token from env or args (check multiple env var names)
|
|
171
222
|
api_token = (
|
|
172
223
|
os.getenv("SOCKET_SECURITY_API_KEY") or
|
|
@@ -258,6 +309,7 @@ class CliConfig:
|
|
|
258
309
|
'reach_lazy_mode': args.reach_lazy_mode,
|
|
259
310
|
'reach_ecosystems': args.reach_ecosystems.split(',') if args.reach_ecosystems else None,
|
|
260
311
|
'reach_exclude_paths': args.reach_exclude_paths.split(',') if args.reach_exclude_paths else None,
|
|
312
|
+
'exclude_paths': normalize_exclude_paths(args.exclude_paths),
|
|
261
313
|
'reach_skip_cache': args.reach_skip_cache,
|
|
262
314
|
'reach_min_severity': args.reach_min_severity,
|
|
263
315
|
'reach_output_file': args.reach_output_file,
|
|
@@ -361,6 +413,10 @@ class CliConfig:
|
|
|
361
413
|
logging.error("--sarif-reachability potentially/reachable-or-potentially requires --sarif-scope full")
|
|
362
414
|
exit(1)
|
|
363
415
|
|
|
416
|
+
# Validate --exclude-paths patterns up front (mirrors Node's assertValidExcludePaths).
|
|
417
|
+
if config_args.get("exclude_paths"):
|
|
418
|
+
validate_exclude_paths(config_args["exclude_paths"])
|
|
419
|
+
|
|
364
420
|
# Validate that only_facts_file requires reach
|
|
365
421
|
if args.only_facts_file and not args.reach:
|
|
366
422
|
logging.error("--only-facts-file requires --reach to be specified")
|
|
@@ -570,6 +626,15 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
570
626
|
help="List of ecosystems to exclude from analysis (JSON array string)"
|
|
571
627
|
)
|
|
572
628
|
|
|
629
|
+
path_group.add_argument(
|
|
630
|
+
"--exclude-paths",
|
|
631
|
+
dest="exclude_paths",
|
|
632
|
+
metavar="<list>",
|
|
633
|
+
help="Comma-separated paths/globs to exclude from BOTH manifest discovery and "
|
|
634
|
+
"reachability analysis (e.g. 'tests/**,packages/legacy,*.spec.ts'). "
|
|
635
|
+
"Supersedes --reach-exclude-paths."
|
|
636
|
+
)
|
|
637
|
+
|
|
573
638
|
# Branch and Scan Configuration
|
|
574
639
|
config_group = parser.add_argument_group('Branch and Scan Configuration')
|
|
575
640
|
config_group.add_argument(
|
|
@@ -919,7 +984,8 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
919
984
|
"--reach-exclude-paths",
|
|
920
985
|
dest="reach_exclude_paths",
|
|
921
986
|
metavar="<list>",
|
|
922
|
-
help="Paths to exclude from reachability analysis
|
|
987
|
+
help="[DEPRECATED: use --exclude-paths] Paths to exclude from reachability analysis "
|
|
988
|
+
"(comma-separated). Still honored and unioned with --exclude-paths."
|
|
923
989
|
)
|
|
924
990
|
reachability_group.add_argument(
|
|
925
991
|
"--reach-min-severity",
|
socketsecurity/core/__init__.py
CHANGED
|
@@ -213,6 +213,67 @@ class Core:
|
|
|
213
213
|
return True
|
|
214
214
|
return False
|
|
215
215
|
|
|
216
|
+
@staticmethod
|
|
217
|
+
def _exclude_glob_to_regex(pattern: str) -> str:
|
|
218
|
+
"""Translate a micromatch-style glob into an anchored regex string.
|
|
219
|
+
|
|
220
|
+
Mirrors the Node CLI's --exclude-paths matcher (src/commands/scan/exclude-paths.mts):
|
|
221
|
+
patterns are matched against scan-root-relative POSIX paths, case-sensitively, where
|
|
222
|
+
``*`` does NOT cross ``/`` and ``**`` DOES. Patterns are anchored at the scan root, so
|
|
223
|
+
``tests`` matches ``tests`` (not ``src/tests``); use ``**/tests`` to match at any depth.
|
|
224
|
+
"""
|
|
225
|
+
i, n = 0, len(pattern)
|
|
226
|
+
out = ["^"]
|
|
227
|
+
while i < n:
|
|
228
|
+
c = pattern[i]
|
|
229
|
+
if c == "*":
|
|
230
|
+
if i + 1 < n and pattern[i + 1] == "*":
|
|
231
|
+
if i + 2 < n and pattern[i + 2] == "/":
|
|
232
|
+
out.append("(?:[^/]+/)*") # '**/' -> zero or more path segments
|
|
233
|
+
i += 3
|
|
234
|
+
else:
|
|
235
|
+
out.append(".*") # '**' at end / before non-slash -> any, incl '/'
|
|
236
|
+
i += 2
|
|
237
|
+
else:
|
|
238
|
+
out.append("[^/]*") # '*' -> within a single path segment
|
|
239
|
+
i += 1
|
|
240
|
+
elif c == "?":
|
|
241
|
+
out.append("[^/]")
|
|
242
|
+
i += 1
|
|
243
|
+
else:
|
|
244
|
+
out.append(re.escape(c))
|
|
245
|
+
i += 1
|
|
246
|
+
out.append("$")
|
|
247
|
+
return "".join(out)
|
|
248
|
+
|
|
249
|
+
@staticmethod
|
|
250
|
+
def compile_exclude_paths(patterns: Optional[List[str]]) -> List["re.Pattern"]:
|
|
251
|
+
"""Compile --exclude-paths globs into anchored regexes (compiled once per scan).
|
|
252
|
+
|
|
253
|
+
Each pattern ``P`` is expanded the way Node feeds fast-glob's ``ignore``: ``P`` (a file-
|
|
254
|
+
or dir-shaped exact match) plus ``P/**`` (its subtree), unless ``P`` already ends with
|
|
255
|
+
``/**``. Validation of the patterns happens earlier, in CliConfig.from_args.
|
|
256
|
+
"""
|
|
257
|
+
compiled: List["re.Pattern"] = []
|
|
258
|
+
for raw in patterns or []:
|
|
259
|
+
p = (raw or "").strip().replace("\\", "/").rstrip("/")
|
|
260
|
+
if not p:
|
|
261
|
+
continue
|
|
262
|
+
globs = [p] if p.endswith("/**") else [p, f"{p}/**"]
|
|
263
|
+
compiled.extend(re.compile(Core._exclude_glob_to_regex(g)) for g in globs)
|
|
264
|
+
return compiled
|
|
265
|
+
|
|
266
|
+
@staticmethod
|
|
267
|
+
def path_matches_exclude_regexes(rel_path: str, regexes: List["re.Pattern"]) -> bool:
|
|
268
|
+
rp = rel_path.replace(os.sep, "/").replace("\\", "/")
|
|
269
|
+
return any(r.match(rp) for r in regexes)
|
|
270
|
+
|
|
271
|
+
@staticmethod
|
|
272
|
+
def matches_exclude_paths(file_path: str, base_path: str, patterns: List[str]) -> bool:
|
|
273
|
+
"""Convenience matcher (compiles patterns per call); used in tests/ad-hoc checks."""
|
|
274
|
+
rel_path = os.path.relpath(file_path, base_path).replace(os.sep, "/")
|
|
275
|
+
return Core.path_matches_exclude_regexes(rel_path, Core.compile_exclude_paths(patterns))
|
|
276
|
+
|
|
216
277
|
def save_submitted_files_list(self, files: List[str], output_path: str) -> None:
|
|
217
278
|
"""
|
|
218
279
|
Save the list of submitted file names to a JSON file for debugging.
|
|
@@ -336,6 +397,17 @@ class Core:
|
|
|
336
397
|
start_time = time.time()
|
|
337
398
|
files: Set[str] = set()
|
|
338
399
|
|
|
400
|
+
# Unified --exclude-paths: filter discovered manifests by the same paths/globs that are
|
|
401
|
+
# forwarded to coana's --exclude-dirs. Only consulted when the user supplied the flag.
|
|
402
|
+
# Patterns are anchored to `path` (the scan root this pass walks), matching coana's
|
|
403
|
+
# target and the Node CLI's fast-glob cwd. NOTE: when scanning multiple --sub-path
|
|
404
|
+
# targets, find_files runs once per sub-path, so a pattern like `tests` anchors to each
|
|
405
|
+
# sub-path independently (Node anchors all patterns to a single scan-root cwd). This only
|
|
406
|
+
# differs for the multi-target full-scan + --exclude-paths combo; the reach flow is
|
|
407
|
+
# single-target, so it matches Node there.
|
|
408
|
+
exclude_paths = getattr(self.cli_config, "exclude_paths", None) if self.cli_config else None
|
|
409
|
+
exclude_regexes = Core.compile_exclude_paths(exclude_paths) if exclude_paths else []
|
|
410
|
+
|
|
339
411
|
# Get supported patterns from the API
|
|
340
412
|
patterns = self.get_supported_patterns()
|
|
341
413
|
|
|
@@ -365,8 +437,15 @@ class Core:
|
|
|
365
437
|
|
|
366
438
|
for glob_file in glob_files:
|
|
367
439
|
glob_file_str = str(glob_file)
|
|
368
|
-
if os.path.isfile(glob_file_str)
|
|
369
|
-
|
|
440
|
+
if not os.path.isfile(glob_file_str):
|
|
441
|
+
continue
|
|
442
|
+
if Core.is_excluded(glob_file_str, self.config.excluded_dirs):
|
|
443
|
+
continue
|
|
444
|
+
if exclude_regexes:
|
|
445
|
+
rel = os.path.relpath(glob_file_str, path)
|
|
446
|
+
if Core.path_matches_exclude_regexes(rel, exclude_regexes):
|
|
447
|
+
continue
|
|
448
|
+
files.add(glob_file_str.replace("\\", "/"))
|
|
370
449
|
|
|
371
450
|
glob_end = time.time()
|
|
372
451
|
log.debug(f"Globbing took {glob_end - glob_start:.4f} seconds")
|
socketsecurity/socketcli.py
CHANGED
|
@@ -388,7 +388,15 @@ def main_code():
|
|
|
388
388
|
timeout=config.reach_analysis_timeout,
|
|
389
389
|
memory_limit=config.reach_analysis_memory_limit,
|
|
390
390
|
ecosystems=config.reach_ecosystems,
|
|
391
|
-
|
|
391
|
+
# Union the deprecated --reach-exclude-paths with the unified --exclude-paths
|
|
392
|
+
# and forward verbatim to coana's --exclude-dirs. Patterns are scan-root
|
|
393
|
+
# relative; coana resolves --exclude-dirs relative to its `run` target, which
|
|
394
|
+
# here is `.` == cwd == scan root, so passthrough is correct. If a nested
|
|
395
|
+
# target is ever supported, re-anchor patterns to the target first (see Node's
|
|
396
|
+
# pathRelativeToTarget in exclude-paths.mts).
|
|
397
|
+
exclude_paths=(
|
|
398
|
+
(config.reach_exclude_paths or []) + (config.exclude_paths or [])
|
|
399
|
+
) or None,
|
|
392
400
|
min_severity=config.reach_min_severity,
|
|
393
401
|
skip_cache=config.reach_skip_cache or False,
|
|
394
402
|
disable_analytics=config.reach_disable_analytics or False,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: socketsecurity
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.4
|
|
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>
|
|
@@ -43,7 +43,7 @@ Requires-Dist: packaging
|
|
|
43
43
|
Requires-Dist: prettytable
|
|
44
44
|
Requires-Dist: python-dotenv
|
|
45
45
|
Requires-Dist: requests
|
|
46
|
-
Requires-Dist: socketdev<4.0.0,>=3.
|
|
46
|
+
Requires-Dist: socketdev<4.0.0,>=3.2.0
|
|
47
47
|
Provides-Extra: dev
|
|
48
48
|
Requires-Dist: hatch; extra == 'dev'
|
|
49
49
|
Requires-Dist: pre-commit; extra == 'dev'
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
LICENSE,sha256=42PY8FVn2DIlllI33b2wn91DeorIVqORc6Q9uw11DUU,1066
|
|
2
|
-
socketsecurity/__init__.py,sha256=
|
|
3
|
-
socketsecurity/config.py,sha256=
|
|
2
|
+
socketsecurity/__init__.py,sha256=OmNsArjDwCgclnJjgfTPOzi-UN_YKlbrAaz_Rq2ZLhY,94
|
|
3
|
+
socketsecurity/config.py,sha256=m0gxrEAUGHkUVgWGGkWh0p8TUDU26EaPuSIhLTDpxdA,42464
|
|
4
4
|
socketsecurity/fossa_compat.py,sha256=NO_bRlZkuzDNel2C-6lLfPbWAYwLta5gYWAVhtRaJAc,17596
|
|
5
5
|
socketsecurity/output.py,sha256=mCcf8bvmxHFgB3XSHYIWNsBa9WZ6SkrM9OftFhzZ2pw,17394
|
|
6
|
-
socketsecurity/socketcli.py,sha256=
|
|
7
|
-
socketsecurity/core/__init__.py,sha256=
|
|
6
|
+
socketsecurity/socketcli.py,sha256=idEfWoVkODD0i0uuuRGaVUPWtuXM8CGCb4ZjIHnH3l0,42729
|
|
7
|
+
socketsecurity/core/__init__.py,sha256=MYeXn4bAcGFZ36m8-RsYzWM7NkFZhGewA51OTu2hwtE,80386
|
|
8
8
|
socketsecurity/core/alert_selection.py,sha256=_lEm5sslT52bsM1YmdRWVaHTSq3U27465juOk99u-wY,8660
|
|
9
9
|
socketsecurity/core/classes.py,sha256=s33Z434aj2HXt1o6fDbGP3RqreW2KXBcOdjPbOT5F4w,17510
|
|
10
10
|
socketsecurity/core/cli_client.py,sha256=S9Ba-oCpx7QlGXLp5C77oarTv_uBU39biEHVMJJep1k,2548
|
|
@@ -34,8 +34,8 @@ socketsecurity/plugins/teams.py,sha256=49Q4zxx_S5oGOoB2D0XqneAV0e5M3YpumO9XL6SxW
|
|
|
34
34
|
socketsecurity/plugins/webhook.py,sha256=IG-eM3irrewWfieuyxBNUcJ9rqPBYjvM3bK42S3e0a0,439
|
|
35
35
|
socketsecurity/plugins/formatters/__init__.py,sha256=9FQBrh1Ju30Tlo4d2ao91-qQaRdcEX0A3Gu_0kk-Kno,145
|
|
36
36
|
socketsecurity/plugins/formatters/slack.py,sha256=6G5vH87akgPYxJ1XJWLgTozuTOOmk8m9VAAh677rc4A,9531
|
|
37
|
-
socketsecurity-2.4.
|
|
38
|
-
socketsecurity-2.4.
|
|
39
|
-
socketsecurity-2.4.
|
|
40
|
-
socketsecurity-2.4.
|
|
41
|
-
socketsecurity-2.4.
|
|
37
|
+
socketsecurity-2.4.4.dist-info/METADATA,sha256=cLWn3Fug6IC5n3LN8eMRHmoawAFboYSCEbGIg2_3IME,11687
|
|
38
|
+
socketsecurity-2.4.4.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
39
|
+
socketsecurity-2.4.4.dist-info/entry_points.txt,sha256=89ullhKkHRQUMGAXard1jWN2xNDL3qleyKOTAqnIyzY,103
|
|
40
|
+
socketsecurity-2.4.4.dist-info/licenses/LICENSE,sha256=42PY8FVn2DIlllI33b2wn91DeorIVqORc6Q9uw11DUU,1066
|
|
41
|
+
socketsecurity-2.4.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|