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
47 changes: 47 additions & 0 deletions docs/internals/requirements/requirements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -471,14 +471,52 @@ Versioning
================================ ========================================================


.. tool_req:: Safety/security classification consistency across relations
:id: tool_req__docs_safety_security_relation
:tags: Common Attributes
:version: 1
:implemented: NO
:parent_covered: YES
:satisfies:
gd_req__req_linkage_safety[version==1],
gd_req__arch_linkage_requirement[version==1],
gd_req__arch_linkage_safety_trace[version==1],
gd_req__arch_linkage_security_trace[version==1]

Docs-as-Code shall flag any ``status == valid`` need that is linked via a checked relation to a need
whose ``safety`` or ``security`` value violates the relation's safety-flow constraint.
Each relation enforces a specific direction: the *safety owner* end determines the constraint, and the other end must match (same safety/security value).

The following table specifies the safety-flow direction and flagged mismatch per relation:

.. list-table::
:header-rows: 1

* - Relation
- Safety Owner
- Flagged Mismatch
* - ``implements``, ``covers``, ``includes``, ``consists_of``, ``uses``, ``provides``, ``mitigated_by``
- Source
- ASIL source + QM target
* - ``satisfied_by``, ``derived_from``, ``fulfils``, ``belongs_to``, ``included_by``
- Target
- QM source + ASIL target
* - ``contains``, ``has``, ``input``, ``output``, ``responsible``, ``approved_by``, ``supported_by``, ``complies``, ``realizes``, ``satisfies``, ``violates``, ``fully_verifies``, ``partially_verifies``, ``evidence``
- Not checked
- —


.. tool_req:: Safety: enforce safe linking
:id: tool_req__docs_common_attr_safety_link_check
:tags: Common Attributes
:version: 1
:implemented: YES
:parent_covered: YES
:status: invalid
:satisfies: gd_req__req_linkage_safety[version==1]

OBSOLETE: Superseded by :need:`tool_req__docs_safety_security_relation`.

QM requirements (safety == QM) shall not be linked to safety requirements (safety != QM) via the ``derived_from`` attribute.

.. tool_req:: Requirement linkage to AoU via covers
Expand Down Expand Up @@ -618,9 +656,12 @@ Architecture Attributes
:tags: Architecture
:implemented: PARTIAL
:version: 2
:status: invalid
:satisfies: gd_req__arch_linkage_requirement[version==1]
:parent_covered: YES

OBSOLETE: Superseded by :need:`tool_req__docs_safety_security_relation`.

Docs-as-Code shall enforce that architecture elements of type
:need:`tool_req__docs_arch_types` with ``safety == QM`` are not linked to requirements
of type :need:`tool_req__docs_req_types` with ``safety != QM``.
Expand All @@ -631,11 +672,14 @@ Architecture Attributes
:tags: Architecture
:implemented: YES
:version: 2
:status: invalid
:satisfies:
gd_req__arch_linkage_safety_trace[version==1],
gd_req__req_linkage_safety[version==1]
:parent_covered: YES

OBSOLETE: Superseded by :need:`tool_req__docs_safety_security_relation`.

Docs-as-Code shall enforce that valid safety architectural elements (Safety != QM) can
only be linked against valid safety architectural elements.

Expand All @@ -644,9 +688,12 @@ Architecture Attributes
:tags: Architecture
:implemented: YES
:version: 1
:status: invalid
:parent_covered: YES
:satisfies: gd_req__arch_linkage_security_trace[version==1]

OBSOLETE: Superseded by :need:`tool_req__docs_safety_security_relation`.

Docs-as-Code shall enforce that security relevant :need:`tool_req__docs_arch_types` (Security ==
YES) can only be linked against security relevant :need:`tool_req__docs_arch_types`.

Expand Down
90 changes: 90 additions & 0 deletions src/extensions/score_metamodel/checks/graph_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,93 @@ def check_valid_only_links_to_valid(
if invalid_needs:
msg = f"is valid but links to invalid need(s): {invalid_needs}"
log.warning_for_need(need, msg, is_new_check=True)


# Relations whose safety/security classification must match across the link.
# Source-owned (the source determines the constraint):
# implements, covers, includes, consists_of, uses, provides, mitigated_by
# Target-owned (the target determines the constraint):
# satisfied_by, derived_from, fulfils, belongs_to, included_by
# All other relations (e.g. complies, satisfies, fully_verifies, evidence, ...)
# are "Not checked" per tool_req__docs_safety_security_relation and skipped.
CHECKED_RELATIONS: frozenset[str] = frozenset(
{
"implements",
"covers",
"includes",
"consists_of",
"uses",
"provides",
"mitigated_by",
"satisfied_by",
"derived_from",
"fulfils",
"belongs_to",
"included_by",
}
)


# req-Id: tool_req__docs_safety_security_relation
@graph_check
def check_safety_security_relation(
app: Sphinx,
all_needs: NeedsView,
log: CheckLogger,
):
"""Flag safety/security classification mismatches across checked relations.

A ``status == valid`` need linked via a checked relation to a need whose
``safety`` (or ``security``) value differs is flagged. Both directions are
covered: source-owned relations flag an ASIL source linking a QM target, and
target-owned relations flag a QM source linking an ASIL target — together any
ASIL/QM (or YES/NO) mismatch across a checked relation is flagged.

Only valid *sources* are checked; invalid/draft sources are ignored. Targets
lacking the attribute are skipped (no false positive).
"""
needs_dict_all = {need["id"]: need for need in all_needs.values()}

for need in all_needs.filter_is_external(False).values():
if need.get("status") != "valid":
continue
src_safety = need.get("safety")
src_security = need.get("security")
if src_safety is None and src_security is None:
continue

# need._links: relation name -> iterable of NeedLink (each has .id)
for relation, targets in need._links.items(): # type: ignore[attr-defined]
if relation not in CHECKED_RELATIONS:
continue
for target in targets:
parent = needs_dict_all.get(target.id)
if parent is None:
continue
_check_attr_mismatch(need, parent, relation, "safety", src_safety, log)
_check_attr_mismatch(
need, parent, relation, "security", src_security, log
)


def _check_attr_mismatch(
need: NeedItem,
parent: NeedItem,
relation: str,
attr: str,
src_value: str | None,
log: CheckLogger,
) -> None:
"""Flag a classification mismatch for one attribute of one linked target."""
if src_value is None:
return
target_value = parent.get(attr)
if target_value is None or target_value == src_value:
return
log.warning_for_need(
need,
f"{attr} classification mismatch via `{relation}`: "
f"source is `{src_value}` but `{parent['id']}` is "
f"`{target_value}`. {attr.capitalize()}-relevant and "
f"non-{attr} elements must not be linked.",
)
42 changes: 0 additions & 42 deletions src/extensions/score_metamodel/metamodel.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1133,48 +1133,6 @@ needs_extra_links:
##############################################################

graph_checks:
# req-Id: tool_req__docs_common_attr_safety_link_check
tool_req__docs_common_attr_safety_link_check:
needs:
include: stkh_req, feat_req, comp_req, aou_req, gd_req, tool_req
condition: safety == QM
check:
derived_from: safety == QM
explanation: QM requirements cannot be derived from ASIL requirements.

# req-Id: tool_req__docs_arch_link_qm_to_safety_req
tool_req__docs_arch_link_qm_to_safety_req:
needs:
include: feat_arc_sta, logic_arc_int, logic_arc_int_op, comp_arc_sta, real_arc_int, real_arc_int_op
condition: safety == QM
check:
fulfils: safety != QM
explanation: An QM architecture element cannot implement ASIL requirements.

# req-Id: tool_req__docs_req_arch_link_safety_to_arch
tool_req__docs_req_arch_link_safety_to_arch:
needs:
include: feat_arc_sta, logic_arc_int, logic_arc_int_op, comp_arc_sta, real_arc_int, real_arc_int_op
condition:
and:
- safety != QM
- status == valid
check:
implements: # TODO: which attribute???
and:
- safety != QM
- status == valid
explanation: An safety architecture element can only link other safety architecture elements.

# req-Id: tool_req__docs_arch_link_security
tool_req__docs_arch_link_security:
needs:
include: feat_arc_sta, logic_arc_int, logic_arc_int_op, comp_arc_sta, real_arc_int, real_arc_int_op
condition: security == YES
check:
implements: security == YES # Which attribute???
explanation: An security architecture element can only link other security architecture elements.

# Workproducts may only link to ASPICE 40 IIC stakeholder requirements
workproduct_aspice_40:
needs:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ Component 1
:safety: ASIL_B
:status: valid
:includes: comp__test_component_1, comp__test_component_2
:expect: safety classification mismatch via `includes`

.. mod_view_sta:: Feature Test Module 1 Static View
:id: mod_view_sta__test_feature_1_module_1__test_static_view_1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

.. test_metadata::
:id: test_metadata__valid_links_to_valid
:partially_verifies_list: tool_req__docs_req_arch_link_safety_to_arch
:partially_verifies_list: tool_req__docs_safety_security_relation
:test_type: requirements_based
:derivation_technique: requirements_based

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,67 +14,113 @@

.. test_metadata::
:id: test_metadata__metamodel_graph_checks
:partially_verifies_list: tool_req__docs_common_attr_safety_link_check
:version: 1
:partially_verifies_list: tool_req__docs_safety_security_relation
:test_type: requirements_based
:derivation_technique: requirements_based

Tests if metamodel graph checks work as defined / intended
Tests the safety/security classification consistency check across relations.


.. Checks if the child requirement has the at least the same safety level as the parent requirement. It's allowed to "overfill" the safety level of the parent.
.. ASIL decomposition is not foreseen in S-CORE. Therefore it's not allowed to have a child requirement with a lower safety level than the parent requirement as
.. it is possible in an decomposition case.
.. feat_req:: Parent requirement QM
:id: feat_req__parent__QM
.. Setup: a QM and an ASIL_B stakeholder requirement, both valid.

.. stkh_req:: Parent requirement QM
:id: stkh_req__graph__parent_qm
:version: 1
:reqtype: Functional
:safety: QM
:security: YES
:rationale: Setup target for the derived_from tests.
:valid_from: v0.1
:status: valid



.. feat_req:: Parent requirement ASIL_B
:id: feat_req__parent__ASIL_B
.. stkh_req:: Parent requirement ASIL_B
:id: stkh_req__graph__parent_asil_b
:version: 1
:reqtype: Functional
:safety: ASIL_B
:security: YES
:rationale: Setup target for the derived_from tests.
:valid_from: v0.1
:status: valid



.. Positive Test: Child requirement QM. Parent requirement has the correct related safety level. Parent requirement is `QM`.
.. Positive Test: matching safety (QM -> QM) via derived_from is not flagged.

.. feat_req:: Child requirement 1
:id: feat_req__child__1
:id: feat_req__graph__child_1
:version: 1
:reqtype: Functional
:safety: QM
:derived_from: feat_req__parent__QM
:security: YES
:valid_from: v0.1
:status: valid
:expect_not: safety requirement
:derived_from: stkh_req__graph__parent_qm
:expect_not: mismatch



.. Positive Test: Child requirement ASIL B. Parent requirement has the correct related safety level. Parent requirement is `QM`.
.. Positive Test: matching safety (ASIL_B -> ASIL_B) via derived_from is not flagged.

.. feat_req:: Child requirement 2
:id: feat_req__child__2
:id: feat_req__graph__child_2
:version: 1
:reqtype: Functional
:safety: ASIL_B
:derived_from: feat_req__parent__ASIL_B
:security: YES
:valid_from: v0.1
:status: valid
:expect_not: safety
:derived_from: stkh_req__graph__parent_asil_b
:expect_not: mismatch



.. Negative Test: Child requirement QM. Parent requirement is `ASIL_B`. Child cant fulfill the safety level of the parent.
.. Negative Test: ASIL_B source derived_from a QM target is flagged
(source-owned relation, reverse direction previously unchecked).

.. comp_req:: Child requirement 3
:id: feat_req__qm_child_with_asil_parent
:safety: QM
:derived_from: feat_req__parent__ASIL_B
.. feat_req:: Child requirement 3
:id: feat_req__graph__child_3
:version: 1
:reqtype: Functional
:safety: ASIL_B
:security: YES
:valid_from: v0.1
:status: valid
:expect: QM requirements cannot be derived from ASIL requirements.
:derived_from: stkh_req__graph__parent_qm
:expect: safety classification mismatch via `derived_from`



.. Parent requirement does not exist
.. Negative Test: QM source derived_from an ASIL_B target is flagged
(target-owned relation).

.. feat_req:: Child requirement 4
:id: feat_req__linking_to_unknown_parent
:id: feat_req__graph__child_4
:version: 1
:reqtype: Functional
:safety: QM
:security: YES
:valid_from: v0.1
:status: valid
:derived_from: stkh_req__graph__parent_asil_b
:expect: safety classification mismatch via `derived_from`



.. Negative Test (dead link): target does not exist.
This warning comes from sphinx-needs core dead-link detection, not from the
score_metamodel graph checks, so it survives the rewrite.

.. feat_req:: Child requirement 5
:id: feat_req__graph__child_5
:version: 1
:reqtype: Functional
:safety: ASIL_B
:security: YES
:valid_from: v0.1
:status: valid
:derived_from: feat_req__parent0__abcd
:derived_from: feat_req__graph__does_not_exist
:expect: unknown outgoing link
Loading
Loading