Metadata-Version: 2.4
Name: synapse-sbom
Version: 0.1.0
Summary: SYNAPSE SBOM scanner for Python projects — generate a CycloneDX SBOM locally and submit it to SYNAPSE Software Component Analysis.
Project-URL: Homepage, https://github.com/vitorallo/synapse/tree/main/tools/sbom-scanner-py#readme
Project-URL: Repository, https://github.com/vitorallo/synapse
Project-URL: Issues, https://github.com/vitorallo/synapse/issues
Author-email: PeachStudio <me@vitorallo.com>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: cyclonedx,pypi,sbom,sca,supply-chain,synapse,vulnerability
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# synapse-sbom (Python)

> Command: **`synapse-sbom`** · PyPI package: **`synapse-sbom`** · the
> Python twin of the npm [`@peachstudio/synapse-sbom`](https://www.npmjs.com/package/@peachstudio/synapse-sbom)

Generate a [CycloneDX 1.6](https://cyclonedx.org/) SBOM from your Python
project **locally** and submit it to **SYNAPSE Software Component
Analysis** (SCA). Vulnerable components are then **continuously
re-evaluated** as new advisories land — *scan once, monitored forever*.

- **Zero runtime dependencies** — Python ≥ 3.9, stdlib only.
- The SBOM is built on your machine; only the SBOM JSON leaves it.
- Same `/v1/sca/analyze` contract, **shared config & project identity**
  with the npm scanner — a polyglot repo is the **same product**
  whichever scanner runs.

---

## Quick start

```bash
# 1. Create an API key in the SYNAPSE webapp:
#    Settings → Security → API Keys → name it → Create → copy the syn_… key

# 2. Store it once (shared with the npm scanner: ~/.config/synapse-sbom/config.json, 0600):
uvx synapse-sbom login --key syn_xxxxxxxxxxxx
#   (or: pipx run synapse-sbom login --key syn_xxx)

# 3. From your project root:
uvx synapse-sbom scan
```

Install it instead of using `uvx`/`pipx run` if you prefer:

```bash
uv tool install synapse-sbom        # → `synapse-sbom` on PATH
pipx install synapse-sbom           # same
pip install synapse-sbom            # into the current environment
```

`uvx synapse-sbom` / `pipx run synapse-sbom` is the `npx` analog — no
install needed.

---

## What it reads

To build the component list (first match wins — closest to the npm
tool's lockfile-first approach):

1. **`poetry.lock`** or **`uv.lock`** (TOML; needs Python ≥ 3.11 for the
   stdlib `tomllib`, otherwise skipped)
2. **`requirements.txt`** (pinned `name==version` lines)
3. else the **active environment** — every installed distribution via
   `importlib.metadata` (zero-config, reliable; run it in your project's
   venv)

Product name/version come from `pyproject.toml` `[project]` (or
`[tool.poetry]`), else the directory name. Components are emitted as
`pkg:pypi/<normalized-name>@<version>`. Anything the resolver doesn't
support server-side is reported `skipped`, never fatal.

---

## Authentication (API key)

1. SYNAPSE webapp → **Settings → Security → API Keys** (Business or
   Enterprise tier). Name it, **Create**, copy the `syn_…` key — it is
   shown **once**.
2. Give it to the scanner — precedence **flag → env → stored config**:

| Method | How | Best for |
|---|---|---|
| Flag | `--key syn_…` | one-off / overrides |
| Env | `SYNAPSE_API_KEY=syn_…` | CI (use a secret) |
| Stored | `synapse-sbom login --key syn_…` → `~/.config/synapse-sbom/config.json` (`0600`) | local dev |

The config file and the `.synapse-sbom.json` project marker are the
**same** as the npm scanner's — `login` once, use either tool. The key
is never written to the project, the SBOM, or `.synapse-sbom.json`.

---

## Command reference

Copy-paste, no install needed (`uvx`; or `pipx run`, or drop the prefix
if installed):

```bash
# Authenticate once (shared 0600 config)
uvx synapse-sbom login --key syn_xxx --url https://api.synapse-intel.com
uvx synapse-sbom login                       # interactive hidden prompt

# Scan
uvx synapse-sbom scan                        # current project, submit
uvx synapse-sbom scan ./path/to/app          # a specific project
uvx synapse-sbom scan --dry-run              # print SBOM only, no upload, no side effects
uvx synapse-sbom scan --product my-svc       # override display name
uvx synapse-sbom scan --project my-key       # explicit stable project key

# Per-run auth/endpoint overrides (skip stored config)
uvx synapse-sbom scan --url https://api.synapse-intel.com --key syn_xxx
SYNAPSE_API_KEY=syn_xxx SYNAPSE_API_URL=https://api.synapse-intel.com \
  uvx synapse-sbom scan

# Local SYNAPSE instead of prod
uvx synapse-sbom scan --url http://localhost:8085 --key syn_local

# Inspect resolved URL / masked key / config path
uvx synapse-sbom whoami

# Help
uvx synapse-sbom help
```

| Flag | Env | Default | Meaning |
|---|---|---|---|
| `--key` | `SYNAPSE_API_KEY` | stored config | Bearer API key |
| `--url` | `SYNAPSE_API_URL` | `https://api.synapse-intel.com` | SCA API base URL |
| `--project` | `SYNAPSE_PROJECT` | git remote → generated | Stable project identity (survives renames/CI) |
| `--product` | — | `pyproject.toml` name | Display name only |
| `--dry-run` | — | off | Build SBOM, print to stdout, **don't** upload or write `.synapse-sbom.json` |
| — | `SYNAPSE_TIMEOUT_MS` | `1200000` (20 min) | Client request timeout |
| — | `SYNAPSE_SBOM_CONFIG_DIR` | `~/.config/synapse-sbom` | Config directory (shared with npm tool) |

**Resolution order** (key & URL): `flag` > `env` > stored config > default.

---

## Project identity (re-scans update the same product)

Same rules and **same `.synapse-sbom.json` format** as the npm tool, so
a project scanned by either resolves to one product:

1. `--project` / `SYNAPSE_PROJECT`
2. committed `.synapse-sbom.json` — `{ "projectId": "…" }`
3. the git remote URL, normalized (`git:github.com/org/repo`)
4. else a generated UUID written to `.synapse-sbom.json` — **commit it**.

Server identity is `(your account, project key)`; the product name is a
mutable label. `--dry-run` reports the key without writing the file.

---

## Use in CI

```yaml
# .github/workflows/sca.yml
name: SBOM SCA
on: [push]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.12" }
      - run: pip install -r requirements.txt   # so the env reflects deps
      - run: uvx synapse-sbom scan
        env:
          SYNAPSE_API_KEY: ${{ secrets.SYNAPSE_API_KEY }}
```

Commit `.synapse-sbom.json` so every run reports as the same product.
Exit `0` on success, `1` on any error (fail the build as you see fit).

---

## Security

The SBOM is built locally; only it (+ a Bearer key over HTTPS) leaves
your machine. The key is stored `0600`, never written to the project,
the SBOM, or `.synapse-sbom.json`, and never printed (the CLI **warns**
if you point `--url` at a plaintext `http://` non-local host;
`http://localhost` and `https://` are silent). Zero third-party
dependencies — no transitive supply-chain surface.

---

## Troubleshooting

| Symptom | Cause / fix |
|---|---|
| `(N components, from environment)` but you expected lockfile | No `poetry.lock`/`uv.lock`/`requirements.txt` found, or Python < 3.11 for TOML locks. Run in the project venv, or add a pinned `requirements.txt`. |
| `no API key …` | `login`, or pass `--key` / `SYNAPSE_API_KEY`. |
| `API 401` | Key invalid/revoked, or wrong `--url`. Check `synapse-sbom whoami`. |
| `API 403` | Your tier doesn't include SCA (Business/Enterprise), or API access not enabled in webapp settings. |
| `API 413` | SBOM exceeds the server component cap. |
| `timed out after …s` | Large project still resolving. Re-run later (results cache) or raise `SYNAPSE_TIMEOUT_MS`. |

---

## Family

`synapse-sbom` ships for **npm** (`@peachstudio/synapse-sbom`, published)
and **PyPI** (this package). A **Go** scanner will follow the same
contract — generate a CycloneDX 1.6 SBOM locally, submit to
`/v1/sca/analyze`. The SCA engine is ecosystem-agnostic, so a CycloneDX
file from any tool works today.
