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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/srac/** text eol=lf
2 changes: 1 addition & 1 deletion .github/workflows/internal_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ jobs:
internal_tests:
uses: eclipse-score/cicd-workflows/.github/workflows/tests.yml@37eaadadfefc263d94a858d25665735659e916fa
with:
bazel-target: "test //scripts/tooling:tooling_tests //scripts/known_good:known_good_tests"
bazel-target: "test //scripts/tooling:tooling_tests //scripts/known_good:known_good_tests //srac:srac_projection_tests"
66 changes: 66 additions & 0 deletions srac/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************
load("@rules_python//python:defs.bzl", "py_binary", "py_library")
load("@score_tooling//python_basics:defs.bzl", "score_py_pytest")

py_library(
name = "profile",
srcs = [
"__init__.py",
"tools/__init__.py",
"tools/profile.py",
],
visibility = ["//visibility:public"],
)

py_library(
name = "needs_export",
srcs = ["tools/export_needs.py"],
visibility = ["//visibility:public"],
deps = [":profile"],
)

py_binary(
name = "export_needs",
srcs = ["tools/export_needs_cli.py"],
main = "tools/export_needs_cli.py",
visibility = ["//visibility:public"],
deps = [":needs_export"],
)

score_py_pytest(
name = "srac_projection_tests",
srcs = ["tests/test_needs_export.py"],
data = [
"examples/generated/communication.sbom-reference.json",
"examples/generated/communication.srac.json",
"examples/needs/communication.export.json",
"examples/needs/communication.needs.json",
"schema/srac.schema.json",
"schema/srac-report.schema.json",
],
pytest_config = "//:pyproject.toml",
deps = [
":needs_export",
":profile",
],
)

exports_files([
"examples/generated/communication.sbom-reference.json",
"examples/generated/communication.srac.json",
"examples/needs/communication.export.json",
"examples/needs/communication.needs.json",
"schema/srac-report.schema.json",
"schema/srac.schema.json",
])
78 changes: 78 additions & 0 deletions srac/NEEDS_EXPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Exporting S-CORE safety metadata from `needs.json`

This experiment treats the S-CORE Sphinx-needs model as the single source of truth. It does not ask a component team to copy its
safety classification, rationale, requirements, evidence or review state into a second hand-authored file.

```text
authoritative Sphinx-needs model
needs.json
|
| read-only, deterministic projection
v
portable SRAC safety metadata
|
| SPDX/CycloneDX external reference
v
SPDX or CycloneDX SBOM
|
| discovery and SHA-256 verification
v
receiving tool
```

## What is authored and what is derived

`communication.export.json` contains only transport configuration: the root need ID, explicitly included need IDs, component
PURL/version/repository, publication URI and reproducible creation timestamp. It contains no safety classification or engineering
decision.

The exporter reads the following values from `needs.json`:

- `safety` and `safety_relevant` for the projected relevance and classification;
- `status` as source lifecycle provenance, without translating `valid` into an SRAC approval;
- `id`, `type`, `docname` and `external_url` for stable source references; and
- the Sphinx-needs version and exact input digest.

The generated assertion remains `draft` because the export itself has not received a separate approval. The source need and its
S-CORE review process remain authoritative. Missing or contradictory safety fields fail closed as `undetermined` or an error;
the exporter never infers safety relevance from a title, path or relationship.

The exporter accepts only the S-CORE source vocabulary `QM`, `ASIL_B` and `ASIL_D`. It does not emit ASIL-A, ASIL-C, SIL or DAL.
An absent classification is exported as `undetermined` / `not-assigned`; an explicitly unsupported value is rejected rather than
silently translated. Functional-safety relevance and security relevance remain separate: security belongs in the impact-analysis
or SBOM/VEX security model, not in `safetyRelevance`.

## Run the minimal example

The checked-in `communication.needs.json` is a reduced fixture taken from the public S-CORE `needs.json` shape. It includes a
Feature, Component and Assumption of Use so the mapping gap is visible without checking in the full generated site artifact.

```bash
bazel run //srac:export_needs -- \
--needs srac/examples/needs/communication.needs.json \
--config srac/examples/needs/communication.export.json \
--output /tmp/communication.srac.json \
--reference-output /tmp/communication.sbom-reference.json
```

The expected outputs are checked in under `examples/generated/`. The reference manifest shows the proposed SPDX 2.3 `OTHER`
external reference and CycloneDX 1.6 `other` external reference. The synthetic carrier SBOMs in that directory demonstrate the
same references in context. `receive_srac` discovers the reference, resolves a previously retrieved local artifact, verifies its
SHA-256 before parsing, validates the assertion and only then correlates it to the referenced component. It does not perform
network retrieval or modify an SBOM.

## Mapping and feedback for SPDX Functional Safety

| S-CORE source concept | Export behavior | Standards feedback |
|---|---|---|
| Feature (`feat`) | Preserved as a typed Sphinx-needs evidence reference | A portable model needs an explicit Feature/Dependable Element level or a documented mapping. |
| Component (`comp`) | Used as the projection root and preserved by ID/type/URI | Component identity must remain distinct from an SBOM package and from a source folder. |
| Unit | Not present in this reduced public example | The SPDX mapping needs an unambiguous Unit level below Component. |
| Assumption of Use (`aou_req`) | Preserved as a typed requirement reference | SEooC exchanges need first-class AoU semantics, applicability and satisfaction/verification links. |
| `safety: QM`, `ASIL_B` or `ASIL_D` | Projected exactly to the corresponding S-CORE functional-safety value | Safety integrity is contextual and must not become a global package property. |
| Unsupported ASIL/SIL/DAL or security value | Export rejected | The S-CORE exporter must not imply support for a vocabulary the source model does not use. |
| `status: valid` | Recorded in `sourceOfTruth.rootElementStatus` | Source lifecycle state must not be silently reinterpreted as an external approval. |
| Sphinx-needs relationships | Source IDs remain resolvable; no new relation is inferred | Dependable Element / Feature / Component / Unit and AoU relationships need lossless standardized predicates. |

This makes the PoC useful as a standards feedback vehicle: it demonstrates transport from an existing safety model while making
the semantic gaps explicit, rather than creating a parallel safety argumentation system.
32 changes: 32 additions & 0 deletions srac/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# SRAC canonical projection and Sphinx-Needs adapter

This directory contains the first production-oriented slice of the Safety Relevance Assertion Capability (SRAC) work.

The implementation reads selected records from an authoritative Sphinx-Needs `needs.json` file and produces a deterministic, validated portability artifact. It does not create, infer, review or approve a safety decision. The lifecycle source remains authoritative.

This draft implementation is intentionally limited to:

- the canonical SRAC projection and validation contract;
- a read-only Sphinx-Needs adapter;
- deterministic example output and an integrity-bound SBOM reference manifest; and
- focused positive and fail-closed tests.

The Eclipse S-CORE proof-of-concept pilots, receiver integrations, signatures and attestations are outside this change. Production adoption is subject to the architecture decision proposed in [eclipse-score/score#3318](https://github.com/eclipse-score/score/pull/3318).

## Run the tests

```bash
bazel test //srac:srac_projection_tests
```

## Generate the example

```bash
bazel run //srac:export_needs -- \
--needs srac/examples/needs/communication.needs.json \
--config srac/examples/needs/communication.export.json \
--output /tmp/communication.srac.json \
--reference-output /tmp/communication.sbom-reference.json
```

See [NEEDS_EXPORT.md](NEEDS_EXPORT.md) for the mapping boundary and supported S-CORE vocabulary.
13 changes: 13 additions & 0 deletions srac/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************
"""SRAC sidecar proof of concept."""
23 changes: 23 additions & 0 deletions srac/examples/generated/communication.sbom-reference.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"artifact": {
"uri": "https://example.org/srac/communication.srac.json",
"sha256": "2108209f8a367eb370de5082b034ac015d6c9df9606c7a065c974d5c08344eea"
},
"spdx23PackageExternalRef": {
"referenceCategory": "OTHER",
"referenceType": "srac",
"referenceLocator": "https://example.org/srac/communication.srac.json",
"comment": "SRAC sidecar; SHA-256: 2108209f8a367eb370de5082b034ac015d6c9df9606c7a065c974d5c08344eea"
},
"cycloneDx16ExternalReference": {
"type": "other",
"url": "https://example.org/srac/communication.srac.json",
"comment": "SRAC sidecar",
"hashes": [
{
"alg": "SHA-256",
"content": "2108209f8a367eb370de5082b034ac015d6c9df9606c7a065c974d5c08344eea"
}
]
}
}
57 changes: 57 additions & 0 deletions srac/examples/generated/communication.srac.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"schemaVersion": "0.2-draft",
"assertionId": "srac-needs-export-comp__com_configuration-0.1",
"subject": {
"name": "score_communication::configuration",
"purl": "pkg:github/eclipse-score/communication@313f1178628189d267c8cd0d75971bdac04c20ee#score/mw/com/configuration",
"version": "313f1178628189d267c8cd0d75971bdac04c20ee",
"repository": "https://github.com/eclipse-score/communication",
"componentPath": "score/mw/com/configuration"
},
"safetyRelevance": {
"status": "safety-related",
"classification": "ASIL-B"
},
"rationale": "Read-only projection of S-CORE need 'comp__com_configuration' at needs.json version '0.1'. The source lifecycle status is 'valid'. The export adds no safety decision; the referenced Sphinx-needs model remains authoritative.",
"requirements": [
{
"id": "aou_req__communication__1",
"type": "sphinx-needs:aou_req",
"uri": "https://eclipse-score.github.io/score/main/modules/communication/docs/requirements/aou_req.html#aou_req__communication__1"
}
],
"evidence": [
{
"id": "comp__com_configuration",
"type": "sphinx-needs:comp",
"uri": "https://eclipse-score.github.io/score/main/modules/communication/configuration/docs/architecture/index.html#comp__com_configuration"
},
{
"id": "feat__com_communication",
"type": "sphinx-needs:feat",
"uri": "https://eclipse-score.github.io/score/main/features/communication/architecture/index.html#feat__com_communication"
}
],
"sourceOfTruth": {
"system": "sphinx-needs",
"documentUri": "https://eclipse-score.github.io/score/main/needs.json",
"version": "0.1",
"rootElement": "comp__com_configuration",
"rootElementType": "comp",
"rootElementStatus": "valid",
"includedElements": [
"feat__com_communication",
"aou_req__communication__1"
],
"sha256": "8741a2702fa43193f3bb31a0e3b1d38105dcaffa565ba5bd6eced4a9d87e7dfa"
},
"assertion": {
"status": "draft",
"author": {
"name": "S-CORE needs.json export",
"uri": "https://eclipse-score.github.io/score/main/needs.json"
},
"reviewer": null,
"created": "2026-09-29T00:00:00Z"
}
}
19 changes: 19 additions & 0 deletions srac/examples/needs/communication.export.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"sourceVersion": "0.1",
"sourceDocumentUri": "https://eclipse-score.github.io/score/main/needs.json",
"documentationBaseUri": "https://eclipse-score.github.io/score/main",
"rootNeedId": "comp__com_configuration",
"includedNeedIds": [
"feat__com_communication",
"aou_req__communication__1"
],
"subject": {
"name": "score_communication::configuration",
"purl": "pkg:github/eclipse-score/communication@313f1178628189d267c8cd0d75971bdac04c20ee#score/mw/com/configuration",
"version": "313f1178628189d267c8cd0d75971bdac04c20ee",
"repository": "https://github.com/eclipse-score/communication",
"componentPath": "score/mw/com/configuration"
},
"artifactUri": "https://example.org/srac/communication.srac.json",
"created": "2026-09-29T00:00:00Z"
}
52 changes: 52 additions & 0 deletions srac/examples/needs/communication.needs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"current_version": "0.1",
"project": "S-CORE",
"project_url": "https://eclipse-score.github.io/score",
"versions": {
"0.1": {
"creator": {
"program": "sphinx_needs",
"version": "8.5.0"
},
"needs": {
"feat__com_communication": {
"id": "feat__com_communication",
"title": "Communication Feature",
"type": "feat",
"type_name": "Feature",
"docname": "features/communication/architecture/index",
"safety": "ASIL_B",
"security": "YES",
"status": "valid",
"version": 1
},
"comp__com_configuration": {
"id": "comp__com_configuration",
"title": "Lola Configuration",
"type": "comp",
"type_name": "Component",
"docname": "modules/communication/configuration/docs/architecture/index",
"belongs_to": [
"feat__com_communication[version==1]"
],
"safety": "ASIL_B",
"security": "YES",
"status": "valid",
"version": 1
},
"aou_req__communication__1": {
"id": "aou_req__communication__1",
"title": "Monotonic Semi-Dynamic Memory Allocation",
"type": "aou_req",
"type_name": "Assumption of Use Requirement",
"docname": "modules/communication/docs/requirements/aou_req",
"content": "It shall be ensured that enough memory is configured for shared memory instances, in order that LoLa can perform all necessary allocations.",
"safety": "ASIL_B",
"security": "NO",
"status": "valid",
"version": 1
}
}
}
}
}
Loading
Loading