Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,64 @@ Retrieve the package information for a purl post
- **license (str)** - The license parameter if enabled will show alerts and license information. If disabled will only show the basic package metadata and scores. Default is true
- **components (array{dict})** - The components list of packages urls

export.cdx_bom(org_slug, id, query_params)
"""""""""""""""""""""""""""""""""""""""""
Export a Socket SBOM as a CycloneDX SBOM

**Usage:**

.. code-block::

from socketdev import socketdev
from socketdev.export import ExportQueryParams

socket = socketdev(token="REPLACE_ME")
query_params = ExportQueryParams(
author="john_doe",
project_name="my-project"
)
print(socket.export.cdx_bom("org_slug", "sbom_id", query_params))

**PARAMETERS:**

- **org_slug (str)** - The organization name
- **id (str)** - The ID of either a full scan or an SBOM report
- **query_params (ExportQueryParams)** - Optional query parameters for filtering:
- **author (str)** - Filter by author
- **project_group (str)** - Filter by project group
- **project_name (str)** - Filter by project name
- **project_version (str)** - Filter by project version
- **project_id (str)** - Filter by project ID

export.spdx_bom(org_slug, id, query_params)
""""""""""""""""""""""""""""""""""""""""""
Export a Socket SBOM as an SPDX SBOM

**Usage:**

.. code-block::

from socketdev import socketdev
from socketdev.export import ExportQueryParams

socket = socketdev(token="REPLACE_ME")
query_params = ExportQueryParams(
project_name="my-project",
project_version="1.0.0"
)
print(socket.export.spdx_bom("org_slug", "sbom_id", query_params))

**PARAMETERS:**

- **org_slug (str)** - The organization name
- **id (str)** - The ID of either a full scan or an SBOM report
- **query_params (ExportQueryParams)** - Optional query parameters for filtering:
- **author (str)** - Filter by author
- **project_group (str)** - Filter by project group
- **project_name (str)** - Filter by project name
- **project_version (str)** - Filter by project version
- **project_id (str)** - Filter by project ID

fullscans.get(org_slug)
"""""""""""""""""""""""
Retrieve the Fullscans information for around Organization
Expand Down Expand Up @@ -143,6 +201,25 @@ Delete an existing full scan.
- **org_slug (str)** - The organization name
- **full_scan_id (str)** - The ID of the full scan

fullscans.stream_diff(org_slug, before, after, preview)
"""""""""""""""""""""""""""""""""""""""""""""""""""""""
Stream a diff between two full scans. Returns a scan diff.

**Usage:**

.. code-block::

from socketdev import socketdev
socket = socketdev(token="REPLACE_ME")
print(socket.fullscans.stream_diff("org_slug", "before_scan_id", "after_scan_id"))

**PARAMETERS:**

- **org_slug (str)** - The organization name
- **before (str)** - The base full scan ID
- **after (str)** - The comparison full scan ID
- **preview (bool)** - Create a diff-scan that is not persisted. Defaults to False

fullscans.stream(org_slug, full_scan_id)
""""""""""""""""""""""""""""""""""""""""
Stream all SBOM artifacts for a full scan.
Expand Down
80 changes: 79 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ classifiers = [
"Programming Language :: Python :: 3.14"
]

# modern, faster linter and language server. install with `pip install -e ".[dev]"`
[project.optional-dependencies]
dev = [
"ruff>=0.3.0",
]

[project.urls]
Homepage = "https://github.com/socketdev/socket-sdk-python"

Expand All @@ -53,4 +59,76 @@ include = [
]

[tool.setuptools.dynamic]
version = {attr = "socketdev.__version__"}
version = {attr = "socketdev.__version__"}

[tool.ruff]
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"site-packages",
"venv",
]

[tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E4", "E7", "E9", "F"]
ignore = []

# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []

# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"

# Like Black, indent with spaces, rather than tabs.
indent-style = "space"

# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false

# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"

# Enable auto-formatting of code examples in docstrings. Markdown,
# reStructuredText code/literal blocks and doctests are all supported.
#
# This is currently disabled by default, but it is planned for this
# to be opt-out in the future.
docstring-code-format = false

# Set the line length limit used when formatting code snippets in
# docstrings.
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"
49 changes: 18 additions & 31 deletions socketdev/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,26 @@
import requests
import base64

from socketdev.core.classes import Response
from socketdev.dependencies import Dependencies
from socketdev.exceptions import APIKeyMissing, APIFailure, APIAccessDenied, APIInsufficientQuota, APIResourceNotFound
from socketdev.export import Export
from socketdev.fullscans import FullScans
from socketdev.npm import NPM
from socketdev.openapi import OpenAPI
from socketdev.org import Orgs
from socketdev.purl import Purl
from socketdev.quota import Quota
from socketdev.report import Report
from socketdev.sbom import Sbom
from socketdev.purl import Purl
from socketdev.fullscans import FullScans
from socketdev.repos import Repos
from socketdev.repositories import Repositories
from socketdev.sbom import Sbom
from socketdev.settings import Settings
from socketdev.core.classes import Response
from socketdev.exceptions import APIKeyMissing, APIFailure, APIAccessDenied, APIInsufficientQuota, APIResourceNotFound


__author__ = 'socket.dev'
__version__ = '1.0.12'
__all__ = [
"socketdev"
]
__author__ = "socket.dev"
__version__ = "1.0.13"
__all__ = ["socketdev"]


global encoded_key
Expand All @@ -36,15 +35,11 @@

def encode_key(token: str):
global encoded_key
encoded_key = base64.b64encode(token.encode()).decode('ascii')
encoded_key = base64.b64encode(token.encode()).decode("ascii")


def do_request(
path: str,
headers: dict = None,
payload: [dict, str] = None,
files: list = None,
method: str = "GET"
path: str, headers: dict = None, payload: [dict, str] = None, files: list = None, method: str = "GET"
) -> Response:
"""
Shared function for performing the requests against the API.
Expand All @@ -60,19 +55,14 @@ def do_request(

if headers is None:
headers = {
'Authorization': f"Basic {encoded_key}",
'User-Agent': f'SocketPythonScript/{__version__}',
"accept": "application/json"
"Authorization": f"Basic {encoded_key}",
"User-Agent": f"SocketPythonScript/{__version__}",
"accept": "application/json",
}
url = f"{api_url}/{path}"
try:
response = requests.request(
method.upper(),
url,
headers=headers,
data=payload,
files=files,
timeout=request_timeout
method.upper(), url, headers=headers, data=payload, files=files, timeout=request_timeout
)
if response.status_code >= 400:
raise APIFailure("Bad Request")
Expand All @@ -85,11 +75,7 @@ def do_request(
elif response.status_code == 429:
raise APIInsufficientQuota("Insufficient quota for API route")
except Exception as error:
response = Response(
text=f"{error}",
error=True,
status_code=500
)
response = Response(text=f"{error}", error=True, status_code=500)
raise APIFailure(response)
return response

Expand All @@ -104,8 +90,9 @@ class socketdev:
quota: Quota
report: Report
sbom: Sbom
purl: purl
purl: Purl
fullscans: FullScans
export: Export
repositories: Repositories
settings: Settings
repos: Repos
Expand Down
62 changes: 62 additions & 0 deletions socketdev/export/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from urllib.parse import urlencode
from dataclasses import dataclass, asdict
from typing import Optional
import socketdev


@dataclass
class ExportQueryParams:
author: Optional[str] = None
project_group: Optional[str] = None
project_name: Optional[str] = None
project_version: Optional[str] = None
project_id: Optional[str] = None

def to_query_params(self) -> str:
# Filter out None values and convert to query string
params = {k: v for k, v in asdict(self).items() if v is not None}
if not params:
return ""
return "?" + urlencode(params)


class Export:
@staticmethod
def cdx_bom(org_slug: str, id: str, query_params: Optional[ExportQueryParams] = None) -> dict:
"""
Export a Socket SBOM as a CycloneDX SBOM
:param org_slug: String - The slug of the organization
:param id: String - The id of either a full scan or an sbom report
:param query_params: Optional[ExportQueryParams] - Query parameters for filtering
:return:
"""
path = f"orgs/{org_slug}/export/cdx/{id}"
if query_params:
path += query_params.to_query_params()
result = socketdev.do_request(path=path)
try:
sbom = result.json()
sbom["success"] = True
except Exception as error:
sbom = {"success": False, "message": str(error)}
return sbom

@staticmethod
def spdx_bom(org_slug: str, id: str, query_params: Optional[ExportQueryParams] = None) -> dict:
"""
Export a Socket SBOM as an SPDX SBOM
:param org_slug: String - The slug of the organization
:param id: String - The id of either a full scan or an sbom report
:param query_params: Optional[ExportQueryParams] - Query parameters for filtering
:return:
"""
path = f"orgs/{org_slug}/export/spdx/{id}"
if query_params:
path += query_params.to_query_params()
result = socketdev.do_request(path=path)
try:
sbom = result.json()
sbom["success"] = True
except Exception as error:
sbom = {"success": False, "message": str(error)}
return sbom
Loading