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
2 changes: 1 addition & 1 deletion .github/workflows/pr-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ jobs:
VERSION: ${{ env.VERSION }}
run: |
for i in {1..30}; do
if pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple socketdev==${VERSION}; then
if pip install --index-url 'https://test.pypi.org/simple/' --extra-index-url 'https://pypi.org/simple' socketdev==${VERSION}; then
echo "Package ${VERSION} is now available and installable on Test PyPI"
pip uninstall -y socketdev
echo "success=true" >> $GITHUB_OUTPUT
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ dist
*.dist
*.egg-info
*.cpython-312.pyc
example-socket-export.py
example-socket-export.py
__pycache__/
40 changes: 40 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,46 @@ Get GitHub Flavored Markdown diff between two full scans.
- **before (str)** - The base full scan ID
- **after (str)** - The comparison full scan ID

basics.get_config(org_slug, use_types)
""""""""""""""""""""""""""""""""""""""
Get Socket Basics configuration for an organization. Socket Basics is a CI/CD security scanning suite that includes SAST scanning, secret detection, container security, and dependency analysis.

**Usage:**

.. code-block:: python

from socketdev import socketdev
socket = socketdev(token="REPLACE_ME")

# Basic usage - returns dictionary
config = socket.basics.get_config("org_slug")
print(f"Python SAST enabled: {config['pythonSastEnabled']}")
print(f"Secret scanning enabled: {config['secretScanningEnabled']}")

# Using typed response objects
from socketdev.basics import SocketBasicsConfig, SocketBasicsResponse
response = socket.basics.get_config("org_slug", use_types=True)
if response.success and response.config:
print(f"JavaScript SAST: {response.config.javascriptSastEnabled}")
print(f"Trivy scanning: {response.config.trivyImageEnabled}")

**PARAMETERS:**

- **org_slug (str)** - The organization name
- **use_types (bool)** - Whether to return typed response objects (default: False)

**Socket Basics Features:**

- **Python SAST** - Static analysis for Python code
- **Go SAST** - Static analysis for Go code
- **JavaScript SAST** - Static analysis for JavaScript/TypeScript code
- **Secret Scanning** - Detection of hardcoded secrets and credentials
- **Trivy Image Scanning** - Vulnerability scanning for Docker images
- **Trivy Dockerfile Scanning** - Vulnerability scanning for Dockerfiles
- **Socket SCA** - Supply chain analysis for dependencies
- **Socket Scanning** - General dependency security scanning
- **Additional Parameters** - Custom configuration options

dependencies.get(limit, offset)
"""""""""""""""""""""""""""""""
Retrieve the dependencies for the organization associated with the API Key
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "socketdev"
version = "3.0.7"
version = "3.0.13"
requires-python = ">= 3.9"
dependencies = [
'requests',
Expand Down
2 changes: 2 additions & 0 deletions socketdev/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from socketdev.auditlog import AuditLog
from socketdev.analytics import Analytics
from socketdev.alerttypes import AlertTypes
from socketdev.basics import Basics
from socketdev.log import log

__author__ = "socket.dev"
Expand Down Expand Up @@ -72,6 +73,7 @@ def __init__(self, token: str, timeout: int = 1200):
self.auditlog = AuditLog(self.api)
self.analytics = Analytics(self.api)
self.alerttypes = AlertTypes(self.api)
self.basics = Basics(self.api)

@staticmethod
def set_timeout(timeout: int):
Expand Down
120 changes: 120 additions & 0 deletions socketdev/basics/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import logging
from typing import Optional, Union
from dataclasses import dataclass, asdict

log = logging.getLogger("socketdev")


@dataclass
class SocketBasicsConfig:
"""Data class representing Socket Basics configuration settings."""
pythonSastEnabled: bool = False
golangSastEnabled: bool = False
javascriptSastEnabled: bool = False
secretScanningEnabled: bool = False
trivyImageEnabled: bool = False
trivyDockerfileEnabled: bool = False
socketScanningEnabled: bool = False
socketScaEnabled: bool = False
additionalParameters: str = ""

def __getitem__(self, key):
return getattr(self, key)

def to_dict(self):
return asdict(self)

@classmethod
def from_dict(cls, data: dict) -> "SocketBasicsConfig":
return cls(
pythonSastEnabled=data.get("pythonSastEnabled", False),
golangSastEnabled=data.get("golangSastEnabled", False),
javascriptSastEnabled=data.get("javascriptSastEnabled", False),
secretScanningEnabled=data.get("secretScanningEnabled", False),
trivyImageEnabled=data.get("trivyImageEnabled", False),
trivyDockerfileEnabled=data.get("trivyDockerfileEnabled", False),
socketScanningEnabled=data.get("socketScanningEnabled", False),
socketScaEnabled=data.get("socketScaEnabled", False),
additionalParameters=data.get("additionalParameters", ""),
)


@dataclass
class SocketBasicsResponse:
"""Data class representing the response from Socket Basics API calls."""
success: bool
status: int
config: Optional[SocketBasicsConfig] = None
message: Optional[str] = None

def __getitem__(self, key):
return getattr(self, key)

def to_dict(self):
return asdict(self)

@classmethod
def from_dict(cls, data: dict) -> "SocketBasicsResponse":
return cls(
config=SocketBasicsConfig.from_dict(data) if data else None,
success=True,
status=200,
)


class Basics:
"""
Socket Basics API client for managing CI/CD security scanning configurations.

Socket Basics is a security scanning suite that includes:
- SAST (Static Application Security Testing) for Python, Go, and JavaScript
- Secret scanning for hardcoded credentials
- Container security for Docker images and Dockerfiles
- Socket SCA dependency scanning
"""

def __init__(self, api):
self.api = api

def get_config(
self, org_slug: str, use_types: bool = False
) -> Union[dict, SocketBasicsResponse]:
"""
Get Socket Basics configuration for an organization.

Args:
org_slug: Organization slug
use_types: Whether to return typed response objects (default: False)

Returns:
dict or SocketBasicsResponse: Configuration settings for Socket Basics

Example:
>>> basics = socketdev_client.basics
>>> config = basics.get_config("my-org")
>>> print(config["pythonSastEnabled"])

>>> # Using typed response
>>> response = basics.get_config("my-org", use_types=True)
>>> print(response.config.pythonSastEnabled)
"""
path = f"orgs/{org_slug}/settings/socket-basics"
response = self.api.do_request(path=path, method="GET")

if response.status_code == 200:
config_data = response.json()
if use_types:
return SocketBasicsResponse.from_dict(config_data)
return config_data

error_message = response.json().get("error", {}).get("message", "Unknown error")
log.error(f"Failed to get Socket Basics configuration: {response.status_code}, message: {error_message}")

if use_types:
return SocketBasicsResponse(
success=False,
status=response.status_code,
config=None,
message=error_message
)
return {}
24 changes: 16 additions & 8 deletions socketdev/core/dedupe.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def alert_key(alert: dict) -> tuple:
return (
alert["type"],
alert["severity"],
alert["category"],
alert.get("category"),
Dedupe.normalize_file_path(alert.get("file")),
alert.get("start"),
alert.get("end")
Expand All @@ -25,7 +25,7 @@ def alert_identity(alert: dict) -> tuple:
return (
alert["type"],
alert["severity"],
alert["category"],
alert.get("category"),
Dedupe.normalize_file_path(alert.get("file")),
alert.get("start"),
alert.get("end")
Expand All @@ -39,21 +39,29 @@ def alert_identity(alert: dict) -> tuple:

for alert in pkg.get("alerts", []):
identity = alert_identity(alert)
file = Dedupe.normalize_file_path(alert.get("file"))

if identity not in alert_map:
alert_map[identity] = {
# Build alert dict with only fields that exist in the original alert
consolidated_alert = {
"key": alert["key"], # keep the first key seen
"type": alert["type"],
"severity": alert["severity"],
"category": alert["category"],
"file": file,
"start": alert.get("start"),
"end": alert.get("end"),
"releases": [release],
"props": alert.get("props", []),
"action": alert["action"]
}

# Only include optional fields if they exist in the original alert
if "category" in alert:
consolidated_alert["category"] = alert["category"]
if "file" in alert:
consolidated_alert["file"] = Dedupe.normalize_file_path(alert["file"])
if "start" in alert:
consolidated_alert["start"] = alert["start"]
if "end" in alert:
consolidated_alert["end"] = alert["end"]

alert_map[identity] = consolidated_alert
else:
if release not in alert_map[identity]["releases"]:
alert_map[identity]["releases"].append(release)
Expand Down
2 changes: 1 addition & 1 deletion socketdev/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "3.0.7"
__version__ = "3.0.13"
Loading