From 3211c8c00491fb30ad571a4a5e46c1c4506fed0a Mon Sep 17 00:00:00 2001 From: devashridatta-dotcom <252791751+devashridatta-dotcom@users.noreply.github.com> Date: Sun, 18 Oct 2026 09:03:47 -0700 Subject: [PATCH] Add Safety Perspective mappings and worked examples Signed-off-by: devashridatta-dotcom <252791751+devashridatta-dotcom@users.noreply.github.com> --- perspectives/README.md | 121 +++++++ perspectives/safety-perspective.json | 108 ++++++ tools/src/test/js/schema-v2/package.json | 4 +- .../js/schema-v2/safety-perspective-tests.js | 242 ++++++++++++++ ...alid-safety-perspective-no-action-2.0.json | 284 ++++++++++++++++ ...lid-safety-perspective-validation-2.0.json | 266 +++++++++++++++ ...-safety-perspective-vulnerability-2.0.json | 309 ++++++++++++++++++ 7 files changed, 1333 insertions(+), 1 deletion(-) create mode 100644 perspectives/README.md create mode 100644 perspectives/safety-perspective.json create mode 100644 tools/src/test/js/schema-v2/safety-perspective-tests.js create mode 100644 tools/src/test/resources/2.0/valid-safety-perspective-no-action-2.0.json create mode 100644 tools/src/test/resources/2.0/valid-safety-perspective-validation-2.0.json create mode 100644 tools/src/test/resources/2.0/valid-safety-perspective-vulnerability-2.0.json diff --git a/perspectives/README.md b/perspectives/README.md new file mode 100644 index 000000000..7f2ae8b1c --- /dev/null +++ b/perspectives/README.md @@ -0,0 +1,121 @@ +# Safety Perspective + +[safety-perspective.json](safety-perspective.json) proposes a reusable Safety +Perspective for [issue #1122](https://github.com/CycloneDX/specification/issues/1122). +It uses the existing CycloneDX 2.0 perspective, blueprint, risk, annotation, and +declaration models, including the safety work merged in +[PR #1120](https://github.com/CycloneDX/specification/pull/1120). +It adds examples and mappings, with no schema or vocabulary changes. + +The catalog document contains a single inline perspective. A consumer can copy +that entry into a document's `perspectives` array. The worked examples reference +the catalog through an external reference of type `perspective`; this reference +does not cause a consumer to fetch or apply the perspective automatically. +Catalog registration and predefined identities proposed in +[PR #1067](https://github.com/CycloneDX/specification/pull/1067) are separate work. + +## Reading the view + +The mappings select data across the **whole document**. They do not implement +per-assessment scoping, joins, reference resolution, or a completeness policy. +In a document containing several contexts, resolve each assessment's `risks`, +then each risk's `affects`, to identify the relevant asset and its classification. +Resolve that asset's `componentRef` to the component identity and version. +Follow `triggeredBy` to the existing source object and each claim's `target`, +`evidence`, and `counterEvidence` to its subject and supporting records. +Never apply every selected claim or evidence object to every assessment. + +The graph in the examples is: + +```text +assessment -> triggeredBy -> vulnerability or report annotation +assessment -> risks -> risk -> affects -> asset -> componentRef -> component + -> relatedDamageScenarios -> potential harm +claim -> target -> asset + -> evidence / counterEvidence -> evidence records +assessment -> externalReferences -> authoritative review or change records +``` + +Assessment status describes progress. Conclusion describes risk acceptability. +For example, an assessment may be `completed` and conclude `unacceptable` while +corrective implementation and verification remain open. `acceptable` is not a +release authorization. A supplied `approved` status still requires the consumer +to establish who approved what, under which process, and with what authority. +The examples preserve the manufacturer's authority and do not certify a product. + +## Lessons from the SPDX reviews + +These are usage lessons from ongoing SPDX proposals, not a claim that their +unmerged vocabulary is standardized or a lossless SPDX/CycloneDX conversion. + +| Review lesson | Application here | +| --- | --- | +| Reuse an existing vulnerability as the source | `triggeredBy` references the vulnerability directly; no duplicate trigger is created. Non-security reports use existing annotations. | +| A trigger can lead to no change | The customer-report example records a bounded no-action result with an explicit rationale and applicable baseline evidence. | +| Separate analysis lifecycle, outcome, and decision | `status` and `conclusion` keep their native meanings. Per-element dispositions and their lifecycle remain in authoritative records; no new Decision class or approval enums are invented. | +| Distinguish affected from added, modified, or removed | `risk.affects` records scope, not a change set. The validation example links to the change record rather than misusing `affects` for proposed edits. | +| Explain why verification is sufficient | `claim.reasoning` explains the selected method and evidence coverage. It is distinct from the source report, analysis scope, and review disposition. | +| Avoid ambiguous impact scales | Classifications remain scheme-specific. Harm is described with its category and example context; no universal safety score or ASIL/SIL/DAL conversion is defined. | +| Keep generic models reusable | Safety-specific workflow guidance lives here and in fixtures rather than restricting the generic risk, annotation, or declaration models. | +| Model closure evidence explicitly | Claims reference actual evidence, and review records bind a subject and assessment. Missing references remain missing even if a status says `completed`. | + +The relevant discussions are +[SPDX #1442](https://github.com/spdx/spdx-3-model/pull/1442), +[SPDX #1443](https://github.com/spdx/spdx-3-model/pull/1443), and +[SPDX #1458](https://github.com/spdx/spdx-3-model/pull/1458). + +The integrity level on a blueprint asset describes that context, not an intrinsic +classification of every use of the component. An omitted optional classification +means that it was not supplied; it does not assert an absence of safety relevance. +Where a process stops an assessment or closes it as duplicate, retain that state +using the native custom-status form and link the covering analysis or lifecycle +record. Do not label stopped work `completed` solely to imply closure. + +## Worked examples + +All names, reports, policies, and outcomes below are fictional illustrations. +They are complete local graphs for demonstrating the mappings, not evidence +about an actual product or a general definition of safety-case completeness. + +| Example | Result | Evidence and authority boundary | +| --- | --- | --- | +| [Vulnerability](../tools/src/test/resources/2.0/valid-safety-perspective-vulnerability-2.0.json) | Completed, acceptable in the assessed certificate-pinning configuration | Direct vulnerability trigger, contextual IEC 62304 classification, FMEA and regression evidence; separate review record. Existing VEX remains unchanged. | +| [Validation failure](../tools/src/test/resources/2.0/valid-safety-perspective-validation-2.0.json) | Completed, unacceptable; corrective work remains open | Failed test supports a non-conformance claim. A change record tracks the proposed correction and verification rerun. Completed assessment does not close them. | +| [Customer report](../tools/src/test/resources/2.0/valid-safety-perspective-no-action-2.0.json) | Completed, acceptable with a bounded no-action disposition | Design inspection explains why the existing baseline verification remains sufficient and no additional safety verification is required for this report. | + +Each reference of type `evidence` on an assessment is a BOM-Link to an embedded +review record. These records are text evidence of example dispositions, not a +portable machine-readable Decision model or an authenticated authorization grant. +For operational use, the authoritative system must establish the responsible +identity, scope, process or policy version, relevant evidence, and authorization. +Keep analysis results separate from each element's disposition and its lifecycle. + +Changes to source versions, assumptions, requirements, deployment context, or +evidence validity require reassessment under the responsible lifecycle process. +Do not silently carry a prior decision to the new baseline. Digests detect byte +changes; they do not by themselves detect semantic staleness or completeness. + +## Validation + +From `tools/src/test/js/schema-v2`, install dependencies and run: + +```sh +npm install +npm run test:v2.0:t4-safety-perspective +``` + +The tests validate the catalog, fixtures, and embedded perspective against the +unmodified source schemas; evaluate every JSONPath mapping on each example; +check local identities, reference binding, and exact-byte hashes of the new +embedded text records; and exercise missing evidence, missing triggers, +counter-evidence, duplicate identities, cross-subject substitution, wrong BOM +versions, and modified evidence. They also demonstrate that an unacceptable +result can coexist with a completed assessment and that VEX is not rewritten. + +The test checks are limited to these examples. Schema validity, graph integrity, +cryptographic authenticity, evidence sufficiency, authorization, and safety claim +truth are different checks. The fixtures are unsigned; no signature verification, +authority-policy evaluation, safety acceptance, or release decision is automated. +In particular, a valid signature would not repair missing evidence or give its +signer authority. Required downstream verification may remain open, or a +responsible review may justify no additional verification, as illustrated above. diff --git a/perspectives/safety-perspective.json b/perspectives/safety-perspective.json new file mode 100644 index 000000000..60d29254e --- /dev/null +++ b/perspectives/safety-perspective.json @@ -0,0 +1,108 @@ +{ + "$schema": "https://cyclonedx.org/schema/2.0/cyclonedx-2.0.schema.json", + "specFormat": "CycloneDX", + "specVersion": "2.0", + "version": 1, + "perspectives": [ + { + "bom-ref": "perspective-safety", + "name": "Safety Perspective", + "description": "A view of safety impact assessments, their classified product context, source triggers, affected elements, claims, evidence, and recorded conclusions. Presence of these fields does not establish evidence completeness, reviewer authority, release approval, or safety claim truth. The mappings apply to the whole document; consumers resolve references to associate each assessment with its own context.", + "domains": [ + "functional-safety", + "risk-management" + ], + "mappings": [ + { + "expression": "$.blueprints[*].assets[*].classification.safetyIntegrityLevels[*]", + "nativeName": "Safety integrity context", + "nativeDescription": "The scheme and integrity level assigned to an asset in its product or deployment context.", + "relevance": "recommended", + "rationale": "An integrity classification is contextual and is not a risk score. Absence means no classification was supplied; it does not mean the asset has no safety relevance." + }, + { + "expression": "$.risks.assessments[*].triggeredBy[*]", + "nativeName": "Analysis source", + "nativeDescription": "References to existing source objects that occasioned the assessment.", + "relevance": "required", + "rationale": "Reuse an existing vulnerability, annotation, or other source object instead of creating a second trigger for the same event. A trigger does not imply that a change is necessary." + }, + { + "expression": "$.risks.assessments[*].scope", + "nativeName": "Analysis scope", + "nativeDescription": "The system, version, deployment, and boundaries considered by the analysis.", + "relevance": "required" + }, + { + "expression": "$.risks.assessments[*].risks[*]", + "nativeName": "Assessed risks", + "nativeDescription": "References to the risks evaluated by each assessment.", + "relevance": "required" + }, + { + "expression": "$.risks.risks[*].affects[*]", + "nativeName": "Affected elements", + "nativeDescription": "References to the elements considered by a risk.", + "relevance": "required", + "rationale": "Being affected or in scope does not mean that an element was added, modified, or removed. Those changes belong in the authoritative change records." + }, + { + "expression": "$.risks.damageScenarios[*]", + "nativeName": "Potential harm", + "nativeDescription": "Damage scenarios describing the consequence whose applicability or change is assessed.", + "relevance": "recommended", + "rationale": "Describe the harm and the impact category; severity and likelihood labels need an identified organizational methodology." + }, + { + "expression": "$.risks.risks[*].inherentRisk.impact", + "nativeName": "Safety impact", + "nativeDescription": "The impact category and potential harm before the stated risk responses.", + "relevance": "recommended", + "rationale": "A potential consequence is not a finding that it occurred. No cross-standard conversion of ASIL, SIL, DAL, or software safety classes is implied." + }, + { + "expression": "$.risks.assessments[*].status", + "nativeName": "Assessment lifecycle state", + "nativeDescription": "Progress of the assessment activity.", + "relevance": "required", + "rationale": "A completed assessment can conclude unacceptable or inconclusive. Lifecycle state alone is insufficient to infer release permission." + }, + { + "expression": "$.risks.assessments[*].conclusion", + "nativeName": "Assessment conclusion", + "nativeDescription": "Acceptability of the assessed risks under the applicable context and process.", + "relevance": "required", + "rationale": "The conclusion supports a decision; it does not identify the authorized decision-maker or encode a general per-element decision lifecycle." + }, + { + "expression": "$.declarations.claims[*]", + "nativeName": "Claims and verification rationale", + "nativeDescription": "Claims against explicit targets, with reasoning explaining why the referenced evidence supports each claim.", + "relevance": "required", + "rationale": "Keep the reason for initiating an assessment separate from the rationale for its result and from the justification for a verification method." + }, + { + "expression": "$.declarations.evidence[*]", + "nativeName": "Supporting and counter evidence", + "nativeDescription": "Evidence objects referenced by claims, including test results, analysis records, and authoritative decision records.", + "relevance": "required", + "rationale": "Resolve both evidence and counterEvidence. The presence of an evidence object does not prove that every required branch is complete, current, or accepted." + }, + { + "expression": "$.risks.assessments[*].assessors[*]", + "nativeName": "Assessment provenance", + "nativeDescription": "The parties that performed the assessment.", + "relevance": "recommended", + "rationale": "An assessor or automated tool is not necessarily authorized to approve a product release." + }, + { + "expression": "$.risks.assessments[*].externalReferences[?(@.type=='evidence')]", + "nativeName": "Decision and review records", + "nativeDescription": "References to records of review, per-element disposition, responsible identity, and governing process where supplied.", + "relevance": "recommended", + "rationale": "Retain reusable decisions and authorization in the authoritative lifecycle system. A reference, reviewer name, signature, or recorded approval does not by itself establish relying-party authorization." + } + ] + } + ] +} diff --git a/tools/src/test/js/schema-v2/package.json b/tools/src/test/js/schema-v2/package.json index 63b0b645f..ae3140bf2 100644 --- a/tools/src/test/js/schema-v2/package.json +++ b/tools/src/test/js/schema-v2/package.json @@ -9,6 +9,7 @@ "ajv-formats": "^3.0.1", "ajv-formats-draft2019": "^1.6.1", "glob": "^13.0.0", + "jsonpath-plus": "^11.2.1", "npm-run-all2": "^9.0.3" }, "devDependencies": { @@ -19,6 +20,7 @@ "test:v2.0": "run-s \"test:v2.0:*\"", "test:v2.0:t1-json-schema-validate": "node -- json-schema-validate-tests.js -v 2.0", "test:v2.0:t2-json-schema-semantic": "node -- json-schema-semantic-tests.js -v 2.0", - "test:v2.0:t3-json-schema-functional": "node -- json-schema-functional-tests.js -v 2.0" + "test:v2.0:t3-json-schema-functional": "node -- json-schema-functional-tests.js -v 2.0", + "test:v2.0:t4-safety-perspective": "node --test safety-perspective-tests.js" } } diff --git a/tools/src/test/js/schema-v2/safety-perspective-tests.js b/tools/src/test/js/schema-v2/safety-perspective-tests.js new file mode 100644 index 000000000..f616669ce --- /dev/null +++ b/tools/src/test/js/schema-v2/safety-perspective-tests.js @@ -0,0 +1,242 @@ +"use strict"; + +import assert from 'node:assert/strict'; +import {createHash} from 'node:crypto'; +import {readFile, readdir} from 'node:fs/promises'; +import {test} from 'node:test'; + +import Ajv2020 from 'ajv/dist/2020.js'; +import draft7MetaSchema from 'ajv/dist/refs/json-schema-draft-07.json' with {type: 'json'}; +import addFormats from 'ajv-formats'; +import addFormats2019 from 'ajv-formats-draft2019'; +import {JSONPath} from 'jsonpath-plus'; + +const root = new URL('../../../../../', import.meta.url); +const readJson = async path => JSON.parse(await readFile(new URL(path, root), 'utf8')); +const catalog = await readJson('perspectives/safety-perspective.json'); +const perspective = catalog.perspectives[0]; +const scenarios = ['vulnerability', 'validation', 'no-action']; +const examples = await Promise.all(scenarios.map(scenario => + readJson(`tools/src/test/resources/2.0/valid-safety-perspective-${scenario}-2.0.json`))); + +// Register the unmodified source schemas. readdir also works on Windows, +// where a platform-specific glob pattern can silently select no modules. +const ajv = new Ajv2020({strict: false, allErrors: true, validateFormats: true}); +ajv.addMetaSchema(draft7MetaSchema); +addFormats(ajv); +addFormats2019(ajv, {formats: ['idn-email']}); +ajv.addFormat('iri-reference', true); // Same limitation as the existing harness. +for (const name of ['spdx', 'cryptography-defs', 'behavior-taxonomy']) { + ajv.addSchema(await readJson(`schema/${name}.schema.json`), `https://cyclonedx.org/schema/${name}.schema.json`); +} +const moduleDirectory = new URL('schema/2.0/modules/', root); +for (const name of (await readdir(moduleDirectory)).sort()) { + if (name.endsWith('.schema.json')) { + ajv.addSchema(JSON.parse(await readFile(new URL(name, moduleDirectory), 'utf8'))); + } +} +const validate = ajv.compile(await readJson('schema/2.0/cyclonedx-2.0.schema.json')); +const schemaValid = document => assert.ok(validate(document), JSON.stringify(validate.errors)); + +const select = (document, expression) => JSONPath({path: expression, json: document}); + +function indexReferences(document) { + const index = new Map(); + function visit(value) { + if (Array.isArray(value)) return value.forEach(visit); + if (value === null || typeof value !== 'object') return; + if (value['bom-ref']) { + assert.ok(!index.has(value['bom-ref']), `Duplicate bom-ref: ${value['bom-ref']}`); + index.set(value['bom-ref'], value); + } + Object.values(value).forEach(visit); + } + visit(document); + return index; +} + +// These checks describe the self-contained examples, not a general +// CycloneDX conformance, completeness, authorization, or safety evaluator. +function assertExampleLinks(document) { + const index = indexReferences(document); + const resolve = ref => { + assert.ok(index.has(ref), `Unresolved local reference: ${ref}`); + return index.get(ref); + }; + const affected = new Set(document.risks.risks.flatMap(risk => risk.affects)); + for (const assessment of document.risks.assessments) { + assessment.triggeredBy.forEach(resolve); + assessment.risks.forEach(ref => assert.ok(document.risks.risks.includes(resolve(ref)))); + for (const reference of assessment.externalReferences ?? []) { + if (reference.url.startsWith('urn:cdx:')) { + const prefix = `urn:cdx:${document.serialNumber.slice('urn:uuid:'.length)}/${document.version}#`; + assert.ok(reference.url.startsWith(prefix), 'Review record belongs to another BOM or version'); + const evidence = resolve(reference.url.slice(prefix.length)); + assert.ok(document.declarations.evidence.includes(evidence), 'Review reference must resolve to evidence'); + } + } + } + for (const asset of document.blueprints.flatMap(blueprint => blueprint.assets)) { + resolve(asset.componentRef); + } + for (const risk of document.risks.risks) { + risk.affects.forEach(resolve); + for (const field of ['relatedVulnerabilities', 'relatedRequirements', 'relatedClaims', 'relatedDamageScenarios']) { + (risk[field] ?? []).forEach(resolve); + } + } + for (const annotation of document.annotations ?? []) annotation.subjects.forEach(resolve); + for (const claim of document.declarations.claims) { + resolve(claim.target); + assert.ok(affected.has(claim.target), 'Claim is bound to a different subject'); + for (const ref of [...claim.evidence ?? [], ...claim.counterEvidence ?? []]) { + assert.ok(document.declarations.evidence.includes(resolve(ref)), 'Claim must reference evidence'); + } + } +} + +function assertEmbeddedDigests(document) { + for (const evidence of document.declarations.evidence) { + for (const {contents} of evidence.data ?? []) { + if (!contents.attachment || contents.attachment.encoding) continue; + for (const hash of contents.hashes ?? []) { + assert.equal(hash.alg, 'SHA-256'); + assert.equal(createHash('sha256').update(contents.attachment.content, 'utf8').digest('hex'), hash.content); + } + } + } +} + +test('perspective definition is a valid native CycloneDX 2.0 document', () => { + schemaValid(catalog); + assert.equal(catalog.perspectives.length, 1); + assert.equal(perspective.name, 'Safety Perspective'); + assert.ok(!('predefined' in perspective)); + assert.ok(perspective.mappings.every(mapping => !('via' in mapping) && !('expectation' in mapping))); +}); + +for (const [i, scenario] of scenarios.entries()) { + test(`${scenario}: schema, local graph, evidence digests, and actual mapping results`, () => { + const document = examples[i]; + schemaValid(document); + assertExampleLinks(document); + assertEmbeddedDigests(document); + // A reader can embed the catalog entry without any schema extension. + schemaValid({...document, perspectives: [perspective]}); + for (const mapping of perspective.mappings) { + const selected = select(document, mapping.expression); + assert.ok(selected.length > 0, `Mapping selects no example data: ${mapping.nativeName}`); + } + assert.deepEqual(select(document, '$.risks.assessments[*].triggeredBy[*]'), document.risks.assessments[0].triggeredBy); + assert.deepEqual(select(document, '$.blueprints[*].assets[*].classification.safetyIntegrityLevels[*]'), [{scheme: 'IEC-62304', level: 'class-c'}]); + assert.deepEqual(select(document, "$.risks.assessments[*].externalReferences[?(@.type=='evidence')]"), document.risks.assessments[0].externalReferences.filter(ref => ref.type === 'evidence')); + }); +} + +test('existing vulnerability is the trigger, without a duplicate trigger annotation', () => { + assert.equal(examples[0].risks.assessments[0].triggeredBy[0], examples[0].vulnerabilities[0]['bom-ref']); + assert.ok(!examples[0].annotations); +}); + +test('completed assessment can have an unacceptable conclusion and unfinished corrective work', () => { + const assessment = examples[1].risks.assessments[0]; + assert.equal(assessment.status, 'completed'); + assert.equal(assessment.conclusion, 'unacceptable'); + assert.match(assessment.summary, /correction and verification rerun remain open/); +}); + +test('recorded evidence exists before the assessment and document snapshot', () => { + for (const document of examples) { + const assessmentTime = Date.parse(document.risks.assessments[0].timestamp); + assert.ok(assessmentTime <= Date.parse(document.metadata.timestamp)); + for (const evidence of document.declarations.evidence) { + assert.ok(Date.parse(evidence.created) <= assessmentTime); + } + } +}); + +test('no-action outcome has existing baseline evidence and a sufficiency rationale', () => { + const document = examples[2]; + assert.equal(document.risks.assessments[0].conclusion, 'acceptable'); + assert.match(document.declarations.claims[0].reasoning, /baseline/); + assert.deepEqual(document.declarations.claims[0].evidence, ['evidence-design-inspection', 'evidence-baseline-test']); +}); + +test('schema-valid missing evidence is detected by the example graph checks', () => { + const broken = structuredClone(examples[1]); + broken.declarations.evidence = broken.declarations.evidence.filter(e => e['bom-ref'] !== 'evidence-validation-failure'); + schemaValid(broken); + assert.throws(() => assertExampleLinks(broken), /Unresolved local reference/); +}); + +test('schema-valid missing trigger is detected', () => { + const broken = structuredClone(examples[1]); + broken.annotations = []; + schemaValid(broken); + assert.throws(() => assertExampleLinks(broken), /Unresolved local reference/); +}); + +test('counter-evidence references must also resolve', () => { + const broken = structuredClone(examples[0]); + broken.declarations.claims[0].counterEvidence = ['missing-counter-evidence']; + schemaValid(broken); + assert.throws(() => assertExampleLinks(broken), /Unresolved local reference/); +}); + +test('duplicate identities cannot silently shadow another object', () => { + const broken = structuredClone(examples[1]); + broken.declarations.evidence[1]['bom-ref'] = broken.declarations.evidence[0]['bom-ref']; + schemaValid(broken); + assert.throws(() => assertExampleLinks(broken), /Duplicate bom-ref/); +}); + +test('an existing but unrelated claim target cannot substitute for the assessed context', () => { + const broken = structuredClone(examples[1]); + broken.definitions.components.push({'bom-ref': 'another-device', type: 'device', name: 'Unrelated device'}); + broken.declarations.claims[0].target = 'another-device'; + schemaValid(broken); + assert.throws(() => assertExampleLinks(broken), /different subject/); +}); + +test('review BOM-Link must bind the current document and version', () => { + const broken = structuredClone(examples[1]); + broken.risks.assessments[0].externalReferences[0].url = broken.risks.assessments[0].externalReferences[0].url.replace('/1#', '/2#'); + schemaValid(broken); + assert.throws(() => assertExampleLinks(broken), /another BOM or version/); +}); + +test('schema-valid evidence modification fails its exact-byte digest check', () => { + const broken = structuredClone(examples[1]); + broken.declarations.evidence[0].data[0].contents.attachment.content += ' Result changed to pass.'; + schemaValid(broken); + assert.throws(() => assertEmbeddedDigests(broken), assert.AssertionError); +}); + +test('unresolved required graph branch is not repaired by a completed or approved status', () => { + for (const status of ['completed', 'approved']) { + const broken = structuredClone(examples[1]); + broken.risks.assessments[0].status = status; + broken.declarations.claims[0].evidence.push('missing-verification-rerun'); + schemaValid(broken); + assert.throws(() => assertExampleLinks(broken), /Unresolved local reference/); + } +}); + +test('perspective and graph checks leave the supplied VEX applicability unchanged', () => { + const document = structuredClone(examples[0]); + const original = structuredClone(document.vulnerabilities[0].analysis); + assertExampleLinks(document); + for (const mapping of perspective.mappings) select(document, mapping.expression); + assert.deepEqual(document.vulnerabilities[0].analysis, original); +}); + +test('graph and digest checks make no authentication or authorization assertion', () => { + for (const document of examples) { + assertExampleLinks(document); + assertEmbeddedDigests(document); + // These synthetic, unsigned records deliberately carry no grant of authority. + assert.ok(!document.signatures); + assert.ok(document.declarations.evidence.every(evidence => !evidence.signatures)); + assert.notEqual(document.risks.assessments[0].status, 'approved'); + } +}); diff --git a/tools/src/test/resources/2.0/valid-safety-perspective-no-action-2.0.json b/tools/src/test/resources/2.0/valid-safety-perspective-no-action-2.0.json new file mode 100644 index 000000000..ffa12d529 --- /dev/null +++ b/tools/src/test/resources/2.0/valid-safety-perspective-no-action-2.0.json @@ -0,0 +1,284 @@ +{ + "$schema": "https://cyclonedx.org/schema/2.0/cyclonedx-2.0.schema.json", + "$comment": "Illustrative Safety Perspective fixture. All organizations, records, and results are synthetic. The declarations carry evidence for review, not a safety certification or an independently established authorization.", + "specFormat": "CycloneDX", + "specVersion": "2.0", + "serialNumber": "urn:uuid:2f984e8e-5e55-4b2d-875a-ef5144aa9f8a", + "version": 1, + "metadata": { + "timestamp": "2026-10-16T15:00:00Z" + }, + "definitions": { + "components": [ + { + "bom-ref": "comp-pump-firmware", + "type": "firmware", + "name": "acme-pump-firmware", + "version": "3.2.0", + "externalReferences": [ + { + "type": "hazard-analysis", + "url": "https://plm.acme-medical.example/safety/RA-2026-117" + } + ] + }, + { + "bom-ref": "comp-globex-tls", + "type": "library", + "name": "globex-tls", + "version": "1.4.2" + } + ], + "requirements": [ + { + "bom-ref": "req-command-integrity", + "id": "SR-041", + "name": "Therapy command integrity", + "type": [ + "safety" + ], + "description": "Only authentic therapy commands are accepted in the stated deployment.", + "rationale": "Command integrity prevents unintended drug delivery in the classified therapy path." + } + ] + }, + "blueprints": [ + { + "bom-ref": "bp-pump-deployment", + "name": "Infusion pump, hospital deployment", + "modelTypes": [ + "data-flow" + ], + "assets": [ + { + "bom-ref": "asset-tls-path", + "componentRef": "comp-globex-tls", + "classification": { + "criticality": "critical", + "safetyIntegrityLevels": [ + { + "scheme": "IEC-62304", + "level": "class-c" + } + ] + } + } + ] + } + ], + "declarations": { + "claims": [ + { + "bom-ref": "claim-no-new-safety-impact", + "target": "asset-tls-path", + "predicate": "REPORT-043 does not invalidate the assessed therapy-path safety requirement or baseline verification.", + "reasoning": "Design inspection establishes that the maintenance icon cannot affect command processing. Existing command-integrity verification applies to the unchanged baseline, so repeating it would not test a changed safety path.", + "evidence": [ + "evidence-design-inspection", + "evidence-baseline-test" + ] + } + ], + "evidence": [ + { + "bom-ref": "evidence-design-inspection", + "description": "Illustrative design inspection result.", + "data": [ + { + "name": "evidence-design-inspection", + "contents": { + "attachment": { + "mediaType": "text/plain", + "content": "INSPECT-043: firmware 3.2.0 maintenance icon reads a diagnostic-only value. There is no write or control-flow path to therapy command processing. The affected baseline and isolation boundary were inspected." + }, + "hashes": [ + { + "alg": "SHA-256", + "content": "aef97527e884eaed60974862a282a2e66e68cf41107e26dfa65df11ece12b10e" + } + ] + } + } + ], + "created": "2026-10-16T12:00:00Z", + "author": { + "name": "Example safety engineering team" + } + }, + { + "bom-ref": "evidence-baseline-test", + "description": "Illustrative applicable baseline verification.", + "data": [ + { + "name": "evidence-baseline-test", + "contents": { + "attachment": { + "mediaType": "text/plain", + "content": "BASELINE-041: firmware 3.2.0 therapy command-integrity verification passed. REPORT-043 changes neither the baseline nor the assumptions of that verification." + }, + "hashes": [ + { + "alg": "SHA-256", + "content": "60690a2f9831557d0b3237f4b69a7370230b039e9dd56a3e6aab0bd8fc92e8f2" + } + ] + } + } + ], + "created": "2026-10-16T12:00:00Z", + "author": { + "name": "Example safety engineering team" + } + }, + { + "bom-ref": "evidence-review", + "description": "Illustrative no-action decision record.", + "data": [ + { + "name": "evidence-review", + "contents": { + "attachment": { + "mediaType": "text/plain", + "content": "REVIEW-043; subject: asset-tls-path; assessment: assessment-customer-report; disposition: no action for the therapy safety requirement. Reason: design inspection confirms isolation, and baseline verification remains applicable. Reviewer: Example product safety manager; process: Example lifecycle policy P-01 revision 1. No new verification is required for this disposition; other product-quality follow-up is outside this safety assessment." + }, + "hashes": [ + { + "alg": "SHA-256", + "content": "456ff51d4dc0838ff31a4c4795320b38bee886ed8bbc2ab01028a9fe323598c2" + } + ] + } + } + ], + "created": "2026-10-16T12:00:00Z", + "author": { + "name": "Example safety engineering team" + } + } + ] + }, + "risks": { + "risks": [ + { + "bom-ref": "risk-reported-indicator", + "name": "Reported indicator failure considered for therapy impact", + "statement": "If the reported status icon were part of the therapy command path, a hidden unsafe state could affect delivery. Analysis checks this possible connection.", + "domains": [ + { + "type": "safety" + } + ], + "affects": [ + "asset-tls-path", + "comp-pump-firmware" + ], + "inherentRisk": { + "impact": { + "level": "catastrophic", + "polarity": "harm", + "categories": [ + "safety" + ], + "description": "Incorrect drug delivery to a patient.", + "riskAttributes": [ + "integrity" + ] + }, + "rationale": "Example qualitative hazard-analysis method H-01 revision 1: catastrophic denotes potential death or permanent injury from unintended delivery. No event probability or numeric risk score is asserted." + }, + "status": "assessed", + "relatedDamageScenarios": [ + "damage-unintended-delivery" + ], + "residualRisk": { + "rationale": "The reported maintenance icon is outside the therapy command path. Design inspection and the existing command-integrity verification establish that the report does not invalidate SR-041 or its verification in this baseline." + } + } + ], + "assessments": [ + { + "bom-ref": "assessment-customer-report", + "name": "Safety triage of REPORT-043", + "type": [ + "safety" + ], + "cadence": "triggered", + "triggeredBy": [ + "trigger-customer-report" + ], + "timestamp": "2026-10-16T14:00:00Z", + "status": "completed", + "scope": "Pump firmware 3.2.0, Globex TLS 1.4.2, and the hospital deployment therapy command path. No claim about other products or deployments.", + "assessors": [ + { + "roles": [ + { + "role": "auditor" + } + ], + "organization": { + "name": "Acme Medical Systems" + } + } + ], + "risks": [ + "risk-reported-indicator" + ], + "conclusion": "acceptable", + "summary": "Completed triage found no additional safety impact. A justified no-action disposition is recorded for this context; no new verification is required under the example process because the report does not invalidate the existing baseline evidence.", + "nextReview": "2026-12-12T00:00:00Z", + "externalReferences": [ + { + "type": "evidence", + "url": "urn:cdx:2f984e8e-5e55-4b2d-875a-ef5144aa9f8a/1#evidence-review", + "comment": "Review record supporting a bounded no-action disposition; not a blanket release approval." + } + ], + "assumptions": [ + "The maintenance interface remains isolated from the therapy command path in firmware 3.2.0." + ] + } + ], + "damageScenarios": [ + { + "bom-ref": "damage-unintended-delivery", + "name": "Unintended drug delivery", + "description": "An unauthentic therapy command changes delivery to a patient.", + "affects": [ + "asset-tls-path" + ], + "impacts": [ + { + "level": "catastrophic", + "categories": [ + "safety" + ], + "polarity": "harm", + "description": "Potential patient harm from unintended drug delivery; the example severity label is interpreted under the fictional manufacturer hazard-analysis process." + } + ] + } + ] + }, + "externalReferences": [ + { + "type": "perspective", + "url": "https://github.com/CycloneDX/specification/blob/2.0-dev/perspectives/safety-perspective.json" + } + ], + "annotations": [ + { + "bom-ref": "trigger-customer-report", + "subjects": [ + "asset-tls-path" + ], + "annotator": { + "organization": { + "name": "Example validation laboratory" + } + }, + "timestamp": "2026-10-16T12:00:00Z", + "text": "REPORT-043: customer reports a missing status icon in the maintenance interface. Review determines whether the report changes the therapy safety context; no change is presumed." + } + ] +} diff --git a/tools/src/test/resources/2.0/valid-safety-perspective-validation-2.0.json b/tools/src/test/resources/2.0/valid-safety-perspective-validation-2.0.json new file mode 100644 index 000000000..3c7e87b3b --- /dev/null +++ b/tools/src/test/resources/2.0/valid-safety-perspective-validation-2.0.json @@ -0,0 +1,266 @@ +{ + "$schema": "https://cyclonedx.org/schema/2.0/cyclonedx-2.0.schema.json", + "$comment": "Illustrative Safety Perspective fixture. All organizations, records, and results are synthetic. The declarations carry evidence for review, not a safety certification or an independently established authorization.", + "specFormat": "CycloneDX", + "specVersion": "2.0", + "serialNumber": "urn:uuid:dde87342-8401-49d2-9898-223b1c5d03da", + "version": 1, + "metadata": { + "timestamp": "2026-10-16T15:00:00Z" + }, + "definitions": { + "components": [ + { + "bom-ref": "comp-pump-firmware", + "type": "firmware", + "name": "acme-pump-firmware", + "version": "3.2.0", + "externalReferences": [ + { + "type": "hazard-analysis", + "url": "https://plm.acme-medical.example/safety/RA-2026-117" + } + ] + }, + { + "bom-ref": "comp-globex-tls", + "type": "library", + "name": "globex-tls", + "version": "1.4.2" + } + ], + "requirements": [ + { + "bom-ref": "req-command-integrity", + "id": "SR-041", + "name": "Therapy command integrity", + "type": [ + "safety" + ], + "description": "Only authentic therapy commands are accepted in the stated deployment.", + "rationale": "Command integrity prevents unintended drug delivery in the classified therapy path." + } + ] + }, + "blueprints": [ + { + "bom-ref": "bp-pump-deployment", + "name": "Infusion pump, hospital deployment", + "modelTypes": [ + "data-flow" + ], + "assets": [ + { + "bom-ref": "asset-tls-path", + "componentRef": "comp-globex-tls", + "classification": { + "criticality": "critical", + "safetyIntegrityLevels": [ + { + "scheme": "IEC-62304", + "level": "class-c" + } + ] + } + } + ] + } + ], + "declarations": { + "claims": [ + { + "bom-ref": "claim-command-integrity-failure", + "target": "asset-tls-path", + "predicate": "Requirement SR-041 is not satisfied by firmware 3.2.0 in the tested certificate replacement configuration.", + "reasoning": "The test exercises the safety-relevant command path using a substituted certificate. Acceptance of an unauthentic command directly violates SR-041, so the failed result supports the non-conformance finding.", + "evidence": [ + "evidence-validation-failure" + ] + } + ], + "evidence": [ + { + "bom-ref": "evidence-validation-failure", + "description": "Illustrative failed validation result.", + "data": [ + { + "name": "evidence-validation-failure", + "contents": { + "attachment": { + "mediaType": "text/plain", + "content": "TR-042; subject: asset-tls-path; firmware 3.2.0; requirement SR-041. Method: replace gateway certificate, send unauthentic therapy command, observe delivery command acceptance. Expected: reject. Observed: accepted. Result: fail." + }, + "hashes": [ + { + "alg": "SHA-256", + "content": "c868000fc7be3ce59bdc4ae81e3d84ae26e3d866c1e785a3373b61b23b3a3762" + } + ] + } + } + ], + "created": "2026-10-16T12:00:00Z", + "author": { + "name": "Example safety engineering team" + } + }, + { + "bom-ref": "evidence-review", + "description": "Illustrative review and disposition record.", + "data": [ + { + "name": "evidence-review", + "contents": { + "attachment": { + "mediaType": "text/plain", + "content": "REVIEW-042; subject: asset-tls-path; assessment: assessment-validation-failure; disposition: request change, release blocked. Reason: failed SR-041 validation demonstrates an unauthentic command can enter the therapy path. Reviewer: Example product safety manager; process: Example lifecycle policy P-01 revision 1. CR-042 tracks the proposed modification and replacement verification; they are not completed by this assessment." + }, + "hashes": [ + { + "alg": "SHA-256", + "content": "26b4b2cb6f3745869847515810e42cca663ea0cb4ba832a629be2975115e76d7" + } + ] + } + } + ], + "created": "2026-10-16T12:00:00Z", + "author": { + "name": "Example safety engineering team" + } + } + ] + }, + "risks": { + "risks": [ + { + "bom-ref": "risk-validation-failure", + "name": "Failed command-integrity validation", + "statement": "The tested certificate replacement permits an unauthentic command and could cause unintended delivery.", + "domains": [ + { + "type": "safety" + } + ], + "affects": [ + "asset-tls-path", + "comp-pump-firmware" + ], + "inherentRisk": { + "impact": { + "level": "catastrophic", + "polarity": "harm", + "categories": [ + "safety" + ], + "description": "Incorrect drug delivery to a patient.", + "riskAttributes": [ + "integrity" + ] + }, + "rationale": "Example qualitative hazard-analysis method H-01 revision 1: catastrophic denotes potential death or permanent injury from unintended delivery. No event probability or numeric risk score is asserted." + }, + "status": "assessed", + "relatedDamageScenarios": [ + "damage-unintended-delivery" + ], + "relatedRequirements": [ + "req-command-integrity" + ], + "relatedClaims": [ + "claim-command-integrity-failure" + ] + } + ], + "assessments": [ + { + "bom-ref": "assessment-validation-failure", + "name": "Safety impact assessment of TR-042", + "type": [ + "safety" + ], + "cadence": "triggered", + "triggeredBy": [ + "trigger-validation-report" + ], + "timestamp": "2026-10-16T14:00:00Z", + "status": "completed", + "scope": "Pump firmware 3.2.0, Globex TLS 1.4.2, and the hospital deployment therapy command path. No claim about other products or deployments.", + "assessors": [ + { + "roles": [ + { + "role": "auditor" + } + ], + "organization": { + "name": "Acme Medical Systems" + } + } + ], + "risks": [ + "risk-validation-failure" + ], + "conclusion": "unacceptable", + "summary": "The impact assessment is completed, but SR-041 fails validation. Release is blocked under the example lifecycle process; correction and verification rerun remain open in the authoritative change record.", + "nextReview": "2026-12-12T00:00:00Z", + "externalReferences": [ + { + "type": "evidence", + "url": "urn:cdx:dde87342-8401-49d2-9898-223b1c5d03da/1#evidence-review", + "comment": "Review record distinguishes the completed assessment from unfinished corrective work." + }, + { + "type": "issue-tracker", + "url": "https://qms.example.invalid/changes/CR-042", + "comment": "Authoritative change record: firmware correction proposed; replacement verification to be added; previous result retained. No release approval recorded." + } + ], + "assumptions": [ + "This result applies to the tested certificate replacement and firmware 3.2.0. A later correction requires a new assessment and verification evidence." + ] + } + ], + "damageScenarios": [ + { + "bom-ref": "damage-unintended-delivery", + "name": "Unintended drug delivery", + "description": "An unauthentic therapy command changes delivery to a patient.", + "affects": [ + "asset-tls-path" + ], + "impacts": [ + { + "level": "catastrophic", + "categories": [ + "safety" + ], + "polarity": "harm", + "description": "Potential patient harm from unintended drug delivery; the example severity label is interpreted under the fictional manufacturer hazard-analysis process." + } + ] + } + ] + }, + "externalReferences": [ + { + "type": "perspective", + "url": "https://github.com/CycloneDX/specification/blob/2.0-dev/perspectives/safety-perspective.json" + } + ], + "annotations": [ + { + "bom-ref": "trigger-validation-report", + "subjects": [ + "asset-tls-path" + ], + "annotator": { + "organization": { + "name": "Example validation laboratory" + } + }, + "timestamp": "2026-10-16T12:00:00Z", + "text": "TR-042: validation of firmware 3.2.0 found that a gateway certificate replacement permits an unauthentic therapy command. This report initiates assessment; it does not prescribe or approve a change." + } + ] +} diff --git a/tools/src/test/resources/2.0/valid-safety-perspective-vulnerability-2.0.json b/tools/src/test/resources/2.0/valid-safety-perspective-vulnerability-2.0.json new file mode 100644 index 000000000..92a7de484 --- /dev/null +++ b/tools/src/test/resources/2.0/valid-safety-perspective-vulnerability-2.0.json @@ -0,0 +1,309 @@ +{ + "$schema": "https://cyclonedx.org/schema/2.0/cyclonedx-2.0.schema.json", + "$comment": "Illustrative Safety Perspective fixture. All organizations, records, and results are synthetic. The declarations carry evidence for review, not a safety certification or an independently established authorization.", + "specFormat": "CycloneDX", + "specVersion": "2.0", + "serialNumber": "urn:uuid:942f395e-482c-48c8-bcba-018678665c5d", + "version": 1, + "metadata": { + "timestamp": "2026-10-16T15:00:00Z" + }, + "definitions": { + "components": [ + { + "bom-ref": "comp-pump-firmware", + "type": "firmware", + "name": "acme-pump-firmware", + "version": "3.2.0", + "externalReferences": [ + { + "type": "hazard-analysis", + "url": "https://plm.acme-medical.example/safety/RA-2026-117" + } + ] + }, + { + "bom-ref": "comp-globex-tls", + "type": "library", + "name": "globex-tls", + "version": "1.4.2" + } + ], + "requirements": [ + { + "bom-ref": "req-command-integrity", + "id": "SR-041", + "name": "Therapy command integrity", + "type": [ + "safety" + ], + "description": "Only authentic therapy commands are accepted in the stated deployment.", + "rationale": "Command integrity prevents unintended drug delivery in the classified therapy path." + } + ] + }, + "blueprints": [ + { + "bom-ref": "bp-pump-deployment", + "name": "Infusion pump, hospital deployment", + "modelTypes": [ + "data-flow" + ], + "assets": [ + { + "bom-ref": "asset-tls-path", + "componentRef": "comp-globex-tls", + "classification": { + "criticality": "critical", + "safetyIntegrityLevels": [ + { + "scheme": "IEC-62304", + "level": "class-c" + } + ] + } + } + ] + } + ], + "vulnerabilities": [ + { + "bom-ref": "vuln-globex-tls-1", + "id": "GLOBEX-2026-0007", + "description": "Certificate name confusion in the Globex TLS library.", + "ratings": [ + { + "method": "CVSSv31", + "score": 7.4, + "severity": "high", + "vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:H" + } + ], + "analysis": { + "state": "not_affected", + "justification": "requires_configuration", + "detail": "The pump pins the gateway certificate, so name confusion is not reachable. Safety impact assessed separately, see the triggered assessment.", + "firstIssued": "2026-09-10T00:00:00Z" + }, + "affects": [ + { + "ref": "comp-globex-tls" + } + ] + } + ], + "declarations": { + "claims": [ + { + "bom-ref": "claim-tls-fmea", + "target": "asset-tls-path", + "predicate": "The vulnerability introduces no new hazardous failure mode on the therapy command path.", + "reasoning": "Re-evaluation of the therapy-path FMEA and a regression test of the actual certificate pinning configuration cover the vulnerability cause and the safety-relevant command path. This evidence supports the bounded claim, not all deployments or future library versions.", + "evidence": [ + "evidence-fmea-excerpt", + "evidence-pin-test" + ] + } + ], + "evidence": [ + { + "bom-ref": "evidence-fmea-excerpt", + "description": "Illustrative FMEA excerpt for the assessed TLS communication path.", + "data": [ + { + "name": "evidence-fmea-excerpt", + "contents": { + "attachment": { + "mediaType": "text/plain", + "content": "FMEA-041; firmware 3.2.0, Globex TLS 1.4.2, hospital deployment. Cause reviewed: certificate name confusion. Potential consequence: unauthentic therapy command and unintended delivery. Control: pin the expected gateway certificate. The control prevents this vulnerability cause in the assessed configuration; its applicability is supported by the recorded regression result. No probability or numeric safety score is asserted." + }, + "hashes": [ + { + "alg": "SHA-256", + "content": "259fc5594ac4fc1ad8c97dbe4b74653bc8e8b6355eba5f4be33e1a389eab950d" + } + ] + } + } + ], + "created": "2026-10-16T12:00:00Z", + "author": { + "name": "Example safety engineering team" + } + }, + { + "bom-ref": "evidence-pin-test", + "description": "Illustrative regression result for the assessed configuration.", + "data": [ + { + "name": "evidence-pin-test", + "contents": { + "attachment": { + "mediaType": "text/plain", + "content": "SR-041 regression: firmware 3.2.0, Globex TLS 1.4.2, pinned hospital gateway certificate. A mismatched name is rejected; an authenticated valid command succeeds. Result: pass." + }, + "hashes": [ + { + "alg": "SHA-256", + "content": "e65389466e8b1c1b14b27f457100aa7d83bd0b2ce34c5189171a368cd1585ed6" + } + ] + } + } + ], + "created": "2026-10-16T12:00:00Z", + "author": { + "name": "Example safety engineering team" + } + }, + { + "bom-ref": "evidence-review", + "description": "Illustrative review record retained by the manufacturer.", + "data": [ + { + "name": "evidence-review", + "contents": { + "attachment": { + "mediaType": "text/plain", + "content": "Record REVIEW-041; subject: asset-tls-path, pump firmware 3.2.0, hospital deployment; assessment: assessment-2026-09-12; rationale: FMEA plus configuration-specific regression evidence support no additional safety-driven change. Reviewer: Example product safety manager; process: Example lifecycle policy P-01 revision 1. This synthetic record does not authenticate that identity or establish its authority for a relying party." + }, + "hashes": [ + { + "alg": "SHA-256", + "content": "e2f2a046ab8cee60d262b1d2ea5627aa07a81d27254d8db320f265f34bfb616e" + } + ] + } + } + ], + "created": "2026-10-16T12:00:00Z", + "author": { + "name": "Example safety engineering team" + } + } + ] + }, + "risks": { + "risks": [ + { + "bom-ref": "risk-tls-vuln-safety", + "name": "Vulnerability GLOBEX-2026-0007 on the therapy command path", + "statement": "If the certificate name confusion were exploitable on the pump, therapy commands could be tampered with, causing incorrect drug delivery.", + "domains": [ + { + "type": "safety" + } + ], + "affects": [ + "asset-tls-path", + "comp-pump-firmware" + ], + "relatedVulnerabilities": [ + "vuln-globex-tls-1" + ], + "inherentRisk": { + "impact": { + "level": "catastrophic", + "polarity": "harm", + "categories": [ + "safety" + ], + "description": "Incorrect drug delivery to a patient.", + "riskAttributes": [ + "integrity" + ] + }, + "rationale": "Example qualitative hazard-analysis method H-01 revision 1: catastrophic denotes potential death or permanent injury from unintended delivery. No event probability or numeric risk score is asserted." + }, + "residualRisk": { + "impact": { + "level": "catastrophic", + "polarity": "harm", + "categories": [ + "safety" + ], + "riskAttributes": [ + "integrity" + ] + }, + "rationale": "Certificate pinning makes the vulnerability unreachable in the deployed configuration." + }, + "status": "assessed", + "relatedDamageScenarios": [ + "damage-unintended-delivery" + ] + } + ], + "assessments": [ + { + "bom-ref": "assessment-2026-09-12", + "name": "Safety impact reassessment for GLOBEX-2026-0007", + "type": [ + "safety", + "vulnerability" + ], + "cadence": "triggered", + "triggeredBy": [ + "vuln-globex-tls-1" + ], + "timestamp": "2026-10-16T14:00:00Z", + "status": "completed", + "scope": "Pump firmware 3.2.0, Globex TLS 1.4.2, and the hospital deployment therapy command path. No claim about other products or deployments.", + "assessors": [ + { + "roles": [ + { + "role": "auditor" + } + ], + "organization": { + "name": "Acme Medical Systems" + } + } + ], + "risks": [ + "risk-tls-vuln-safety" + ], + "conclusion": "acceptable", + "summary": "The assessment concludes that this vulnerability adds no safety impact in the stated configuration. Completed analysis is not release authorization; the responsible organization must evaluate the supporting records under its own policy.", + "nextReview": "2026-12-12T00:00:00Z", + "externalReferences": [ + { + "type": "evidence", + "url": "urn:cdx:942f395e-482c-48c8-bcba-018678665c5d/1#evidence-review", + "comment": "Illustrative authoritative lifecycle review record; the receiver must separately establish issuer identity, scope, and authority." + } + ], + "assumptions": [ + "The assessed configuration pins the gateway certificate. Reassess this conclusion if certificate handling, library version, deployment, or relevant requirements change." + ] + } + ], + "damageScenarios": [ + { + "bom-ref": "damage-unintended-delivery", + "name": "Unintended drug delivery", + "description": "An unauthentic therapy command changes delivery to a patient.", + "affects": [ + "asset-tls-path" + ], + "impacts": [ + { + "level": "catastrophic", + "categories": [ + "safety" + ], + "polarity": "harm", + "description": "Potential patient harm from unintended drug delivery; the example severity label is interpreted under the fictional manufacturer hazard-analysis process." + } + ] + } + ] + }, + "externalReferences": [ + { + "type": "perspective", + "url": "https://github.com/CycloneDX/specification/blob/2.0-dev/perspectives/safety-perspective.json" + } + ] +}