From e99a54ae7e97684530ff5c4b5a205292ec2e087d Mon Sep 17 00:00:00 2001 From: dzeusking-dev Date: Fri, 4 Sep 2026 15:25:56 +0200 Subject: [PATCH 1/2] chore: add OpenSSF trust baseline Signed-off-by: Bartosz Joachimiak --- .bestpractices.json | 32 +++ .github/workflows/codeql.yml | 28 +++ .github/workflows/dependency-review.yml | 19 ++ .github/workflows/scorecard.yml | 42 ++++ GOVERNANCE.md | 5 + MAINTAINERS.md | 7 + README.md | 256 ++++++++++++------------ SUPPORT.md | 5 + THREAT_MODEL.md | 7 + docs/SECURITY-TRUST.md | 11 + security-insights.yml | 57 ++++++ 11 files changed, 342 insertions(+), 127 deletions(-) create mode 100644 .bestpractices.json create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/scorecard.yml create mode 100644 GOVERNANCE.md create mode 100644 MAINTAINERS.md create mode 100644 SUPPORT.md create mode 100644 THREAT_MODEL.md create mode 100644 docs/SECURITY-TRUST.md create mode 100644 security-insights.yml diff --git a/.bestpractices.json b/.bestpractices.json new file mode 100644 index 0000000..40c753a --- /dev/null +++ b/.bestpractices.json @@ -0,0 +1,32 @@ +{ + "osps_do_01_01": "Met", + "osps_do_01_01_justification": "Installation and CLI usage are documented in https://github.com/gexiro-global/csaf-check#install", + "osps_do_02_01": "Met", + "osps_do_02_01_justification": "Defects use repository issue templates and https://github.com/gexiro-global/csaf-check/blob/main/SUPPORT.md", + "osps_gv_02_01": "Met", + "osps_gv_02_01_justification": "Public issues and pull requests are enabled.", + "osps_gv_03_01": "Met", + "osps_gv_03_01_justification": "See https://github.com/gexiro-global/csaf-check/blob/main/CONTRIBUTING.md", + "osps_le_02_01": "Met", + "osps_le_02_01_justification": "Apache-2.0 source license.", + "osps_le_02_02": "Met", + "osps_le_02_02_justification": "Release packaging includes the Apache-2.0 license.", + "osps_le_03_01": "Met", + "osps_le_03_01_justification": "See https://github.com/gexiro-global/csaf-check/blob/main/LICENSE", + "osps_le_03_02": "Met", + "osps_le_03_02_justification": "The license is included in source distributions and wheels.", + "osps_qa_01_01": "Met", + "osps_qa_01_01_justification": "Canonical public source: https://github.com/gexiro-global/csaf-check", + "osps_qa_01_02": "Met", + "osps_qa_01_02_justification": "GitHub publishes the repository commit history.", + "osps_qa_02_01": "Met", + "osps_qa_02_01_justification": "Direct dependencies are declared in pyproject.toml.", + "osps_qa_04_01": "N/A", + "osps_qa_04_01_justification": "csaf-check is a single-repository project.", + "osps_qa_05_01": "Met", + "osps_qa_05_01_justification": "Generated executables are built in CI and not committed.", + "osps_qa_05_02": "Met", + "osps_qa_05_02_justification": "The repository contains source and reviewable data files, not executable binaries.", + "osps_vm_02_01": "Met", + "osps_vm_02_01_justification": "See https://github.com/gexiro-global/csaf-check/blob/main/SECURITY.md" +} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..7eb0241 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,28 @@ +name: CodeQL + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: "17 4 * * 1" + +permissions: + contents: read + security-events: write + +jobs: + analyze: + name: Analyze Python + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Initialize CodeQL + uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 + with: + languages: python + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..fb481f3 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,19 @@ +name: Dependency review + +on: + pull_request: + +permissions: + contents: read + +jobs: + review: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Review dependency changes + uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 + with: + fail-on-severity: high diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000..61d97e9 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,42 @@ +name: OpenSSF Scorecard + +on: + branch_protection_rule: + schedule: + - cron: "31 5 * * 3" + push: + branches: [main] + +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read + security-events: write + id-token: write + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Run OpenSSF Scorecard + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + - name: Preserve SARIF result + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: openssf-scorecard-sarif + path: results.sarif + retention-days: 5 + if-no-files-found: error + - name: Upload SARIF to code scanning + uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 + with: + sarif_file: results.sarif diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 0000000..31b4cab --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,5 @@ +# Governance + +csaf-check is maintained by Gexiro Global Enterprises Ltd. The maintainer reviews issues and pull requests, decides scope and releases, and may reject changes that weaken the read-only, authorization-scoped or fail-explicit design. + +Changes are proposed through GitHub pull requests and must pass CI. The current single-maintainer structure is disclosed in [MAINTAINERS.md](MAINTAINERS.md); no independent review, response-time guarantee or certification is claimed. diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 0000000..ba56d32 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,7 @@ +# Maintainers + +| Maintainer | Role | Contact | +|---|---|---| +| `@dzeusking-dev` | Project owner and release maintainer | [GitHub](https://github.com/dzeusking-dev) | + +Security reports must use [SECURITY.md](SECURITY.md), not public issues. diff --git a/README.md b/README.md index 5d0e6f2..f58296e 100644 --- a/README.md +++ b/README.md @@ -1,129 +1,131 @@ -# csaf-check - -[![CI](https://github.com/gexiro-global/csaf-check/actions/workflows/ci.yml/badge.svg)](https://github.com/gexiro-global/csaf-check/actions/workflows/ci.yml) -[![PyPI](https://img.shields.io/pypi/v/csaf-check.svg)](https://pypi.org/project/csaf-check/) -[![Python](https://img.shields.io/pypi/pyversions/csaf-check.svg)](https://pypi.org/project/csaf-check/) +# csaf-check + +[![CI](https://github.com/gexiro-global/csaf-check/actions/workflows/ci.yml/badge.svg)](https://github.com/gexiro-global/csaf-check/actions/workflows/ci.yml) +[![PyPI](https://img.shields.io/pypi/v/csaf-check.svg)](https://pypi.org/project/csaf-check/) +[![Python](https://img.shields.io/pypi/pyversions/csaf-check.svg)](https://pypi.org/project/csaf-check/) [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) -Validate CSAF 2.0 advisories from Python, using the same validator Secvisogram runs — and get an -honest answer when that validator is not installed. - -## The problem - -If you publish security advisories as CSAF, you want schema validation in your release pipeline, -not in a browser tab at the end. The authoritative implementation of the CSAF 2.0 mandatory tests -is [`@secvisogram/csaf-validator-lib`](https://github.com/secvisogram/csaf-validator-lib), which is -JavaScript. Reimplementing the schema in Python means maintaining a second, subtly different -opinion about what "valid" means — and yours will be the wrong one. - -So this package does not reimplement anything. It bridges to the real validator and handles the -part that is annoying to get right: **what happens when the validator is not there.** - -## The contract - -`validate()` never raises. Not when Node is missing, not when the library is absent, not when the -validator times out or returns something unexpected. Each of those returns a result that says so: - -```python -from csaf_check import validate - -result = validate(advisory_dict) - -result.available # could the validator run at all? -result.is_valid # True / False / None when no verdict was reached -result.errors # one message per failed mandatory test -result.note # why there is no verdict, when there isn't -result.conclusive # available and a verdict exists -``` - -This matters because the alternative — a validator that throws on a missing optional dependency — -turns a quality gate into a hard dependency, and every caller ends up wrapping it in `try/except` -and swallowing real failures along with the boring ones. - -## Install - -```bash -pip install csaf-check -``` - -That gives you the Python API and CLI. For actual validation you also need Node.js and the -validator library: - -```bash -npm install @secvisogram/csaf-validator-lib -``` - -The **default is intentionally lenient**: without the JavaScript validator, `csaf-check` prints -`UNKNOWN - validator unavailable` and exits `0`, so CI environments without Node or the validator -do not break. Strict handling is an explicit opt-in: add `--require-validator` to make validator -unavailability exit `3`. - -The validator is found either next to the installed package or under `node_modules` in your current -working directory, so running `npm install` in your own project directory is enough — you do not -have to install it into `site-packages`. `NODE_PATH` is honoured as well. - -## CLI - -```bash -csaf-check examples/advisory-minimal.json # human-readable verdict -csaf-check examples/advisory-minimal.json --json # machine-readable -csaf-check - < examples/advisory-minimal.json # stdin -csaf-check examples/advisory-minimal.json --require-validator # fail if no verdict is possible -``` - -Exit codes: `0` valid (or inconclusive without `--require-validator`) — `1` invalid — -`2` unreadable or malformed input — `3` no verdict possible and `--require-validator` was set. - -## Python API - -```python -import json -from csaf_check import validate, validator_available - -if not validator_available(): - print("install Node.js to enable strict validation") - -with open("advisory.json", encoding="utf-8") as fh: - result = validate(json.load(fh)) - -if result.conclusive and not result.is_valid: - for message in result.errors: - print("FAIL", message) -``` - -## What this does NOT do - -- It does not generate CSAF documents. It only validates ones you already have. -- It runs the mandatory tests only, not the informative or optional profiles. -- It does not check that your advisory is *correct*, only that it is well-formed. A schema-valid - document can still describe the wrong product or the wrong affected range. -- It does not publish anything, sign anything, or talk to any network service. -- It is not legal advice about the EU Cyber Resilience Act. CRA is format-neutral and does not - mandate CSAF. CSAF is a good machine-readable choice for advisories and VEX, not a compliance - checkbox. - -## Examples - -`examples/advisory-minimal.json` is a small synthetic advisory for a fictional vendor using a -placeholder CVE identifier. `examples/advisory-invalid.json` is deliberately missing the mandatory -`tracking` object. Both exist to exercise this wrapper; neither describes a real product or a real -vulnerability. - -## Testing - -```bash -pip install -e ".[dev]" -pytest -q -``` - -The suite runs without Node.js on purpose — the degradation path is the part most likely to rot. - -## License - -Apache-2.0. See [LICENSE](LICENSE). - -Built and maintained by [Gexiro Global Enterprises Ltd](https://gexiro.com). - -Third-party attribution: see [NOTICE](NOTICE). - -Part of the [Gexiro open-source toolkit](https://github.com/gexiro-global). +[Security and trust evidence](docs/SECURITY-TRUST.md) documents the project's policies and automated checks. No certification or badge level is claimed. + +Validate CSAF 2.0 advisories from Python, using the same validator Secvisogram runs — and get an +honest answer when that validator is not installed. + +## The problem + +If you publish security advisories as CSAF, you want schema validation in your release pipeline, +not in a browser tab at the end. The authoritative implementation of the CSAF 2.0 mandatory tests +is [`@secvisogram/csaf-validator-lib`](https://github.com/secvisogram/csaf-validator-lib), which is +JavaScript. Reimplementing the schema in Python means maintaining a second, subtly different +opinion about what "valid" means — and yours will be the wrong one. + +So this package does not reimplement anything. It bridges to the real validator and handles the +part that is annoying to get right: **what happens when the validator is not there.** + +## The contract + +`validate()` never raises. Not when Node is missing, not when the library is absent, not when the +validator times out or returns something unexpected. Each of those returns a result that says so: + +```python +from csaf_check import validate + +result = validate(advisory_dict) + +result.available # could the validator run at all? +result.is_valid # True / False / None when no verdict was reached +result.errors # one message per failed mandatory test +result.note # why there is no verdict, when there isn't +result.conclusive # available and a verdict exists +``` + +This matters because the alternative — a validator that throws on a missing optional dependency — +turns a quality gate into a hard dependency, and every caller ends up wrapping it in `try/except` +and swallowing real failures along with the boring ones. + +## Install + +```bash +pip install csaf-check +``` + +That gives you the Python API and CLI. For actual validation you also need Node.js and the +validator library: + +```bash +npm install @secvisogram/csaf-validator-lib +``` + +The **default is intentionally lenient**: without the JavaScript validator, `csaf-check` prints +`UNKNOWN - validator unavailable` and exits `0`, so CI environments without Node or the validator +do not break. Strict handling is an explicit opt-in: add `--require-validator` to make validator +unavailability exit `3`. + +The validator is found either next to the installed package or under `node_modules` in your current +working directory, so running `npm install` in your own project directory is enough — you do not +have to install it into `site-packages`. `NODE_PATH` is honoured as well. + +## CLI + +```bash +csaf-check examples/advisory-minimal.json # human-readable verdict +csaf-check examples/advisory-minimal.json --json # machine-readable +csaf-check - < examples/advisory-minimal.json # stdin +csaf-check examples/advisory-minimal.json --require-validator # fail if no verdict is possible +``` + +Exit codes: `0` valid (or inconclusive without `--require-validator`) — `1` invalid — +`2` unreadable or malformed input — `3` no verdict possible and `--require-validator` was set. + +## Python API + +```python +import json +from csaf_check import validate, validator_available + +if not validator_available(): + print("install Node.js to enable strict validation") + +with open("advisory.json", encoding="utf-8") as fh: + result = validate(json.load(fh)) + +if result.conclusive and not result.is_valid: + for message in result.errors: + print("FAIL", message) +``` + +## What this does NOT do + +- It does not generate CSAF documents. It only validates ones you already have. +- It runs the mandatory tests only, not the informative or optional profiles. +- It does not check that your advisory is *correct*, only that it is well-formed. A schema-valid + document can still describe the wrong product or the wrong affected range. +- It does not publish anything, sign anything, or talk to any network service. +- It is not legal advice about the EU Cyber Resilience Act. CRA is format-neutral and does not + mandate CSAF. CSAF is a good machine-readable choice for advisories and VEX, not a compliance + checkbox. + +## Examples + +`examples/advisory-minimal.json` is a small synthetic advisory for a fictional vendor using a +placeholder CVE identifier. `examples/advisory-invalid.json` is deliberately missing the mandatory +`tracking` object. Both exist to exercise this wrapper; neither describes a real product or a real +vulnerability. + +## Testing + +```bash +pip install -e ".[dev]" +pytest -q +``` + +The suite runs without Node.js on purpose — the degradation path is the part most likely to rot. + +## License + +Apache-2.0. See [LICENSE](LICENSE). + +Built and maintained by [Gexiro Global Enterprises Ltd](https://gexiro.com). + +Third-party attribution: see [NOTICE](NOTICE). + +Part of the [Gexiro open-source toolkit](https://github.com/gexiro-global). diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 0000000..c39bdd9 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,5 @@ +# Support + +Use [GitHub Issues](https://github.com/gexiro-global/csaf-check/issues) for reproducible defects and usage questions. Include the package, Python and Node.js versions and a minimal synthetic CSAF document. Do not publish embargoed advisories, credentials or third-party confidential data. + +Security vulnerabilities belong in a private report under the process in [SECURITY.md](SECURITY.md). diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md new file mode 100644 index 0000000..ccd6757 --- /dev/null +++ b/THREAT_MODEL.md @@ -0,0 +1,7 @@ +# Threat model + +csaf-check parses attacker-controlled JSON and invokes a local Node.js validator. The primary risks are resource exhaustion, malformed validator output, executing an attacker-selected `node` from `PATH`, accidental disclosure of unpublished advisories and a misleading conclusive verdict when the validator is unavailable. + +The implementation passes the temporary path as a process argument without shell interpolation, bounds validator runtime, removes the temporary directory and represents validator absence explicitly. Operators must use a trusted runtime and dependencies, apply their own input-size limit, avoid untrusted or embargoed documents in shared CI logs and use `--require-validator` where an inconclusive result must fail. + +The project performs no network discovery and makes no claim that schema validity proves advisory correctness. diff --git a/docs/SECURITY-TRUST.md b/docs/SECURITY-TRUST.md new file mode 100644 index 0000000..4d50cb4 --- /dev/null +++ b/docs/SECURITY-TRUST.md @@ -0,0 +1,11 @@ +# Security and trust evidence + +This page is an evidence index, not a certification. The evidence does not prove the project is vulnerability-free, does not establish a SLSA level, and does not imply OpenSSF affiliation or endorsement. Tool output describes observed posture; it is not proof of compromise or absence of compromise. + +- [Security policy](../SECURITY.md) and [threat model](../THREAT_MODEL.md) +- [Contribution process](../CONTRIBUTING.md), [governance](../GOVERNANCE.md), [maintainers](../MAINTAINERS.md) and [support](../SUPPORT.md) +- CI tests both the no-validator degradation path and the packaged wheel with the real JavaScript validator. +- CodeQL, dependency review, Dependabot and OpenSSF Scorecard are configured in `.github/`. +- Third-party actions are pinned to immutable commit SHAs with version comments. + +The Scorecard badge is intentionally withheld until a successful default-branch run has produced a public API result. `.bestpractices.json` contains evidence-backed automation proposals only; it is not an OpenSSF Best Practices or OSPS Baseline claim. A human must review any badge submission. diff --git a/security-insights.yml b/security-insights.yml new file mode 100644 index 0000000..0b0abb1 --- /dev/null +++ b/security-insights.yml @@ -0,0 +1,57 @@ +header: + schema-version: 2.2.0 + last-updated: '2026-09-04' + last-reviewed: '2026-09-04' + url: https://raw.githubusercontent.com/gexiro-global/csaf-check/main/security-insights.yml + comment: This single-repository file reports current practices and makes no certification claim. +project: + name: csaf-check + homepage: https://github.com/gexiro-global/csaf-check + administrators: + - name: dzeusking-dev + affiliation: Gexiro Global Enterprises Ltd. + social: https://github.com/dzeusking-dev + primary: true + documentation: + quickstart-guide: https://github.com/gexiro-global/csaf-check#install + detailed-guide: https://github.com/gexiro-global/csaf-check/blob/main/docs/PIPELINE.md + code-of-conduct: https://github.com/gexiro-global/csaf-check/blob/main/CODE_OF_CONDUCT.md + support-policy: https://github.com/gexiro-global/csaf-check/blob/main/SUPPORT.md + repositories: + - name: csaf-check + url: https://github.com/gexiro-global/csaf-check + comment: Canonical source and release repository. + vulnerability-reporting: + reports-accepted: true + bug-bounty-available: false + policy: https://github.com/gexiro-global/csaf-check/blob/main/SECURITY.md +repository: + url: https://github.com/gexiro-global/csaf-check + status: active + accepts-change-request: true + accepts-automated-change-request: true + no-third-party-packages: false + core-team: + - name: dzeusking-dev + affiliation: Gexiro Global Enterprises Ltd. + social: https://github.com/dzeusking-dev + primary: true + documentation: + contributing-guide: https://github.com/gexiro-global/csaf-check/blob/main/CONTRIBUTING.md + review-policy: https://github.com/gexiro-global/csaf-check/blob/main/GOVERNANCE.md + security-policy: https://github.com/gexiro-global/csaf-check/blob/main/SECURITY.md + governance: https://github.com/gexiro-global/csaf-check/blob/main/GOVERNANCE.md + dependency-management-policy: https://github.com/gexiro-global/csaf-check/blob/main/docs/SECURITY-TRUST.md + license: + url: https://github.com/gexiro-global/csaf-check/blob/main/LICENSE + expression: Apache-2.0 + release: + changelog: https://github.com/gexiro-global/csaf-check/blob/main/CHANGELOG.md + automated-pipeline: true + distribution-points: + - uri: https://pypi.org/project/csaf-check/ + comment: Published Python package. + security: + assessments: + self: + comment: Maintainer self-assessment only; no independent audit is claimed. From 730dd74d92543a27827594ed29c2c3709ac99128 Mon Sep 17 00:00:00 2001 From: dzeusking-dev Date: Fri, 4 Sep 2026 15:37:10 +0200 Subject: [PATCH 2/2] chore: keep README diff minimal Signed-off-by: Bartosz Joachimiak <262029076+dzeusking-dev@users.noreply.github.com> --- README.md | 256 +++++++++++++++++++++++++++--------------------------- 1 file changed, 128 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index f58296e..5f61570 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,131 @@ -# csaf-check - -[![CI](https://github.com/gexiro-global/csaf-check/actions/workflows/ci.yml/badge.svg)](https://github.com/gexiro-global/csaf-check/actions/workflows/ci.yml) -[![PyPI](https://img.shields.io/pypi/v/csaf-check.svg)](https://pypi.org/project/csaf-check/) -[![Python](https://img.shields.io/pypi/pyversions/csaf-check.svg)](https://pypi.org/project/csaf-check/) +# csaf-check + +[![CI](https://github.com/gexiro-global/csaf-check/actions/workflows/ci.yml/badge.svg)](https://github.com/gexiro-global/csaf-check/actions/workflows/ci.yml) +[![PyPI](https://img.shields.io/pypi/v/csaf-check.svg)](https://pypi.org/project/csaf-check/) +[![Python](https://img.shields.io/pypi/pyversions/csaf-check.svg)](https://pypi.org/project/csaf-check/) [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) [Security and trust evidence](docs/SECURITY-TRUST.md) documents the project's policies and automated checks. No certification or badge level is claimed. - -Validate CSAF 2.0 advisories from Python, using the same validator Secvisogram runs — and get an -honest answer when that validator is not installed. - -## The problem - -If you publish security advisories as CSAF, you want schema validation in your release pipeline, -not in a browser tab at the end. The authoritative implementation of the CSAF 2.0 mandatory tests -is [`@secvisogram/csaf-validator-lib`](https://github.com/secvisogram/csaf-validator-lib), which is -JavaScript. Reimplementing the schema in Python means maintaining a second, subtly different -opinion about what "valid" means — and yours will be the wrong one. - -So this package does not reimplement anything. It bridges to the real validator and handles the -part that is annoying to get right: **what happens when the validator is not there.** - -## The contract - -`validate()` never raises. Not when Node is missing, not when the library is absent, not when the -validator times out or returns something unexpected. Each of those returns a result that says so: - -```python -from csaf_check import validate - -result = validate(advisory_dict) - -result.available # could the validator run at all? -result.is_valid # True / False / None when no verdict was reached -result.errors # one message per failed mandatory test -result.note # why there is no verdict, when there isn't -result.conclusive # available and a verdict exists -``` - -This matters because the alternative — a validator that throws on a missing optional dependency — -turns a quality gate into a hard dependency, and every caller ends up wrapping it in `try/except` -and swallowing real failures along with the boring ones. - -## Install - -```bash -pip install csaf-check -``` - -That gives you the Python API and CLI. For actual validation you also need Node.js and the -validator library: - -```bash -npm install @secvisogram/csaf-validator-lib -``` - -The **default is intentionally lenient**: without the JavaScript validator, `csaf-check` prints -`UNKNOWN - validator unavailable` and exits `0`, so CI environments without Node or the validator -do not break. Strict handling is an explicit opt-in: add `--require-validator` to make validator -unavailability exit `3`. - -The validator is found either next to the installed package or under `node_modules` in your current -working directory, so running `npm install` in your own project directory is enough — you do not -have to install it into `site-packages`. `NODE_PATH` is honoured as well. - -## CLI - -```bash -csaf-check examples/advisory-minimal.json # human-readable verdict -csaf-check examples/advisory-minimal.json --json # machine-readable -csaf-check - < examples/advisory-minimal.json # stdin -csaf-check examples/advisory-minimal.json --require-validator # fail if no verdict is possible -``` - -Exit codes: `0` valid (or inconclusive without `--require-validator`) — `1` invalid — -`2` unreadable or malformed input — `3` no verdict possible and `--require-validator` was set. - -## Python API - -```python -import json -from csaf_check import validate, validator_available - -if not validator_available(): - print("install Node.js to enable strict validation") - -with open("advisory.json", encoding="utf-8") as fh: - result = validate(json.load(fh)) - -if result.conclusive and not result.is_valid: - for message in result.errors: - print("FAIL", message) -``` - -## What this does NOT do - -- It does not generate CSAF documents. It only validates ones you already have. -- It runs the mandatory tests only, not the informative or optional profiles. -- It does not check that your advisory is *correct*, only that it is well-formed. A schema-valid - document can still describe the wrong product or the wrong affected range. -- It does not publish anything, sign anything, or talk to any network service. -- It is not legal advice about the EU Cyber Resilience Act. CRA is format-neutral and does not - mandate CSAF. CSAF is a good machine-readable choice for advisories and VEX, not a compliance - checkbox. - -## Examples - -`examples/advisory-minimal.json` is a small synthetic advisory for a fictional vendor using a -placeholder CVE identifier. `examples/advisory-invalid.json` is deliberately missing the mandatory -`tracking` object. Both exist to exercise this wrapper; neither describes a real product or a real -vulnerability. - -## Testing - -```bash -pip install -e ".[dev]" -pytest -q -``` - -The suite runs without Node.js on purpose — the degradation path is the part most likely to rot. - -## License - -Apache-2.0. See [LICENSE](LICENSE). - -Built and maintained by [Gexiro Global Enterprises Ltd](https://gexiro.com). - -Third-party attribution: see [NOTICE](NOTICE). - -Part of the [Gexiro open-source toolkit](https://github.com/gexiro-global). + +Validate CSAF 2.0 advisories from Python, using the same validator Secvisogram runs — and get an +honest answer when that validator is not installed. + +## The problem + +If you publish security advisories as CSAF, you want schema validation in your release pipeline, +not in a browser tab at the end. The authoritative implementation of the CSAF 2.0 mandatory tests +is [`@secvisogram/csaf-validator-lib`](https://github.com/secvisogram/csaf-validator-lib), which is +JavaScript. Reimplementing the schema in Python means maintaining a second, subtly different +opinion about what "valid" means — and yours will be the wrong one. + +So this package does not reimplement anything. It bridges to the real validator and handles the +part that is annoying to get right: **what happens when the validator is not there.** + +## The contract + +`validate()` never raises. Not when Node is missing, not when the library is absent, not when the +validator times out or returns something unexpected. Each of those returns a result that says so: + +```python +from csaf_check import validate + +result = validate(advisory_dict) + +result.available # could the validator run at all? +result.is_valid # True / False / None when no verdict was reached +result.errors # one message per failed mandatory test +result.note # why there is no verdict, when there isn't +result.conclusive # available and a verdict exists +``` + +This matters because the alternative — a validator that throws on a missing optional dependency — +turns a quality gate into a hard dependency, and every caller ends up wrapping it in `try/except` +and swallowing real failures along with the boring ones. + +## Install + +```bash +pip install csaf-check +``` + +That gives you the Python API and CLI. For actual validation you also need Node.js and the +validator library: + +```bash +npm install @secvisogram/csaf-validator-lib +``` + +The **default is intentionally lenient**: without the JavaScript validator, `csaf-check` prints +`UNKNOWN - validator unavailable` and exits `0`, so CI environments without Node or the validator +do not break. Strict handling is an explicit opt-in: add `--require-validator` to make validator +unavailability exit `3`. + +The validator is found either next to the installed package or under `node_modules` in your current +working directory, so running `npm install` in your own project directory is enough — you do not +have to install it into `site-packages`. `NODE_PATH` is honoured as well. + +## CLI + +```bash +csaf-check examples/advisory-minimal.json # human-readable verdict +csaf-check examples/advisory-minimal.json --json # machine-readable +csaf-check - < examples/advisory-minimal.json # stdin +csaf-check examples/advisory-minimal.json --require-validator # fail if no verdict is possible +``` + +Exit codes: `0` valid (or inconclusive without `--require-validator`) — `1` invalid — +`2` unreadable or malformed input — `3` no verdict possible and `--require-validator` was set. + +## Python API + +```python +import json +from csaf_check import validate, validator_available + +if not validator_available(): + print("install Node.js to enable strict validation") + +with open("advisory.json", encoding="utf-8") as fh: + result = validate(json.load(fh)) + +if result.conclusive and not result.is_valid: + for message in result.errors: + print("FAIL", message) +``` + +## What this does NOT do + +- It does not generate CSAF documents. It only validates ones you already have. +- It runs the mandatory tests only, not the informative or optional profiles. +- It does not check that your advisory is *correct*, only that it is well-formed. A schema-valid + document can still describe the wrong product or the wrong affected range. +- It does not publish anything, sign anything, or talk to any network service. +- It is not legal advice about the EU Cyber Resilience Act. CRA is format-neutral and does not + mandate CSAF. CSAF is a good machine-readable choice for advisories and VEX, not a compliance + checkbox. + +## Examples + +`examples/advisory-minimal.json` is a small synthetic advisory for a fictional vendor using a +placeholder CVE identifier. `examples/advisory-invalid.json` is deliberately missing the mandatory +`tracking` object. Both exist to exercise this wrapper; neither describes a real product or a real +vulnerability. + +## Testing + +```bash +pip install -e ".[dev]" +pytest -q +``` + +The suite runs without Node.js on purpose — the degradation path is the part most likely to rot. + +## License + +Apache-2.0. See [LICENSE](LICENSE). + +Built and maintained by [Gexiro Global Enterprises Ltd](https://gexiro.com). + +Third-party attribution: see [NOTICE](NOTICE). + +Part of the [Gexiro open-source toolkit](https://github.com/gexiro-global).