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/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
python-version: '3.10'
cache: 'pip'

# Install dependencies needed for linting
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
# Define Python versions as specified in tox.ini
python-version: ['3.9','3.10','3.11','3.12']
python-version: ['3.10','3.11','3.12','3.13']

steps:
- name: Check out repository code
Expand All @@ -41,18 +41,18 @@ jobs:
env:
PYTHONPATH: ${{ github.workspace }}
# Map GitHub Actions Python versions to tox environments
TOXENV: py${{ matrix.python-version == '3.9' && '39' || matrix.python-version == '3.10' && '310' || matrix.python-version == '3.11' && '311' || matrix.python-version == '3.12' && '312'}}
TOXENV: py${{ matrix.python-version == '3.10' && '310' || matrix.python-version == '3.11' && '311' || matrix.python-version == '3.12' && '312' || matrix.python-version == '3.13' && '313'}}

# Only run coverage checks on Python 3.9
# Only run coverage checks on Python 3.10
- name: Check coverage threshold
if: matrix.python-version == '3.9'
if: matrix.python-version == '3.10'
run: |
pip install coverage
coverage report --fail-under=60

# Only upload coverage report for Python 3.9
# Only upload coverage report for Python 3.10
- name: Upload coverage report
if: matrix.python-version == '3.9'
if: matrix.python-version == '3.10'
uses: actions/upload-artifact@v4
with:
name: coverage-report
Expand Down
2 changes: 1 addition & 1 deletion AUTHORING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ secops-wrapper/

### Prerequisites

- Python 3.9 or higher
- Python 3.10 or higher

### Setup

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.28.0] - 2025-12-10
### Updated
- Minimum python version support to 3.10 from 3.9 as python 3.9 has reached its end of life.

## [0.27.2] - 2025-12-08
### Updated
- Parser list method to handle pagination properly
Expand Down
2 changes: 1 addition & 1 deletion TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This guide provides comprehensive instructions for setting up and running tests

### Prerequisites

- Python 3.9+
- Python 3.10+
- Chronicle Instance Details:
- Customer ID
- Project Number
Expand Down
10 changes: 5 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ build-backend = "hatchling.build"

[project]
name = "secops"
version = "0.27.2"
version = "0.28.0"
description = "Python SDK for wrapping the Google SecOps API for common use cases"
readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.10"
license = "Apache-2.0"
authors = [
{ name = "Google SecOps Team", email = "[email protected]" }
Expand All @@ -18,10 +18,10 @@ classifiers = [
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Security",
]
dependencies = [
Expand Down
2 changes: 1 addition & 1 deletion src/secops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

__version__ = "0.1.2"

from secops.client import SecOpsClient
from secops.auth import SecOpsAuth
from secops.client import SecOpsClient

__all__ = ["SecOpsClient", "SecOpsAuth"]
37 changes: 19 additions & 18 deletions src/secops/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
"""Authentication handling for Google SecOps SDK."""

import sys
from collections.abc import Sequence
from dataclasses import asdict, dataclass, field
from http import HTTPStatus
from types import TracebackType
from typing import Any, Dict, List, Optional, Sequence, Union
from typing import Any

import google.auth
import google.auth.transport.requests
Expand Down Expand Up @@ -91,7 +92,7 @@ class RetryConfig:
)
backoff_factor: float = 0.3

def to_dict(self) -> Dict[str, Any]:
def to_dict(self) -> dict[str, Any]:
"""Convert the config to a dictionary for urllib3.Retry."""
return asdict(self)

Expand All @@ -105,12 +106,12 @@ class LogRetry(Retry):

def increment(
self,
method: Optional[str] = None,
url: Optional[str] = None,
response: Optional[BaseHTTPResponse] = None,
error: Optional[Exception] = None,
_pool: Optional[ConnectionPool] = None,
_stacktrace: Optional[TracebackType] = None,
method: str | None = None,
url: str | None = None,
response: BaseHTTPResponse | None = None,
error: Exception | None = None,
_pool: ConnectionPool | None = None,
_stacktrace: TracebackType | None = None,
) -> Retry:
"""Return a new Retry object with incremented retry counters and logs
retry attempt.
Expand Down Expand Up @@ -148,12 +149,12 @@ class SecOpsAuth:

def __init__(
self,
credentials: Optional[Credentials] = None,
service_account_path: Optional[str] = None,
service_account_info: Optional[Dict[str, Any]] = None,
impersonate_service_account: Optional[str] = None,
scopes: Optional[List[str]] = None,
retry_config: Optional[Union[RetryConfig, Dict[str, Any], bool]] = None,
credentials: Credentials | None = None,
service_account_path: str | None = None,
service_account_info: dict[str, Any] | None = None,
impersonate_service_account: str | None = None,
scopes: list[str] | None = None,
retry_config: RetryConfig | dict[str, Any] | bool | None = None,
):
"""Initialize authentication for SecOps.

Expand All @@ -179,10 +180,10 @@ def __init__(

def _get_credentials(
self,
credentials: Optional[Credentials],
service_account_path: Optional[str],
service_account_info: Optional[Dict[str, Any]],
impersonate_service_account: Optional[str],
credentials: Credentials | None,
service_account_path: str | None,
service_account_info: dict[str, Any] | None,
impersonate_service_account: str | None,
) -> Credentials:
"""Get credentials from various sources."""
try:
Expand Down
15 changes: 8 additions & 7 deletions src/secops/chronicle/alert.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
"""Alert functionality for Chronicle."""

import json
import re
import time
from datetime import datetime, timezone
from typing import Dict, Any, Optional
from typing import Any

from secops.exceptions import APIError
import re


def _fix_json_formatting(data):
Expand Down Expand Up @@ -54,13 +55,13 @@ def get_alerts(
client,
start_time: datetime,
end_time: datetime,
snapshot_query: Optional[str] = 'feedback_summary.status != "CLOSED"',
baseline_query: Optional[str] = None,
max_alerts: Optional[int] = 1000,
enable_cache: Optional[bool] = None,
snapshot_query: str | None = 'feedback_summary.status != "CLOSED"',
baseline_query: str | None = None,
max_alerts: int | None = 1000,
enable_cache: bool | None = None,
max_attempts: int = 30,
poll_interval: float = 1.0,
) -> Dict[str, Any]:
) -> dict[str, Any]:
"""Get alerts from Chronicle.

This function uses the legacy:legacyFetchAlertsView endpoint to retrieve
Expand Down
21 changes: 11 additions & 10 deletions src/secops/chronicle/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,23 @@
#
"""Case functionality for Chronicle."""

from typing import Dict, Any, List, Optional
from datetime import datetime
from typing import Any

from secops.chronicle.models import Case, CaseList
from secops.exceptions import APIError
from secops.chronicle.models import CaseList, Case


def get_cases(
client,
start_time: Optional[datetime] = None,
end_time: Optional[datetime] = None,
start_time: datetime | None = None,
end_time: datetime | None = None,
page_size: int = 100,
page_token: Optional[str] = None,
case_ids: Optional[List[str]] = None,
asset_identifiers: Optional[List[str]] = None,
tenant_id: Optional[str] = None,
) -> Dict[str, Any]:
page_token: str | None = None,
case_ids: list[str] | None = None,
asset_identifiers: list[str] | None = None,
tenant_id: str | None = None,
) -> dict[str, Any]:
"""Get case data from Chronicle.

Args:
Expand Down Expand Up @@ -93,7 +94,7 @@ def get_cases(
raise APIError(f"Failed to parse cases response: {str(e)}") from e


def get_cases_from_list(client, case_ids: List[str]) -> CaseList:
def get_cases_from_list(client, case_ids: list[str]) -> CaseList:
"""Get cases from Chronicle.

Args:
Expand Down
Loading
Loading