From 9fac0ba1fd6c03be530768ec75e324f0e75b73a1 Mon Sep 17 00:00:00 2001 From: Andreas Zwinkau Date: Thu, 13 Aug 2026 14:42:41 +0200 Subject: [PATCH 1/3] docs: Add tool_req__docs_safety_security_relation --- docs/internals/requirements/requirements.rst | 47 +++++++++++++++++++ src/extensions/score_metamodel/metamodel.yaml | 8 ++-- .../tests/rst/graph/test_invalid_graph.rst | 2 +- .../tests/rst/graph/test_metamodel_graph.rst | 2 +- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/docs/internals/requirements/requirements.rst b/docs/internals/requirements/requirements.rst index cec3ee611..5fc7c0d28 100644 --- a/docs/internals/requirements/requirements.rst +++ b/docs/internals/requirements/requirements.rst @@ -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 @@ -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``. @@ -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. @@ -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`. diff --git a/src/extensions/score_metamodel/metamodel.yaml b/src/extensions/score_metamodel/metamodel.yaml index f81584a6e..19dbce0f6 100644 --- a/src/extensions/score_metamodel/metamodel.yaml +++ b/src/extensions/score_metamodel/metamodel.yaml @@ -1133,7 +1133,7 @@ needs_extra_links: ############################################################## graph_checks: - # req-Id: tool_req__docs_common_attr_safety_link_check + # req-Id: tool_req__docs_safety_security_relation tool_req__docs_common_attr_safety_link_check: needs: include: stkh_req, feat_req, comp_req, aou_req, gd_req, tool_req @@ -1142,7 +1142,7 @@ graph_checks: derived_from: safety == QM explanation: QM requirements cannot be derived from ASIL requirements. - # req-Id: tool_req__docs_arch_link_qm_to_safety_req + # req-Id: tool_req__docs_safety_security_relation 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 @@ -1151,7 +1151,7 @@ graph_checks: fulfils: safety != QM explanation: An QM architecture element cannot implement ASIL requirements. - # req-Id: tool_req__docs_req_arch_link_safety_to_arch + # req-Id: tool_req__docs_safety_security_relation 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 @@ -1166,7 +1166,7 @@ graph_checks: - status == valid explanation: An safety architecture element can only link other safety architecture elements. - # req-Id: tool_req__docs_arch_link_security + # req-Id: tool_req__docs_safety_security_relation 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 diff --git a/src/extensions/score_metamodel/tests/rst/graph/test_invalid_graph.rst b/src/extensions/score_metamodel/tests/rst/graph/test_invalid_graph.rst index 348d3dda3..3561fdc19 100644 --- a/src/extensions/score_metamodel/tests/rst/graph/test_invalid_graph.rst +++ b/src/extensions/score_metamodel/tests/rst/graph/test_invalid_graph.rst @@ -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 diff --git a/src/extensions/score_metamodel/tests/rst/graph/test_metamodel_graph.rst b/src/extensions/score_metamodel/tests/rst/graph/test_metamodel_graph.rst index 112bb702c..3e1d34123 100644 --- a/src/extensions/score_metamodel/tests/rst/graph/test_metamodel_graph.rst +++ b/src/extensions/score_metamodel/tests/rst/graph/test_metamodel_graph.rst @@ -14,7 +14,7 @@ .. test_metadata:: :id: test_metadata__metamodel_graph_checks - :partially_verifies_list: tool_req__docs_common_attr_safety_link_check + :partially_verifies_list: tool_req__docs_safety_security_relation :test_type: requirements_based :derivation_technique: requirements_based From 5354969d7c5b9122ce238d0e188167ed1efed422 Mon Sep 17 00:00:00 2001 From: Andreas Zwinkau Date: Thu, 13 Aug 2026 15:37:23 +0200 Subject: [PATCH 2/3] feat: implement and test tool_req__docs_safety_security_relation --- .../score_metamodel/checks/graph_checks.py | 90 ++++++++++ .../rst/architecture/architecture_tests.rst | 1 + .../tests/rst/graph/test_metamodel_graph.rst | 96 ++++++++--- .../graph/test_safety_security_relation.rst | 109 ++++++++++++ .../tests/test_graph_checks.py | 163 ++++++++++++++++++ 5 files changed, 434 insertions(+), 25 deletions(-) create mode 100644 src/extensions/score_metamodel/tests/rst/graph/test_safety_security_relation.rst diff --git a/src/extensions/score_metamodel/checks/graph_checks.py b/src/extensions/score_metamodel/checks/graph_checks.py index b6c854956..ef075dca3 100644 --- a/src/extensions/score_metamodel/checks/graph_checks.py +++ b/src/extensions/score_metamodel/checks/graph_checks.py @@ -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.", + ) diff --git a/src/extensions/score_metamodel/tests/rst/architecture/architecture_tests.rst b/src/extensions/score_metamodel/tests/rst/architecture/architecture_tests.rst index 47b5e97f3..5cca7e4b7 100644 --- a/src/extensions/score_metamodel/tests/rst/architecture/architecture_tests.rst +++ b/src/extensions/score_metamodel/tests/rst/architecture/architecture_tests.rst @@ -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 diff --git a/src/extensions/score_metamodel/tests/rst/graph/test_metamodel_graph.rst b/src/extensions/score_metamodel/tests/rst/graph/test_metamodel_graph.rst index 3e1d34123..9241b202b 100644 --- a/src/extensions/score_metamodel/tests/rst/graph/test_metamodel_graph.rst +++ b/src/extensions/score_metamodel/tests/rst/graph/test_metamodel_graph.rst @@ -14,67 +14,113 @@ .. test_metadata:: :id: test_metadata__metamodel_graph_checks + :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 diff --git a/src/extensions/score_metamodel/tests/rst/graph/test_safety_security_relation.rst b/src/extensions/score_metamodel/tests/rst/graph/test_safety_security_relation.rst new file mode 100644 index 000000000..b8dec58cb --- /dev/null +++ b/src/extensions/score_metamodel/tests/rst/graph/test_safety_security_relation.rst @@ -0,0 +1,109 @@ +.. + # ******************************************************************************* + # 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 + # ******************************************************************************* + +.. test_metadata:: + :id: test_metadata__safety_security_relation + :partially_verifies_list: tool_req__docs_safety_security_relation + :test_type: requirements_based + :derivation_technique: requirements_based + + Tests the generalized safety/security classification consistency check + across relations (source-owned and target-owned directions). + + +.. Setup: a QM feature used as belongs_to target for the component below. + +.. feat:: Belongs-to target feature + :id: feat__target_feat + :version: 1 + :security: YES + :safety: QM + :status: valid + + + +.. Negative Test (safety, source-owned): an ASIL feature includes a QM interface. + `includes` is source-owned, so the ASIL source + QM target mismatch is flagged. + +.. feat:: Feature with safety mismatch via includes + :id: feat__asil_includes_qm + :version: 1 + :security: YES + :safety: ASIL_B + :status: valid + :includes: logic_arc_int__graph__qm_iface + :expect: safety classification mismatch via `includes` + + + +.. Setup: the QM interface targeted above. + +.. logic_arc_int:: QM interface target + :id: logic_arc_int__graph__qm_iface + :version: 1 + :security: YES + :safety: QM + :status: valid + + + +.. Negative Test (security, source-owned): a YES component implements a NO interface. + `implements` is source-owned; the security mismatch is flagged. + Both ends are QM so no safety flag is expected, only the security one. + +.. comp:: Component with security mismatch via implements + :id: comp__yes_implements_no + :version: 1 + :security: YES + :safety: QM + :status: valid + :belongs_to: feat__target_feat + :implements: logic_arc_int__graph__no_iface + :expect: security classification mismatch via `implements` + + + +.. Setup: the NO interface targeted above. + +.. logic_arc_int:: NO interface target + :id: logic_arc_int__graph__no_iface + :version: 1 + :security: NO + :safety: QM + :status: valid + + + +.. Positive Test (matching): an ASIL feature includes an ASIL interface. + Matching classifications are not flagged. + +.. feat:: Feature with matching includes + :id: feat__asil_includes_asil + :version: 1 + :security: YES + :safety: ASIL_B + :status: valid + :includes: logic_arc_int__graph__asil_iface + :expect_not: mismatch + + + +.. Setup: the ASIL interface targeted above. + +.. logic_arc_int:: ASIL interface target + :id: logic_arc_int__graph__asil_iface + :version: 1 + :security: YES + :safety: ASIL_B + :status: valid diff --git a/src/extensions/score_metamodel/tests/test_graph_checks.py b/src/extensions/score_metamodel/tests/test_graph_checks.py index 064d6568f..6048a25e1 100644 --- a/src/extensions/score_metamodel/tests/test_graph_checks.py +++ b/src/extensions/score_metamodel/tests/test_graph_checks.py @@ -21,6 +21,7 @@ import score_metamodel.checks.graph_checks as graph_checks from score_metamodel.tests import fake_check_logger, need as test_need from sphinx_needs.config import NeedType +from sphinx_needs.need_item import NeedItem, NeedLink class DummyNeedsView: @@ -180,3 +181,165 @@ def test_filter_needs_by_criteria_unknown_type_logs_warning() -> None: log.assert_warning( "Unknown need type `unknown` in graph check.", expect_location=False ) + + +# --------------------------------------------------------------------------- +# check_safety_security_relation +# +# These tests are written FIRST (TDD). The function does not exist yet, so +# each test fails at call-time with AttributeError — that is the expected +# failure. Once implemented, they should pass. +# --------------------------------------------------------------------------- + + +class NeedItemView: + """Minimal NeedsView-like test double backed by real NeedItem objects.""" + + def __init__(self, needs: list[NeedItem]) -> None: + self._needs = needs + + def values(self) -> list[NeedItem]: + return self._needs + + def filter_is_external(self, is_external: bool) -> NeedItemView: + return NeedItemView( + [n for n in self._needs if n.get("is_external", False) == is_external] + ) + + +def _link(need_item: NeedItem, **links: list[str]) -> NeedItem: + """Set ``_links`` on a need built by ``test_need`` (the helper's ``links`` + kwarg is re-wrapped and unusable, so assign the real structure directly).""" + need_item._links = { # type: ignore[attr-defined] + relation: [NeedLink(id=target_id) for target_id in target_ids] + for relation, target_ids in links.items() + } + return need_item + + +def _run_safety_security_relation(needs: list[NeedItem]): + """Run the new check against a list of NeedItems and return the logger.""" + log = fake_check_logger() + graph_checks.check_safety_security_relation( # type: ignore[attr-defined] + app=None, # type: ignore[arg-type] + all_needs=NeedItemView(needs), + log=log, + ) + return log + + +def test_safety_security_asil_source_to_qm_target_via_includes_flags() -> None: + """ASIL source including a QM target is flagged (source-owned relation).""" + target = test_need(id="tgt", type="comp", status="valid", safety="QM") + source = _link( + test_need( + id="src", + type="feat", + status="valid", + safety="ASIL_B", + ), + includes=["tgt"], + ) + log = _run_safety_security_relation([source, target]) + log.assert_warning("safety classification mismatch via `includes`") + + +def test_safety_security_qm_source_to_asil_target_via_derived_from_flags() -> None: + """QM source derived-from an ASIL target is flagged (target-owned, reverse).""" + target = test_need(id="parent", type="stkh_req", status="valid", safety="ASIL_B") + source = _link( + test_need( + id="child", + type="feat_req", + status="valid", + safety="QM", + ), + derived_from=["parent"], + ) + log = _run_safety_security_relation([source, target]) + log.assert_warning("safety classification mismatch via `derived_from`") + + +def test_safety_security_matching_classifications_no_flag() -> None: + """Matching safety classifications across a relation are not flagged.""" + target = test_need(id="parent", type="stkh_req", status="valid", safety="ASIL_B") + source = _link( + test_need( + id="child", + type="feat_req", + status="valid", + safety="ASIL_B", + ), + derived_from=["parent"], + ) + log = _run_safety_security_relation([source, target]) + log.assert_no_warnings() + + +def test_safety_security_invalid_source_ignored() -> None: + """An invalid source is not checked even if it links a mismatched target.""" + target = test_need(id="parent", type="stkh_req", status="valid", safety="ASIL_B") + source = _link( + test_need( + id="child", + type="feat_req", + status="invalid", + safety="QM", + ), + derived_from=["parent"], + ) + log = _run_safety_security_relation([source, target]) + log.assert_no_warnings() + + +def test_safety_security_target_lacking_safety_no_flag() -> None: + """A target without a safety attribute is not flagged (no false positive).""" + target = test_need(id="tc", type="testcase", status="valid") + source = _link( + test_need( + id="src", + type="feat", + status="valid", + safety="ASIL_B", + ), + includes=["tc"], + ) + log = _run_safety_security_relation([source, target]) + log.assert_no_warnings() + + +def test_safety_security_security_mismatch_flags() -> None: + """A security YES/NO mismatch across a relation is flagged.""" + target = test_need(id="iface", type="logic_arc_int", status="valid", security="NO") + source = _link( + test_need( + id="comp", + type="comp", + status="valid", + security="YES", + ), + implements=["iface"], + ) + log = _run_safety_security_relation([source, target]) + log.assert_warning("security classification mismatch via `implements`") + + +def test_safety_security_not_checked_relation_no_flag() -> None: + """A mismatch via a "Not checked" relation (e.g. fully_verifies) is NOT flagged. + + The requirement carves out a "Not checked" group (traceability/evidence + links). A naive check-all implementation would flag this; the correct + implementation must skip it. + """ + target = test_need(id="verified", type="feat_req", status="valid", safety="QM") + source = _link( + test_need( + id="tm", + type="test_metadata", + status="valid", + safety="ASIL_B", + ), + fully_verifies=["verified"], + ) + log = _run_safety_security_relation([source, target]) + log.assert_no_warnings() From 64d6f275480b82a7fc82750e2992087a8ce5a5cc Mon Sep 17 00:00:00 2001 From: Andreas Zwinkau Date: Thu, 13 Aug 2026 15:37:53 +0200 Subject: [PATCH 3/3] fix: remove obsolete graph checks --- src/extensions/score_metamodel/metamodel.yaml | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/src/extensions/score_metamodel/metamodel.yaml b/src/extensions/score_metamodel/metamodel.yaml index 19dbce0f6..1c31c4440 100644 --- a/src/extensions/score_metamodel/metamodel.yaml +++ b/src/extensions/score_metamodel/metamodel.yaml @@ -1133,48 +1133,6 @@ needs_extra_links: ############################################################## graph_checks: - # req-Id: tool_req__docs_safety_security_relation - 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_safety_security_relation - 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_safety_security_relation - 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_safety_security_relation - 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: