cra-scan
SBOM + exploited-vulnerability check for Swift / Apple-platform projects, built for the EU Cyber Resilience Act (CRA). The open-source core of Pinwatch.
- Reads Swift Package Manager
Package.resolved(formats v1–v3) and CocoaPodsPodfile.lock - Writes a CycloneDX 1.6 SBOM
- Checks every pinned Swift package against OSV, and every CocoaPods pod against OSV (via its source repo) and NVD (by CPE), and flags vulnerabilities that CISA KEV or ENISA's EUVD list as exploited, plus EPSS exploit probability
- Pure Python 3.9+, no dependencies, no account, no telemetry
Install & run
pipx install cra-scan # or: pip install cra-scan
cra-scan scan path/to/MyApp --product "My App" --product-version 1.4 --supplier "My Studio"
cra-scan check sbom.cdx.json --format markdown # re-check an existing SBOM
Exit codes: 0 ok · 1 findings at/above --fail-on (default exploited) · 2 input error · 3 feed unreachable.
Levels: exploited (CISA KEV or ENISA EUVD lists it as exploited — if your product is affected, CRA Art. 14 reporting applies: 24 h early warning, 72 h notification, final report within 14 days of a fix) · high (EPSS ≥ 0.10 or CVSS ≥ 9.0) · known (any published advisory for the pinned version).
Example (a demo project pinning libwebp 1.2.0 and SSZipArchive 2.1.0 through CocoaPods, and swift-nio 2.41.0):
cra-scan 0.2.0 · Demo: 8 components, 8 checked, worst: EXPLOITED
[EXPLOITED] libwebp 1.2.0: CVE-2023-4863 epss=1.000 fixed in 1.3.2
Heap buffer overflow in libwebp in Google Chrome prior to 116.0.5845.187 and libwebp 1.3.2 allowed a remote at
[KNOWN ] SSZipArchive 2.1.0: CVE-2022-36943 epss=0.009
SSZipArchive versions 2.5.3 and older contain an arbitrary file write vulnerability due to lack of sanitizatio
[KNOWN ] swift-nio 2.41.0: GHSA-7fj7-39wj-c64f CVE-2022-3215 epss=0.006 fixed in 2.42.0
...
! Actively exploited vulnerability found. If your product is affected, CRA Art. 14 reporting applies:
24h early warning / 72h notification via ENISA's Single Reporting Platform. Record when you became aware.
Coverage
- Swift packages — OSV's
SwiftURLecosystem (GitHub advisory database), by version or, for branch pins, by commit. - CocoaPods pods — there is no advisory feed for pods, so cra-scan reads each pod's podspec from the CocoaPods CDN
to find its source repo, then checks (1) OSV for that repo, which catches pods that are also Swift packages, and
(2) NVD for CVEs whose CPE names the pod or its repo at the pinned version. When the CPE vendor matches the pod's
repo owner (e.g.
webmproject↔webm/libwebp) the finding counts normally. A match by product name only is shown asverifyand capped atknown, so a name collision never fails your build. - NVD allows 5 requests per 30 s without a key, so the first CocoaPods scan takes ~10 s per pod. Results are cached for
24 h; set
NVD_API_KEY(free) for ~10× faster first runs, or--no-nvdto skip it. - Pods from a
:gitor:pathsource, Carthage and binary frameworks are listed in the SBOM only.
Feeds are cached in ~/.cache/cra-scan (CRA_SCAN_CACHE overrides): KEV/EUVD 12 h, NVD 24 h, podspecs 30 days.
GitHub Action
- uses: actions/checkout@v4
- uses: stavrop/cra-scan@v0
with: {product: "My App", version: "1.4", fail-on: exploited}
The report is added to the job summary; output worst = none | known | high | exploited. The cache is kept between
runs with actions/cache; pass nvd-api-key: ${{ secrets.NVD_API_KEY }} to speed up CocoaPods checks.
Development
python3 -m unittest discover -s tests
Licence: Apache-2.0. Not legal advice — you remain the manufacturer responsible for your product's conformity.
Metadata
Release files for cra-scan 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cra_scan-0.2.0.tar.gz | 18.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cra_scan-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 35.9 kB
Release files / cra_scan-0.2.0.tar.gz
| Download URL | cra_scan-0.2.0.tar.gz |
|---|---|
| Size | 18.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
831c46e6255cf4edfc184ba3bf008a2562ac8ee53935d1d7b0099cb5f584d28d
|
|
BLAKE2b-256 checksum How to use checksums |
e1ee430dfa96f06b0b035a8be37dbe102dd33e0dc2df68f1f2a915bc46f2083d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency logRelease files / cra_scan-0.2.0-py3-none-any.whl
| Download URL | cra_scan-0.2.0-py3-none-any.whl |
|---|---|
| Size | 17.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
36738fd3cf445c71f33f03b5f048decba5245777f468c4d8ebc5d7912e58c6d9
|
|
BLAKE2b-256 checksum How to use checksums |
8cc0a1ecc750abe57fae39904a6a63b5e88f42b15f6e8990a820514dd8fd1db1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency log