Metadata-Version: 2.4
Name: blackshield-sdk
Version: 0.2.2
Summary: Official Python SDK for the BlackShield Security Platform
Project-URL: Homepage, https://blackshield.chaplau.com
Project-URL: Documentation, https://blackshield.chaplau.com/en/docs
Author-email: BlackShield Security <contact@chaplau.com>
License-Expression: Apache-2.0
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx<1,>=0.27
Requires-Dist: pydantic<3,>=2.0
Provides-Extra: dev
Requires-Dist: mypy>=1.13; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.22; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Description-Content-Type: text/markdown

# BlackShield Python SDK

Official Python SDK for the [BlackShield Security Platform](https://blackshield.chaplau.com).

## Installation

```bash
pip install blackshield-sdk
```

## Quick Start

```python
import blackshield

client = blackshield.BlackShield(api_key="bsk_live_...")

# List critical findings
page = client.findings.list(severity=["critical", "high"], page_size=10)
for f in page.items:
    print(f"{f.severity.value} | {f.title}")

# Get a specific finding
finding = client.findings.get("550e8400-e29b-41d4-a716-446655440000")

# Update finding status
client.findings.update_status(finding.id, status="in_progress", reason="Investigating")
```

## Async Usage

```python
import asyncio
from blackshield import AsyncBlackShield

async def main():
    async with AsyncBlackShield(api_key="bsk_live_...") as client:
        page = await client.findings.list(severity=["critical"])
        for f in page.items:
            print(f.title)

asyncio.run(main())
```

## Available Resources

| Resource | Description | Example |
|----------|-------------|---------|
| `client.findings` | List, get, update findings | `client.findings.list(severity=["critical"])` |
| `client.ingestion` | Ingest findings & assets | `client.ingestion.ingest_findings([...])` |
| `client.dashboard` | Stats, trends, usage | `client.dashboard.stats()` |
| `client.compliance` | Compliance overview & controls | `client.compliance.overview(framework="SOC2")` |
| `client.reports` | Summaries & CSV/PDF exports | `client.reports.export_findings_csv()` |
| `client.easm` | External attack surface | `client.easm.overview()` |
| `client.threat_intel` | Blocklist & indicator lookup | `client.threat_intel.lookup(indicator="1.2.3.4")` |
| `client.connectors` | Connector health monitoring | `client.connectors.freshness()` |
| `client.api_keys` | API key management | `client.api_keys.create(label="CI")` |
| `client.policies` | Policy guardrails & custom Rego | `client.policies.list_packs()` |
| `client.security_reviews` | Guided security reviews & templates | `client.security_reviews.get_default_template()` |
| `client.audit` | Tenant audit logs & evidence export | `client.audit.query(limit=50)` |
| `client.alerts` | Alerts & alert sync integrations | `client.alerts.list(severity=["critical"])` |
| `client.identity` | Identity, OIDC & mapping configs | `client.identity.list_oidc()` |
| `client.remediation` | Remediation & compensating controls | `client.remediation.get_policy("default")` |
| `client.ai_gateway` | AI Gateway client & usage logs | `client.ai_gateway.list_clients()` |
| `client.ai_assets` | AI asset inventory & ingestion | `client.ai_assets.list(approval_status="unapproved")` |
| `client.ai_integrations` | Tenant AI provider configuration | `client.ai_integrations.preflight("openai", **config)` |
| `client.assertions` | Security assertion suites & runs | `client.assertions.activate(suite_id)` |
| `client.customer_notifications` | Notification templates & delivery runs | `client.customer_notifications.runs()` |
| `client.dast` | Bounded DAST scans & findings | `client.dast.get_scan(job_id)` |
| `client.oauth_apps` | OAuth app and grant governance | `client.oauth_apps.grants(status="pending_review")` |
| `client.threat_modeling` | Hosted threat-model sessions & exports | `client.threat_modeling.create_session(title="Review")` |

Version 0.2.2 preserves the 0.2.0 surface and accepts both the legacy `job_id`
and current `id` ingestion job status fields. It also includes the 0.2.1 durable
EASM verification outcomes, explicit challenge rotation, and synchronous/asynchronous
AI provider configuration deletion. Version 0.2.0 added EASM asset-verification operations, AI Gateway client
updates/deactivation, OAuth token exchange and governed chat completions. Reports
v2 and Threat Intelligence v2 are part of the supported public surface. All new
resources have matching synchronous and asynchronous implementations.

Official packages are released only by the Jenkins `Publish SDK` stage after
the Python 3.10–3.13 matrix and repository security gates pass. The stage
publishes an immutable PyPI version and fingerprints its wheel checksum; it
does not depend on a GitHub workflow, release, or tag.

## Ingestion

```python
from blackshield.types.ingestion import UnifiedFinding, AffectedResource, SourceMeta

finding = UnifiedFinding(
    finding_type="vulnerability",
    title="CVE-2024-1234 in openssl 3.0.8",
    severity="critical",
    cve_id="CVE-2024-1234",
    affected_resource=AffectedResource(
        resource_type="container_image",
        resource_id="sha256:abc123",
        resource_name="myapp:latest",
    ),
    source=SourceMeta(scanner="trivy", scanner_version="0.50.0"),
    dedup_key="vuln:CVE-2024-1234:sha256:abc123",
)

result = client.ingestion.ingest_findings([finding])
print(f"Job ID: {result.job_id}, Status: {result.status}")

# Poll for completion
job = client.ingestion.get_job(result.job_id)
print(f"Created: {job.created}, Updated: {job.updated}")
```

## Error Handling

```python
import blackshield

try:
    finding = client.findings.get("nonexistent-id")
except blackshield.NotFoundError:
    print("Finding not found")
except blackshield.RateLimitError as e:
    print(f"Rate limited, retry after {e.retry_after}s")
except blackshield.AuthenticationError:
    print("Invalid API key")
except blackshield.BlackShieldError as e:
    print(f"API error: {e}")
```

## Configuration

```python
client = blackshield.BlackShield(
    api_key="bsk_live_...",
    base_url="https://api.blackshield.chaplau.com",  # or self-hosted URL
    timeout=60.0,       # request timeout in seconds
    max_retries=3,       # automatic retries on 429/5xx
)
```

## Development

```bash
cd sdk
pip install -e ".[dev]"
pytest -v --cov=blackshield
ruff check .
mypy .
```
