Skip to content

Commit 3b59cce

Browse files
committed
feat: add Socket tier 1 reachability analysis support
- Add --reach flag and related CLI arguments for reachability analysis - Add ReachabilityAnalyzer class to run @coana-tech/cli - Add dependency checks for java, npm, uv, npx when --reach is enabled - Add --only-facts-file mode to submit only .socket.facts.json - Auto-install @coana-tech/cli if not present - Stream reachability CLI output to stderr for user visibility - Filter .socket.facts.json from manifest uploads but include in full scans - Set tmp=False in FullScanParams to fix API 400 errors
1 parent cc45ff4 commit 3b59cce

File tree

7 files changed

+497
-12
lines changed

7 files changed

+497
-12
lines changed

pyproject.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
66

77
[project]
88
name = "socketsecurity"
9-
version = "2.2.15"
9+
version = "2.2.16"
1010
requires-python = ">= 3.10"
1111
license = {"file" = "LICENSE"}
1212
dependencies = [
@@ -16,7 +16,8 @@ dependencies = [
1616
'GitPython',
1717
'packaging',
1818
'python-dotenv',
19-
'socketdev>=3.0.6,<4.0.0'
19+
'socketdev>=3.0.6,<4.0.0',
20+
"bs4>=0.0.2",
2021
]
2122
readme = "README.md"
2223
description = "Socket Security CLI for CI/CD"
@@ -158,4 +159,4 @@ docstring-code-format = false
158159
docstring-code-line-length = "dynamic"
159160

160161
[tool.hatch.build.targets.wheel]
161-
include = ["socketsecurity", "LICENSE"]
162+
include = ["socketsecurity", "LICENSE"]

socketsecurity/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__author__ = 'socket.dev'
2-
__version__ = '2.2.15'
2+
__version__ = '2.2.16'
33
USER_AGENT = f'SocketPythonCLI/{__version__}'

socketsecurity/config.py

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,19 @@ class CliConfig:
6262
save_manifest_tar: Optional[str] = None
6363
sub_paths: List[str] = field(default_factory=list)
6464
workspace_name: Optional[str] = None
65-
65+
# Reachability Flags
66+
reach: bool = False
67+
reach_version: Optional[str] = None
68+
reach_analysis_memory_limit: Optional[int] = None
69+
reach_analysis_timeout: Optional[int] = None
70+
reach_disable_analytics: bool = False
71+
reach_ecosystems: Optional[List[str]] = None
72+
reach_exclude_paths: Optional[List[str]] = None
73+
reach_skip_cache: bool = False
74+
reach_min_severity: Optional[str] = None
75+
reach_output_file: Optional[str] = None
76+
only_facts_file: bool = False
77+
6678
@classmethod
6779
def from_args(cls, args_list: Optional[List[str]] = None) -> 'CliConfig':
6880
parser = create_argument_parser()
@@ -110,6 +122,17 @@ def from_args(cls, args_list: Optional[List[str]] = None) -> 'CliConfig':
110122
'save_manifest_tar': args.save_manifest_tar,
111123
'sub_paths': args.sub_paths or [],
112124
'workspace_name': args.workspace_name,
125+
'reach': args.reach,
126+
'reach_version': args.reach_version,
127+
'reach_analysis_timeout': args.reach_analysis_timeout,
128+
'reach_analysis_memory_limit': args.reach_analysis_memory_limit,
129+
'reach_disable_analytics': args.reach_disable_analytics,
130+
'reach_ecosystems': args.reach_ecosystems.split(',') if args.reach_ecosystems else None,
131+
'reach_exclude_paths': args.reach_exclude_paths.split(',') if args.reach_exclude_paths else None,
132+
'reach_skip_cache': args.reach_skip_cache,
133+
'reach_min_severity': args.reach_min_severity,
134+
'reach_output_file': args.reach_output_file,
135+
'only_facts_file': args.only_facts_file,
113136
'version': __version__
114137
}
115138
try:
@@ -141,6 +164,11 @@ def from_args(cls, args_list: Optional[List[str]] = None) -> 'CliConfig':
141164
logging.error("--workspace-name requires --sub-path to be specified")
142165
exit(1)
143166

167+
# Validate that only_facts_file requires reach
168+
if args.only_facts_file and not args.reach:
169+
logging.error("--only-facts-file requires --reach to be specified")
170+
exit(1)
171+
144172
return cls(**config_args)
145173

146174
def to_dict(self) -> dict:
@@ -474,6 +502,78 @@ def create_argument_parser() -> argparse.ArgumentParser:
474502
help="Enabling including module folders like node_modules"
475503
)
476504

505+
# Reachability Configuration
506+
reachability_group = parser.add_argument_group('Reachability Analysis')
507+
reachability_group.add_argument(
508+
"--reach",
509+
dest="reach",
510+
action="store_true",
511+
help="Enable reachability analysis"
512+
)
513+
reachability_group.add_argument(
514+
"--reach-version",
515+
dest="reach_version",
516+
metavar="<version>",
517+
help="Specific version of @coana-tech/cli to use (e.g., '1.2.3')"
518+
)
519+
reachability_group.add_argument(
520+
"--reach-timeout",
521+
dest="reach_analysis_timeout",
522+
type=int,
523+
metavar="<seconds>",
524+
help="Timeout for reachability analysis in seconds"
525+
)
526+
reachability_group.add_argument(
527+
"--reach-memory-limit",
528+
dest="reach_analysis_memory_limit",
529+
type=int,
530+
metavar="<mb>",
531+
help="Memory limit for reachability analysis in MB"
532+
)
533+
reachability_group.add_argument(
534+
"--reach-ecosystems",
535+
dest="reach_ecosystems",
536+
metavar="<list>",
537+
help="Ecosystems to analyze for reachability (comma-separated, e.g., 'npm,pypi')"
538+
)
539+
reachability_group.add_argument(
540+
"--reach-exclude-paths",
541+
dest="reach_exclude_paths",
542+
metavar="<list>",
543+
help="Paths to exclude from reachability analysis (comma-separated)"
544+
)
545+
reachability_group.add_argument(
546+
"--reach-min-severity",
547+
dest="reach_min_severity",
548+
metavar="<level>",
549+
help="Minimum severity level for reachability analysis (info, low, moderate, high, critical)"
550+
)
551+
reachability_group.add_argument(
552+
"--reach-skip-cache",
553+
dest="reach_skip_cache",
554+
action="store_true",
555+
help="Skip cache usage for reachability analysis"
556+
)
557+
reachability_group.add_argument(
558+
"--reach-disable-analytics",
559+
dest="reach_disable_analytics",
560+
action="store_true",
561+
help="Disable analytics sharing for reachability analysis"
562+
)
563+
reachability_group.add_argument(
564+
"--reach-output-file",
565+
dest="reach_output_file",
566+
metavar="<path>",
567+
default=".socket.facts.json",
568+
help="Output file path for reachability analysis results (default: .socket.facts.json)"
569+
)
570+
reachability_group.add_argument(
571+
"--only-facts-file",
572+
dest="only_facts_file",
573+
action="store_true",
574+
help="Submit only the .socket.facts.json file when creating full scan (requires --reach)"
575+
)
576+
477577
parser.add_argument(
478578
'--version',
479579
action='version',

socketsecurity/core/helper/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import markdown
2-
from bs4 import BeautifulSoup, NavigableString, Tag
2+
from bs4 import BeautifulSoup, Tag
3+
from bs4.element import NavigableString
34
import string
45

56

0 commit comments

Comments
 (0)