Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions perspectives/README.md
Original file line number Diff line number Diff line change
@@ -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.
108 changes: 108 additions & 0 deletions perspectives/safety-perspective.json
Original file line number Diff line number Diff line change
@@ -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."
}
]
}
]
}
4 changes: 3 additions & 1 deletion tools/src/test/js/schema-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -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"
}
}
Loading