diff --git a/.gitignore b/.gitignore index 3fbc794..23f720f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ scripts/*.py markdown_overview_temp.md markdown_security_temp.md .DS_Store -*.pyc \ No newline at end of file +*.pyc +test.py \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index accca17..20da55a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "socketsecurity" -version = "0.0.74" +version = "0.0.76" requires-python = ">= 3.9" dependencies = [ 'requests', diff --git a/socketsecurity/core/__init__.py b/socketsecurity/core/__init__.py index f6c1ebb..290dbf8 100644 --- a/socketsecurity/core/__init__.py +++ b/socketsecurity/core/__init__.py @@ -25,7 +25,7 @@ __author__ = 'socket.dev' -__version__ = '0.0.74' +__version__ = '0.0.76' __all__ = [ "Core", "log", @@ -489,7 +489,7 @@ def create_new_diff(path: str, params: FullScanParams, workspace: str) -> Diff: head_full_scan = Core.get_sbom_data(head_full_scan_id) head_end = time.time() total_head_time = head_end - head_start - print(f"Total time to get head full-scan {total_head_time: .2f}") + log.info(f"Total time to get head full-scan {total_head_time: .2f}") except APIResourceNotFound: head_full_scan = [] if files is not None and len(files) > 0: @@ -498,7 +498,7 @@ def create_new_diff(path: str, params: FullScanParams, workspace: str) -> Diff: new_full_scan.packages = Core.create_sbom_dict(new_full_scan.sbom_artifacts) new_scan_end = time.time() total_new_time = new_scan_end - new_scan_start - print(f"Total time to get new full-scan {total_new_time: .2f}") + log.info(f"Total time to get new full-scan {total_new_time: .2f}") diff_report = Core.compare_sboms(new_full_scan.sbom_artifacts, head_full_scan) diff_report.packages = new_full_scan.packages else: @@ -644,7 +644,10 @@ def create_issue_alerts(package: Package, alerts: dict, packages: dict) -> dict: """ for item in package.alerts: alert = Alert(**item) - props = getattr(all_issues, alert.type) + try: + props = getattr(all_issues, alert.type) + except AttributeError: + props = None if props is not None: description = props.description title = props.title diff --git a/socketsecurity/core/classes.py b/socketsecurity/core/classes.py index 30b735a..c1fd0c3 100644 --- a/socketsecurity/core/classes.py +++ b/socketsecurity/core/classes.py @@ -145,6 +145,8 @@ class Issue: suggestion: str introduced_by: list manifests: str + url: str + purl: str def __init__(self, **kwargs): if kwargs: @@ -157,6 +159,8 @@ def __init__(self, **kwargs): self.introduced_by = [] if not hasattr(self, "manifests"): self.manifests = "" + self.url = f"https://socket.dev/{self.pkg_type}/{self.pkg_name}/overview/{self.pkg_version}" + self.purl = f"{self.pkg_type}/{self.pkg_name}@{self.pkg_version}" def __str__(self): return json.dumps(self.__dict__) diff --git a/socketsecurity/core/messages.py b/socketsecurity/core/messages.py index 4aa4290..1984236 100644 --- a/socketsecurity/core/messages.py +++ b/socketsecurity/core/messages.py @@ -1,3 +1,5 @@ +import json + from mdutils import MdUtils from socketsecurity.core.classes import Diff, Purl, Issue from prettytable import PrettyTable @@ -5,6 +7,22 @@ class Messages: + @staticmethod + def create_security_comment_json(diff: Diff) -> dict: + if len(diff.new_alerts) == 0: + scan_failed = False + else: + scan_failed = True + output = { + "scan_failed": scan_failed, + "new_alerts": [] + } + for alert in diff.new_alerts: + alert: Issue + output["new_alerts"].append(json.loads(str(alert))) + return output + + @staticmethod def security_comment_template(diff: Diff) -> str: """ @@ -124,14 +142,13 @@ def create_security_alert_table(diff: Diff, md: MdUtils) -> (MdUtils, list, dict alert.description, alert.suggestion ] - package_url, purl = Messages.create_package_link(alert) - ignore = f"`SocketSecurity ignore {purl}`" + ignore = f"`SocketSecurity ignore {alert.purl}`" if ignore not in ignore_commands: ignore_commands.append(ignore) manifest_str, sources = Messages.create_sources(alert, "console") row = [ alert.title, - package_url, + alert.url, ", ".join(sources), manifest_str ] @@ -247,20 +264,6 @@ def create_purl_link(details: Purl) -> str: package_url = f"[{purl}](https://socket.dev/{details.ecosystem}/{details.name}/overview/{details.version})" return package_url - @staticmethod - def create_package_link(details: Issue) -> (str, str): - """ - Creates the package link for the Security Comment Template - :param details: Purl - Details about the package needed to create the URLs - :return: - """ - purl = f"{details.pkg_name}@{details.pkg_version}" - package_url = ( - f"[{purl}]" - f"(https://socket.dev/{details.pkg_type}/{details.pkg_name}/overview/{details.pkg_version})" - ) - return package_url, purl - @staticmethod def create_console_security_alert_table(diff: Diff) -> PrettyTable: """ @@ -278,11 +281,10 @@ def create_console_security_alert_table(diff: Diff) -> PrettyTable: ) for alert in diff.new_alerts: alert: Issue - package_url, purl = Messages.create_package_link(alert) manifest_str, sources = Messages.create_sources(alert, "console") row = [ alert.title, - package_url, + alert.url, ", ".join(sources), manifest_str ] diff --git a/socketsecurity/socketcli.py b/socketsecurity/socketcli.py index 3619248..4186210 100644 --- a/socketsecurity/socketcli.py +++ b/socketsecurity/socketcli.py @@ -105,6 +105,13 @@ default=False ) +parser.add_argument( + '--enable-json', + help='Enable json output of results instead of table formatted', + action='store_true', + default=False +) + def output_console_comments(diff_report) -> None: console_security_comment = Messages.create_console_security_alert_table(diff_report) @@ -116,7 +123,26 @@ def output_console_comments(diff_report) -> None: log.info("No New Security issues detected by Socket Security") +def output_console_json(diff_report) -> None: + console_security_comment = Messages.create_security_comment_json(diff_report) + print(json.dumps(console_security_comment)) + if len(diff_report.new_alerts) > 0: + sys.exit(1) + + def cli(): + try: + main_code() + except KeyboardInterrupt: + log.info("Keyboard Interrupt detected, exiting") + sys.exit(2) + except Exception as error: + log.error("Unexpected error when running the cli") + log.error(error) + sys.exit(3) + + +def main_code(): arguments = parser.parse_args() debug = arguments.enable_debug if debug: @@ -132,6 +158,7 @@ def cli(): commit_sha = arguments.commit_sha sbom_file = arguments.sbom_file license_mode = arguments.generate_license + enable_json = arguments.enable_json license_file = f"{repo}" if branch is not None: license_file += f"_{branch}" @@ -196,12 +223,18 @@ def cli(): new_security_comment, new_overview_comment ) - output_console_comments(diff) + if enable_json: + output_console_json(diff) + else: + output_console_comments(diff) else: log.info("API Mode") diff: Diff diff = core.create_new_diff(target_path, params, workspace=target_path) - output_console_comments(diff) + if enable_json: + output_console_json(diff) + else: + output_console_comments(diff) if diff is not None and license_mode: all_packages = {} for package_id in diff.packages: