From 62a3094034ff7121bc8af5099c33b36dee3f5d1d Mon Sep 17 00:00:00 2001 From: Douglas Coburn Date: Mon, 23 Jun 2025 22:25:20 -0700 Subject: [PATCH 1/2] Revert "Fixed logic for removed dependencies triggering dependency overview and fixed regex expander" This reverts commit 73e1ce29d1b8487e5b246dd60990a6f018958a48. --- pyproject.toml | 2 +- socketsecurity/__init__.py | 2 +- socketsecurity/core/__init__.py | 168 ++++++++++++++++++-------------- socketsecurity/socketcli.py | 2 +- 4 files changed, 97 insertions(+), 77 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 40f35d2..f331d6a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "socketsecurity" -version = "2.1.5" +version = "2.1.4" requires-python = ">= 3.10" license = {"file" = "LICENSE"} dependencies = [ diff --git a/socketsecurity/__init__.py b/socketsecurity/__init__.py index 9b92888..a088955 100644 --- a/socketsecurity/__init__.py +++ b/socketsecurity/__init__.py @@ -1,2 +1,2 @@ __author__ = 'socket.dev' -__version__ = '2.1.5' +__version__ = '2.1.4' diff --git a/socketsecurity/core/__init__.py b/socketsecurity/core/__init__.py index b5b80de..22742c1 100644 --- a/socketsecurity/core/__init__.py +++ b/socketsecurity/core/__init__.py @@ -133,40 +133,25 @@ def create_sbom_output(self, diff: Diff) -> dict: @staticmethod def expand_brace_pattern(pattern: str) -> List[str]: """ - Recursively expands brace expressions (e.g., {a,b,c}) into separate patterns, supporting nested braces. - """ - def recursive_expand(pat: str) -> List[str]: - stack = [] - for i, c in enumerate(pat): - if c == '{': - stack.append(i) - elif c == '}' and stack: - start = stack.pop() - if not stack: - # Found the outermost pair - before = pat[:start] - after = pat[i+1:] - inner = pat[start+1:i] - # Split on commas not inside nested braces - options = [] - depth = 0 - last = 0 - for j, ch in enumerate(inner): - if ch == '{': - depth += 1 - elif ch == '}': - depth -= 1 - elif ch == ',' and depth == 0: - options.append(inner[last:j]) - last = j+1 - options.append(inner[last:]) - results = [] - for opt in options: - expanded = before + opt + after - results.extend(recursive_expand(expanded)) - return results - return [pat] - return recursive_expand(pattern) + Expands brace expressions (e.g., {a,b,c}) into separate patterns. + """ + brace_regex = re.compile(r"\{([^{}]+)\}") + + # Expand all brace groups + expanded_patterns = [pattern] + while any("{" in p for p in expanded_patterns): + new_patterns = [] + for pat in expanded_patterns: + match = brace_regex.search(pat) + if match: + options = match.group(1).split(",") # Extract values inside {} + prefix, suffix = pat[:match.start()], pat[match.end():] + new_patterns.extend([prefix + opt + suffix for opt in options]) + else: + new_patterns.append(pat) + expanded_patterns = new_patterns + + return expanded_patterns @staticmethod def is_excluded(file_path: str, excluded_dirs: Set[str]) -> bool: @@ -191,7 +176,13 @@ def find_files(self, path: str) -> List[str]: files: Set[str] = set() # Get supported patterns from the API - patterns = self.get_supported_patterns() + try: + patterns = self.get_supported_patterns() + except Exception as e: + log.error(f"Error getting supported patterns from API: {e}") + log.warning("Falling back to local patterns") + from .utils import socket_globs as fallback_patterns + patterns = fallback_patterns for ecosystem in patterns: if ecosystem in self.config.excluded_ecosystems: @@ -434,7 +425,7 @@ def create_packages_dict(self, sbom_artifacts: list[SocketArtifact]) -> dict[str packages = {} top_level_count = {} for artifact in sbom_artifacts: - package = Package.from_socket_artifact(asdict(artifact)) + package = Package.from_diff_artifact(artifact.__dict__) if package.id in packages: print("Duplicate package?") else: @@ -543,14 +534,21 @@ def update_package_values(pkg: Package) -> Package: pkg.url += f"/{pkg.name}/overview/{pkg.version}" return pkg - def get_added_and_removed_packages(self, head_full_scan_id: str, new_full_scan_id: str) -> Tuple[Dict[str, Package], Dict[str, Package]]: + def get_added_and_removed_packages( + self, + head_full_scan_id: str, + new_full_scan_id: str, + merge: bool = False, + external_href: str = None, + ) -> Tuple[Dict[str, Package], Dict[str, Package], str, str]: """ Get packages that were added and removed between scans. Args: - head_full_scan: Previous scan (may be None if first scan) - head_full_scan_id: New scan just created - + head_full_scan_id: Previous scan + new_full_scan_id: New scan just created + merge: Whether the scan is merged into the default branch + external_href: External reference Returns: Tuple of (added_packages, removed_packages) dictionaries """ @@ -558,7 +556,22 @@ def get_added_and_removed_packages(self, head_full_scan_id: str, new_full_scan_i log.info(f"Comparing scans - Head scan ID: {head_full_scan_id}, New scan ID: {new_full_scan_id}") diff_start = time.time() try: - diff_report = self.sdk.fullscans.stream_diff(self.config.org_slug, head_full_scan_id, new_full_scan_id, use_types=True).data + params = { + "before": head_full_scan_id, + "after": new_full_scan_id, + "description": f"Diff scan between head {head_full_scan_id} and new {new_full_scan_id} scans", + "merge": merge, + } + if external_href: + params["external_href"] = external_href + new_diff_scan = self.sdk.diffscans.create_from_ids(self.config.org_slug, params) + data = new_diff_scan.get("diff_scan", {}) + diff_scan_id = data.get("id") + if not diff_scan_id: + log.error(f"Failed to get diff scan ID for {new_full_scan_id}") + log.error(new_diff_scan) + sys.exit(1) + diff_report = self.sdk.diffscans.get(self.config.org_slug, diff_scan_id) except APIFailure as e: log.error(f"API Error: {e}") sys.exit(1) @@ -568,44 +581,63 @@ def get_added_and_removed_packages(self, head_full_scan_id: str, new_full_scan_i log.error(f"Stack trace:\n{traceback.format_exc()}") raise + diff_data = diff_report.get("diff_scan", {}) diff_end = time.time() + diff_url = diff_data.get("html_url") + after_data = diff_data.get("after_full_scan") + if after_data: + new_full_scan_url = after_data.get("html_url") + else: + new_full_scan_url = "" + artifacts = diff_data.get("artifacts", {}) + added = artifacts.get("added", []) + removed = artifacts.get("removed", []) + unchanged = artifacts.get("unchanged", []) + replaced = artifacts.get("replaced", []) + updated = artifacts.get("updated", []) log.info(f"Diff Report Gathered in {diff_end - diff_start:.2f} seconds") log.info("Diff report artifact counts:") - log.info(f"Added: {len(diff_report.artifacts.added)}") - log.info(f"Removed: {len(diff_report.artifacts.removed)}") - log.info(f"Unchanged: {len(diff_report.artifacts.unchanged)}") - log.info(f"Replaced: {len(diff_report.artifacts.replaced)}") - log.info(f"Updated: {len(diff_report.artifacts.updated)}") + log.info(f"Added: {len(added)}") + log.info(f"Removed: {len(removed)}") + log.info(f"Unchanged: {len(unchanged)}") + log.info(f"Replaced: {len(replaced)}") + log.info(f"Updated: {len(updated)}") - added_artifacts = diff_report.artifacts.added + diff_report.artifacts.updated - removed_artifacts = diff_report.artifacts.removed + diff_report.artifacts.replaced + added_artifacts = added + updated + removed_artifacts = removed added_packages: Dict[str, Package] = {} removed_packages: Dict[str, Package] = {} for artifact in added_artifacts: + artifact_id = artifact.get("id") + artifact_name = artifact.get("name") + artifact_version = artifact.get("version") try: - pkg = Package.from_diff_artifact(asdict(artifact)) + pkg = Package.from_diff_artifact(artifact) pkg = Core.update_package_values(pkg) - added_packages[artifact.id] = pkg + added_packages[pkg.id] = pkg except KeyError: - log.error(f"KeyError: Could not create package from added artifact {artifact.id}") - log.error(f"Artifact details - name: {artifact.name}, version: {artifact.version}") + log.error(f"KeyError: Could not create package from added artifact {artifact_id}") + log.error(f"Artifact details - name: {artifact_name}, version: {artifact_version}") log.error("No matching packages found in new_full_scan") for artifact in removed_artifacts: + artifact_id = artifact.get("id") + artifact_name = artifact.get("name") + artifact_version = artifact.get("version") try: - pkg = Package.from_diff_artifact(asdict(artifact)) + pkg = Package.from_diff_artifact(artifact) pkg = Core.update_package_values(pkg) if pkg.namespace: pkg.purl += f"{pkg.namespace}/{pkg.purl}" - removed_packages[artifact.id] = pkg + removed_packages[pkg.id] = pkg except KeyError: - log.error(f"KeyError: Could not create package from removed artifact {artifact.id}") - log.error(f"Artifact details - name: {artifact.name}, version: {artifact.version}") + log.error(f"KeyError: Could not create package from removed artifact {artifact_id}") + log.error(f"Artifact details - name: {artifact_name}, version: {artifact_version}") log.error("No matching packages found in head_full_scan") - return added_packages, removed_packages + return added_packages, removed_packages, diff_url, new_full_scan_url def create_new_diff( self, @@ -651,7 +683,6 @@ def create_new_diff( try: new_scan_start = time.time() new_full_scan = self.create_full_scan(files_for_sending, params) - new_full_scan.sbom_artifacts = self.get_sbom_data(new_full_scan.id) new_scan_end = time.time() log.info(f"Total time to create new full scan: {new_scan_end - new_scan_start:.2f}") except APIFailure as e: @@ -663,26 +694,15 @@ def create_new_diff( log.error(f"Stack trace:\n{traceback.format_exc()}") raise - scans_ready = self.check_full_scans_status(head_full_scan_id, new_full_scan.id) - if scans_ready is False: - log.error(f"Full scans did not complete within {self.config.timeout} seconds") - added_packages, removed_packages = self.get_added_and_removed_packages(head_full_scan_id, new_full_scan.id) + added_packages, removed_packages, diff_url, report_url = self.get_added_and_removed_packages( + head_full_scan_id, + new_full_scan.id + ) diff = self.create_diff_report(added_packages, removed_packages) - - base_socket = "https://socket.dev/dashboard/org" diff.id = new_full_scan.id - - report_url = f"{base_socket}/{self.config.org_slug}/sbom/{diff.id}" - if not params.include_license_details: - report_url += "?include_license_details=false" diff.report_url = report_url - - if head_full_scan_id is not None: - diff.diff_url = f"{base_socket}/{self.config.org_slug}/diff/{head_full_scan_id}/{diff.id}" - else: - diff.diff_url = diff.report_url - + diff.diff_url = diff_url return diff def create_diff_report( diff --git a/socketsecurity/socketcli.py b/socketsecurity/socketcli.py index 5613a3c..e6594ad 100644 --- a/socketsecurity/socketcli.py +++ b/socketsecurity/socketcli.py @@ -235,7 +235,7 @@ def main_code(): log.debug("Updated security comment with no new alerts") # FIXME: diff.new_packages is never populated, neither is removed_packages - if (len(diff.new_packages) == 0) or config.disable_overview: + if (len(diff.new_packages) == 0 and len(diff.removed_packages) == 0) or config.disable_overview: if not update_old_overview_comment: new_overview_comment = False log.debug("No new/removed packages or Dependency Overview comment disabled") From 642e1f7e155403f39ca96a0fd5746699f87d59de Mon Sep 17 00:00:00 2001 From: Douglas Coburn Date: Mon, 23 Jun 2025 22:25:20 -0700 Subject: [PATCH 2/2] Revert "Added helper function for converting gfm output" This reverts commit e039fb74319f93898a81094af4528b21f43adc68. --- pyproject.toml | 2 +- socketsecurity/__init__.py | 2 +- socketsecurity/core/__init__.py | 107 ++++++++-------------- socketsecurity/core/classes.py | 19 ++-- socketsecurity/core/helper/__init__.py | 119 ------------------------- socketsecurity/core/messages.py | 3 +- socketsecurity/output.py | 3 +- 7 files changed, 48 insertions(+), 207 deletions(-) delete mode 100644 socketsecurity/core/helper/__init__.py diff --git a/pyproject.toml b/pyproject.toml index f331d6a..2c8ddd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "socketsecurity" -version = "2.1.4" +version = "2.1.3" requires-python = ">= 3.10" license = {"file" = "LICENSE"} dependencies = [ diff --git a/socketsecurity/__init__.py b/socketsecurity/__init__.py index a088955..a9cb4f2 100644 --- a/socketsecurity/__init__.py +++ b/socketsecurity/__init__.py @@ -1,2 +1,2 @@ __author__ = 'socket.dev' -__version__ = '2.1.4' +__version__ = '2.1.3' diff --git a/socketsecurity/core/__init__.py b/socketsecurity/core/__init__.py index 22742c1..7245b4d 100644 --- a/socketsecurity/core/__init__.py +++ b/socketsecurity/core/__init__.py @@ -425,7 +425,7 @@ def create_packages_dict(self, sbom_artifacts: list[SocketArtifact]) -> dict[str packages = {} top_level_count = {} for artifact in sbom_artifacts: - package = Package.from_diff_artifact(artifact.__dict__) + package = Package.from_socket_artifact(asdict(artifact)) if package.id in packages: print("Duplicate package?") else: @@ -534,21 +534,14 @@ def update_package_values(pkg: Package) -> Package: pkg.url += f"/{pkg.name}/overview/{pkg.version}" return pkg - def get_added_and_removed_packages( - self, - head_full_scan_id: str, - new_full_scan_id: str, - merge: bool = False, - external_href: str = None, - ) -> Tuple[Dict[str, Package], Dict[str, Package], str, str]: + def get_added_and_removed_packages(self, head_full_scan_id: str, new_full_scan_id: str) -> Tuple[Dict[str, Package], Dict[str, Package]]: """ Get packages that were added and removed between scans. Args: - head_full_scan_id: Previous scan - new_full_scan_id: New scan just created - merge: Whether the scan is merged into the default branch - external_href: External reference + head_full_scan: Previous scan (may be None if first scan) + head_full_scan_id: New scan just created + Returns: Tuple of (added_packages, removed_packages) dictionaries """ @@ -556,22 +549,7 @@ def get_added_and_removed_packages( log.info(f"Comparing scans - Head scan ID: {head_full_scan_id}, New scan ID: {new_full_scan_id}") diff_start = time.time() try: - params = { - "before": head_full_scan_id, - "after": new_full_scan_id, - "description": f"Diff scan between head {head_full_scan_id} and new {new_full_scan_id} scans", - "merge": merge, - } - if external_href: - params["external_href"] = external_href - new_diff_scan = self.sdk.diffscans.create_from_ids(self.config.org_slug, params) - data = new_diff_scan.get("diff_scan", {}) - diff_scan_id = data.get("id") - if not diff_scan_id: - log.error(f"Failed to get diff scan ID for {new_full_scan_id}") - log.error(new_diff_scan) - sys.exit(1) - diff_report = self.sdk.diffscans.get(self.config.org_slug, diff_scan_id) + diff_report = self.sdk.fullscans.stream_diff(self.config.org_slug, head_full_scan_id, new_full_scan_id, use_types=True).data except APIFailure as e: log.error(f"API Error: {e}") sys.exit(1) @@ -581,63 +559,44 @@ def get_added_and_removed_packages( log.error(f"Stack trace:\n{traceback.format_exc()}") raise - diff_data = diff_report.get("diff_scan", {}) diff_end = time.time() - diff_url = diff_data.get("html_url") - after_data = diff_data.get("after_full_scan") - if after_data: - new_full_scan_url = after_data.get("html_url") - else: - new_full_scan_url = "" - artifacts = diff_data.get("artifacts", {}) - added = artifacts.get("added", []) - removed = artifacts.get("removed", []) - unchanged = artifacts.get("unchanged", []) - replaced = artifacts.get("replaced", []) - updated = artifacts.get("updated", []) log.info(f"Diff Report Gathered in {diff_end - diff_start:.2f} seconds") log.info("Diff report artifact counts:") - log.info(f"Added: {len(added)}") - log.info(f"Removed: {len(removed)}") - log.info(f"Unchanged: {len(unchanged)}") - log.info(f"Replaced: {len(replaced)}") - log.info(f"Updated: {len(updated)}") + log.info(f"Added: {len(diff_report.artifacts.added)}") + log.info(f"Removed: {len(diff_report.artifacts.removed)}") + log.info(f"Unchanged: {len(diff_report.artifacts.unchanged)}") + log.info(f"Replaced: {len(diff_report.artifacts.replaced)}") + log.info(f"Updated: {len(diff_report.artifacts.updated)}") - added_artifacts = added + updated - removed_artifacts = removed + added_artifacts = diff_report.artifacts.added + diff_report.artifacts.updated + removed_artifacts = diff_report.artifacts.removed + diff_report.artifacts.replaced added_packages: Dict[str, Package] = {} removed_packages: Dict[str, Package] = {} for artifact in added_artifacts: - artifact_id = artifact.get("id") - artifact_name = artifact.get("name") - artifact_version = artifact.get("version") try: - pkg = Package.from_diff_artifact(artifact) + pkg = Package.from_diff_artifact(asdict(artifact)) pkg = Core.update_package_values(pkg) - added_packages[pkg.id] = pkg + added_packages[artifact.id] = pkg except KeyError: - log.error(f"KeyError: Could not create package from added artifact {artifact_id}") - log.error(f"Artifact details - name: {artifact_name}, version: {artifact_version}") + log.error(f"KeyError: Could not create package from added artifact {artifact.id}") + log.error(f"Artifact details - name: {artifact.name}, version: {artifact.version}") log.error("No matching packages found in new_full_scan") for artifact in removed_artifacts: - artifact_id = artifact.get("id") - artifact_name = artifact.get("name") - artifact_version = artifact.get("version") try: - pkg = Package.from_diff_artifact(artifact) + pkg = Package.from_diff_artifact(asdict(artifact)) pkg = Core.update_package_values(pkg) if pkg.namespace: pkg.purl += f"{pkg.namespace}/{pkg.purl}" - removed_packages[pkg.id] = pkg + removed_packages[artifact.id] = pkg except KeyError: - log.error(f"KeyError: Could not create package from removed artifact {artifact_id}") - log.error(f"Artifact details - name: {artifact_name}, version: {artifact_version}") + log.error(f"KeyError: Could not create package from removed artifact {artifact.id}") + log.error(f"Artifact details - name: {artifact.name}, version: {artifact.version}") log.error("No matching packages found in head_full_scan") - return added_packages, removed_packages, diff_url, new_full_scan_url + return added_packages, removed_packages def create_new_diff( self, @@ -683,6 +642,7 @@ def create_new_diff( try: new_scan_start = time.time() new_full_scan = self.create_full_scan(files_for_sending, params) + new_full_scan.sbom_artifacts = self.get_sbom_data(new_full_scan.id) new_scan_end = time.time() log.info(f"Total time to create new full scan: {new_scan_end - new_scan_start:.2f}") except APIFailure as e: @@ -694,15 +654,26 @@ def create_new_diff( log.error(f"Stack trace:\n{traceback.format_exc()}") raise - added_packages, removed_packages, diff_url, report_url = self.get_added_and_removed_packages( - head_full_scan_id, - new_full_scan.id - ) + scans_ready = self.check_full_scans_status(head_full_scan_id, new_full_scan.id) + if scans_ready is False: + log.error(f"Full scans did not complete within {self.config.timeout} seconds") + added_packages, removed_packages = self.get_added_and_removed_packages(head_full_scan_id, new_full_scan.id) diff = self.create_diff_report(added_packages, removed_packages) + + base_socket = "https://socket.dev/dashboard/org" diff.id = new_full_scan.id + + report_url = f"{base_socket}/{self.config.org_slug}/sbom/{diff.id}" + if not params.include_license_details: + report_url += "?include_license_details=false" diff.report_url = report_url - diff.diff_url = diff_url + + if head_full_scan_id is not None: + diff.diff_url = f"{base_socket}/{self.config.org_slug}/diff/{head_full_scan_id}/{diff.id}" + else: + diff.diff_url = diff.report_url + return diff def create_diff_report( diff --git a/socketsecurity/core/classes.py b/socketsecurity/core/classes.py index 8357458..aefb0ab 100644 --- a/socketsecurity/core/classes.py +++ b/socketsecurity/core/classes.py @@ -97,7 +97,7 @@ class AlertCounts(TypedDict): low: int @dataclass(kw_only=True) -class Package(): +class Package(SocketArtifactLink): """ Represents a package detected in a Socket Security scan. @@ -106,23 +106,16 @@ class Package(): """ # Common properties from both artifact types - type: str + id: str name: str version: str - release: str - diffType: str - id: str - author: List[str] = field(default_factory=list) + type: str score: SocketScore alerts: List[SocketAlert] + author: List[str] = field(default_factory=list) size: Optional[int] = None license: Optional[str] = None namespace: Optional[str] = None - topLevelAncestors: Optional[List[str]] = None - direct: Optional[bool] = False - manifestFiles: Optional[List[SocketManifestReference]] = None - dependencies: Optional[List[str]] = None - artifact: Optional[SocketArtifactLink] = None # Package-specific fields license_text: str = "" @@ -210,9 +203,7 @@ def from_diff_artifact(cls, data: dict) -> "Package": manifestFiles=ref.get("manifestFiles", []), dependencies=ref.get("dependencies"), artifact=ref.get("artifact"), - namespace=data.get('namespace', None), - release=ref.get("release", None), - diffType=ref.get("diffType", None), + namespace=data.get('namespace', None) ) class Issue: diff --git a/socketsecurity/core/helper/__init__.py b/socketsecurity/core/helper/__init__.py deleted file mode 100644 index f10cb6e..0000000 --- a/socketsecurity/core/helper/__init__.py +++ /dev/null @@ -1,119 +0,0 @@ -import markdown -from bs4 import BeautifulSoup, NavigableString, Tag -import string - - -class Helper: - @staticmethod - def parse_gfm_section(html_content): - """ - Parse a GitHub-Flavored Markdown section containing a table and surrounding content. - Returns a dict with "before_html", "columns", "rows_html", and "after_html". - """ - html = markdown.markdown(html_content, extensions=['extra']) - soup = BeautifulSoup(html, "html.parser") - - table = soup.find('table') - if not table: - # If no table, treat entire content as before_html - return {"before_html": html, "columns": [], "rows_html": [], "after_html": ''} - - # Collect HTML before the table - before_parts = [str(elem) for elem in table.find_previous_siblings()] - before_html = ''.join(reversed(before_parts)) - - # Collect HTML after the table - after_parts = [str(elem) for elem in table.find_next_siblings()] - after_html = ''.join(after_parts) - - # Extract table headers - headers = [th.get_text(strip=True) for th in table.find_all('th')] - - # Extract table rows (skip header) - rows_html = [] - for tr in table.find_all('tr')[1:]: - cells = [str(td) for td in tr.find_all('td')] - rows_html.append(cells) - - return { - "before_html": before_html, - "columns": headers, - "rows_html": rows_html, - "after_html": after_html - } - - @staticmethod - def parse_cell(html_td): - """Convert a table cell HTML into plain text or a dict for links/images.""" - soup = BeautifulSoup(html_td, "html.parser") - a = soup.find('a') - if a: - cell = {"url": a.get('href', '')} - img = a.find('img') - if img: - cell.update({ - "img_src": img.get('src', ''), - "title": img.get('title', ''), - "link_text": a.get_text(strip=True) - }) - else: - cell["link_text"] = a.get_text(strip=True) - return cell - return soup.get_text(strip=True) - - @staticmethod - def parse_html_parts(html_fragment): - """ - Convert an HTML fragment into a list of parts. - Each part is either: - - {"text": "..."} - - {"link": "url", "text": "..."} - - {"img_src": "url", "alt": "...", "title": "..."} - """ - soup = BeautifulSoup(html_fragment, 'html.parser') - parts = [] - - def handle_element(elem): - if isinstance(elem, NavigableString): - text = str(elem).strip() - if text and not all(ch in string.punctuation for ch in text): - parts.append({"text": text}) - elif isinstance(elem, Tag): - if elem.name == 'a': - href = elem.get('href', '') - txt = elem.get_text(strip=True) - parts.append({"link": href, "text": txt}) - elif elem.name == 'img': - parts.append({ - "img_src": elem.get('src', ''), - "alt": elem.get('alt', ''), - "title": elem.get('title', '') - }) - else: - # Recurse into children for nested tags - for child in elem.children: - handle_element(child) - - for element in soup.contents: - handle_element(element) - - return parts - - @staticmethod - def section_to_json(section_result): - """ - Convert a parsed section into structured JSON. - Returns {"before": [...], "table": [...], "after": [...]}. - """ - # Build JSON rows for the table - table_rows = [] - cols = section_result.get('columns', []) - for row_html in section_result.get('rows_html', []): - cells = [Helper.parse_cell(cell_html) for cell_html in row_html] - table_rows.append(dict(zip(cols, cells))) - - return { - "before": Helper.parse_html_parts(section_result.get('before_html', '')), - "table": table_rows, - "after": Helper.parse_html_parts(section_result.get('after_html', '')) - } \ No newline at end of file diff --git a/socketsecurity/core/messages.py b/socketsecurity/core/messages.py index 0b5fc62..b86b37f 100644 --- a/socketsecurity/core/messages.py +++ b/socketsecurity/core/messages.py @@ -292,8 +292,7 @@ def create_security_comment_json(diff: Diff) -> dict: output = { "scan_failed": scan_failed, "new_alerts": [], - "full_scan_id": diff.id, - "diff_url": diff.diff_url + "full_scan_id": diff.id } for alert in diff.new_alerts: alert: Issue diff --git a/socketsecurity/output.py b/socketsecurity/output.py index a1f8647..2b523d5 100644 --- a/socketsecurity/output.py +++ b/socketsecurity/output.py @@ -66,8 +66,7 @@ def output_console_comments(self, diff_report: Diff, sbom_file_name: Optional[st console_security_comment = Messages.create_console_security_alert_table(diff_report) self.logger.info("Security issues detected by Socket Security:") - self.logger.info(f"Diff Url: {diff_report.diff_url}") - self.logger.info(f"\n{console_security_comment}") + self.logger.info(console_security_comment) def output_console_json(self, diff_report: Diff, sbom_file_name: Optional[str] = None) -> None: """Outputs JSON formatted results"""