diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 7c1d93c6..83113839 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -8,14 +8,54 @@ on: jobs: build: runs-on: ubuntu-latest - # environment: release + # environment: release # uncomment to require manual approval before publishing to PyPI (recommended for extra protection) permissions: - id-token: write + contents: write # needed to upload SBOM files as release assets via `gh release upload` + id-token: write # needed for PyPI Trusted Publishing (OIDC) via pypa/gh-action-pypi-publish steps: - name: Checkout uses: actions/checkout@v6 + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.x' + cache: 'pip' + + - name: Upgrade pip + run: python -m pip install --upgrade pip + + - name: Install requirements + run: | + python -m pip install -r requirements.txt -r requirements-release.txt + python -m pip install ".[all]" + + - name: Build package + run: python -m build + + - name: Generate SBOM + run: | + cyclonedx-py environment \ + --pyproject pyproject.toml \ + --mc-type library \ + --of JSON \ + -o sbom.cyclonedx.json + cyclonedx-py environment \ + --pyproject pyproject.toml \ + --mc-type library \ + --of XML \ + -o sbom.cyclonedx.xml + + - name: Generate license report + run: | + pip-licenses \ + --ignore-packages build cyclonedx-bom cyclonedx-py pip-licenses \ + --with-authors --with-urls --format=csv --output-file=licenses.csv + pip-licenses \ + --ignore-packages build cyclonedx-bom cyclonedx-py pip-licenses \ + --with-authors --with-urls --format=markdown --output-file=licenses.md + - name: Extract release notes id: extract-release-notes uses: ffurrer2/extract-release-notes@v3 @@ -26,17 +66,16 @@ jobs: body: ${{ steps.extract-release-notes.outputs.release_notes }} token: ${{ secrets.WORKFLOWS_CREATE_RELEASE_TOKEN }} - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: '3.x' - cache: 'pip' - - - name: Build Package + - name: Upload release assets run: | - python -m pip install --upgrade pip - python -m pip install build - python -m build + gh release upload ${{ github.ref_name }} \ + sbom.cyclonedx.json \ + sbom.cyclonedx.xml \ + licenses.csv \ + licenses.md \ + --clobber + env: + GH_TOKEN: ${{ secrets.WORKFLOWS_CREATE_RELEASE_TOKEN }} - name: Publish on PyPI uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml index 211ef052..7ba4e657 100644 --- a/.github/workflows/pre-commit-autoupdate.yml +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/setup-python@v6 with: python-version: '3.x' - - uses: browniebroke/pre-commit-autoupdate-action@main + - uses: browniebroke/pre-commit-autoupdate-action@v1 - uses: peter-evans/create-pull-request@v8 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 00000000..5003aec1 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,37 @@ +name: OpenSSF Scorecard + +on: + push: + branches: [main] + schedule: + - cron: '0 8 * * 6' # every Saturday at 08:00 UTC + workflow_dispatch: + +permissions: read-all + +jobs: + scorecard: + runs-on: ubuntu-latest + permissions: + security-events: write # needed to upload SARIF results to GitHub Security tab + id-token: write # needed for Scorecard's OIDC token + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Run Scorecard + uses: ossf/scorecard-action@v2 + with: + results_file: scorecard-results.sarif + results_format: sarif + publish_results: true + + - name: Upload results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: scorecard-results.sarif + category: scorecard diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml index baf6da13..f1914d44 100644 --- a/.github/workflows/test-package.yml +++ b/.github/workflows/test-package.yml @@ -49,7 +49,7 @@ jobs: coverage xml -o ./coverage.xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: false diff --git a/.gitignore b/.gitignore index f113ccb4..fc864d2e 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,8 @@ dmypy.json # custom TODO.txt test-coverage.sh + +# generated at release time, not committed +sbom.cyclonedx.* +licenses.csv +licenses.md diff --git a/README.md b/README.md index e918418d..8530c1ed 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ [![](https://img.shields.io/scrutinizer/quality/g/fabiocaccamo/python-benedict?logo=scrutinizer)](https://scrutinizer-ci.com/g/fabiocaccamo/python-benedict/?branch=main) [![](https://img.shields.io/badge/code%20style-black-000000.svg?logo=python&logoColor=black)](https://github.com/psf/black) [![](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) +[![](https://img.shields.io/badge/security-policy-blue?logo=github)](https://github.com/fabiocaccamo/python-benedict/blob/main/SECURITY.md) +[![](https://img.shields.io/badge/SBOM-CycloneDX-blue?logo=dependabot)](https://github.com/fabiocaccamo/python-benedict/releases/latest) +[![](https://api.securityscorecards.dev/projects/github.com/fabiocaccamo/python-benedict/badge)](https://scorecard.dev/viewer/?uri=github.com/fabiocaccamo/python-benedict) # python-benedict python-benedict is a dict subclass with **keylist/keypath/keyattr** support, **I/O** shortcuts (`base64`, `cli`, `csv`, `html`, `ini`, `json`, `pickle`, `plist`, `query-string`, `toml`, `xls`, `xml`, `yaml`) and many **utilities**... for humans, obviously. @@ -44,6 +47,7 @@ python-benedict is a dict subclass with **keylist/keypath/keyattr** support, **I - [I/O methods](#io-methods) - [Parse methods](#parse-methods) - [Testing](#testing) +- [Security](#security) - [License](#license) ## Installation @@ -1030,6 +1034,13 @@ tox python -m unittest ``` +## Security + +- **SBOM** — a Software Bill of Materials in [CycloneDX](https://cyclonedx.org/) format (JSON and XML) is generated and published as a release asset on every release. You can download it from the [Releases](https://github.com/fabiocaccamo/python-benedict/releases/latest) page. +- **Trusted Publishing** — packages are published to PyPI via [OIDC Trusted Publishing](https://docs.pypi.org/trusted-publishers/), without long-lived secrets. +- **OpenSSF Scorecard** — the repository is evaluated weekly against the [OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/fabiocaccamo/python-benedict) checks; results are visible in the GitHub Security tab. +- **Reporting** — to report a vulnerability, please follow the [Security Policy](SECURITY.md). + ## License Released under [MIT License](LICENSE.txt). diff --git a/SECURITY.md b/SECURITY.md index e6b4772c..6e13845a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,13 +2,21 @@ ## Supported Versions -Keep this library updated to the latest version. +Only the latest stable release receives security fixes. | Version | Supported | | ------- | ------------------ | | latest | :white_check_mark: | -| oldest | :x: | +| older | :x: | ## Reporting a Vulnerability -Open an issue. +**Please do not open a public GitHub issue for security vulnerabilities.** + +Use GitHub's private [Report a vulnerability](https://github.com/fabiocaccamo/python-benedict/security/advisories/new) feature. You will receive a response within 7 days, and a fix will be released as soon as possible depending on severity. + +## Supply Chain Security + +- **SBOM** — a Software Bill of Materials in [CycloneDX](https://cyclonedx.org/) format (JSON and XML) is attached to every [release](https://github.com/fabiocaccamo/python-benedict/releases/latest) as `sbom.cyclonedx.json` / `sbom.cyclonedx.xml`. +- **License report** — a full dependency license inventory (`licenses.csv` / `licenses.md`) is also attached to every release. +- **Trusted Publishing** — packages are published to PyPI via [OIDC Trusted Publishing](https://docs.pypi.org/trusted-publishers/), without storing long-lived API tokens. diff --git a/requirements-release.txt b/requirements-release.txt new file mode 100644 index 00000000..575462a3 --- /dev/null +++ b/requirements-release.txt @@ -0,0 +1,3 @@ +build == 1.4.* +cyclonedx-bom == 7.3.* +pip-licenses == 5.5.*