diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..5d21942 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,59 @@ +# clang-tidy configuration for the `clang-tidy` CI job (ci.yml). +# +# Why this file exists (issue #80 / cpp-RCP-12): before this file was added, +# ci.yml ran `clang-tidy -p build --warnings-as-errors='*'` with no +# `-checks` argument and no `.clang-tidy` file present. With nothing +# enabling any checks, clang-tidy exits immediately with the fatal usage +# error "Error: no checks enabled" -- and the trailing `|| true` on that +# step was swallowing that failure, meaning the job has never actually +# linted a single file in this repo's history, let alone gated on it. +# +# The check list below is deliberately narrow. It was hand-picked and +# verified (locally, via Homebrew LLVM clang-tidy against this tree) to +# have zero findings against the current include/ + tests/ sources, while +# still being a real, non-trivial "definite bug pattern" gate: use-after-move, +# infinite loops, dangling handles, sizeof/memset/string-compare misuse, +# swapped arguments, virtual near-misses, etc. +# +# Broader categories -- full bugprone-*, cert-*, performance-*, +# portability-*, modernize-*, readability-*, clang-analyzer-*, and even +# clang-diagnostic-* (this project already has pre-existing -Wall/-Wextra +# findings, e.g. -Wmissing-field-initializers in tests/test_legacy_mock.cpp, +# that don't fail the build today because CMakeLists.txt does not pass +# -Werror) -- all currently have real, pre-existing findings across this +# tree. Turning any of those on wholesale would fail this CI job against a +# backlog of findings that are out of scope for a CI/process-only change +# (see the PR that introduced this file for the full accounting). Growing +# this list is left as deliberate follow-up work for whoever owns the C++ +# source, once each finding is triaged and fixed or dispositioned. +Checks: > + -*, + bugprone-copy-constructor-init, + bugprone-dangling-handle, + bugprone-forwarding-reference-overload, + bugprone-inaccurate-erase, + bugprone-infinite-loop, + bugprone-integer-division, + bugprone-lambda-function-name, + bugprone-move-forwarding-reference, + bugprone-multiple-statement-macro, + bugprone-not-null-terminated-result, + bugprone-signed-char-misuse, + bugprone-sizeof-expression, + bugprone-string-integer-assignment, + bugprone-suspicious-memset-usage, + bugprone-suspicious-string-compare, + bugprone-swapped-arguments, + bugprone-terminating-continue, + bugprone-undefined-memory-manipulation, + bugprone-unused-raii, + bugprone-use-after-move, + bugprone-virtual-near-miss + +WarningsAsErrors: '*' + +# Restrict findings surfaced from #included headers to this project's own +# headers, so third-party FetchContent dependencies (e.g. Catch2, vendored +# under build/_deps/catch2-src) don't get flagged for patterns in their own +# code that this project has no control over. +HeaderFilterRegex: 'include/rcp/' diff --git a/.fusa-hara.json b/.fusa-hara.json index 3ae7560..af7107f 100644 --- a/.fusa-hara.json +++ b/.fusa-hara.json @@ -46,7 +46,7 @@ "severity": "S3", "exposure": "E4", "controllability": "C2", - "asil": "ASIL-B" + "asil": "ASIL-C" }, "safetyGoals": [ "SG-001" @@ -231,7 +231,7 @@ { "id": "SG-001", "description": "Requests to safety-critical endpoints shall be delivered within the configured watchdog period or a fault shall be signalled.", - "asil": "ASIL-B" + "asil": "ASIL-C" }, { "id": "SG-002", @@ -241,7 +241,7 @@ { "id": "SG-003", "description": "A per-stream watchdog kick shall be recorded for every accepted inbound request, regardless of request kind or safety tag.", - "asil": "ASIL-B" + "asil": "ASIL-C" }, { "id": "SG-004", @@ -261,7 +261,7 @@ { "id": "SG-007", "description": "An RC Server that stops responding shall be detected as unreachable within the configured liveness deadline.", - "asil": "ASIL-B" + "asil": "ASIL-C" }, { "id": "SG-008", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c52e622..b41b670 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: - name: Set up MSVC environment (Windows) if: runner.os == 'Windows' - uses: ilammy/msvc-dev-cmd@v1 + uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1 - name: Configure shell: bash @@ -93,10 +93,17 @@ jobs: - name: Checkout cpp-RCP uses: actions/checkout@v4 + # Pinned to the latest tagged RELAY release rather than floating on the + # default branch (issue #83 / cpp-RCP-15): an unpinned checkout means + # this job's pass/fail can change with no cpp-RCP-side change at all + # whenever RELAY's main branch moves, which is not reproducible CI. + # Bump this SHA/tag deliberately (in its own commit) when cpp-RCP is + # ready to validate against a newer RELAY conformance ruleset. - name: Checkout RELAY (authoritative conformance validator) uses: actions/checkout@v4 with: repository: SoundMatt/RELAY + ref: 3344b3d1a6a4d870d26c81c6f3c3ff5ca2930e16 # v2.0.2 path: RELAY - name: Set up Go @@ -149,9 +156,18 @@ jobs: run: cmake --build build --parallel - name: Run clang-tidy + # Only pass tests/*.cpp as translation units: this project's + # include/rcp/*.hpp headers are header-only (no standalone compile + # command in compile_commands.json for a .hpp on its own), so + # invoking clang-tidy directly on a header fails with a + # clang-diagnostic-error "file not found" on its own #includes + # (no -I flags to resolve them) rather than a real lint finding. + # .clang-tidy's HeaderFilterRegex ('include/rcp/') still surfaces + # findings from those headers as they're transitively included by + # the .cpp translation units below. run: | - find include tests -name '*.hpp' -o -name '*.cpp' | \ - xargs clang-tidy -p build --warnings-as-errors='*' || true + find tests -name '*.cpp' | \ + xargs clang-tidy -p build --warnings-as-errors='*' # ── LCOV coverage ───────────────────────────────────────────────────────────── coverage: @@ -187,12 +203,28 @@ jobs: - name: Collect LCOV data run: | + # --rc lcov_branch_coverage=1 must be passed to BOTH the --capture + # and --remove invocations below (lcov re-reads/rewrites the .info + # file's branch records on every invocation, so omitting it on + # either step silently drops branch data even if the other step + # collected it). Without it, totalBranches/hitBranches downstream + # in coverage-report.json are always 0/0 -- a vacuous "nothing + # failed" rather than a genuine 100%-branch-coverage result against + # this project's declared DAL-B target (see issues #67/#68). lcov --capture \ --directory build-cov \ --gcov-tool gcov-12 \ + --rc lcov_branch_coverage=1 \ --output-file coverage.info + # /usr/include/* excludes STL/toolchain system headers, which + # otherwise dominate the file-entry count (confirmed: 101/153 file + # entries and ~5979/9034 lines were system headers, dragging + # blended line coverage down to ~68.8% vs. ~91.5% for actual + # project files alone -- see issue #67/#68). lcov --remove coverage.info \ '*/tests/*' '*/catch2/*' '*/FetchContent/*' '*/cmake-build*/*' \ + '/usr/include/*' \ + --rc lcov_branch_coverage=1 \ --output-file coverage.info - name: Upload coverage report @@ -354,7 +386,11 @@ jobs: - name: Traceability check (all requirements traced) run: /tmp/cpfusa/build/cpfusa trace - - name: Traceability gaps report + # Informational only, not a gate: this dumps the gap detail for human + # review. The actual gate for this job is the "Enforce 100% requirement + # coverage" step below, which runs without `|| true` and fails the job + # for real. + - name: Traceability gaps report (informational, non-gating) run: /tmp/cpfusa/build/cpfusa trace --gaps || true # RELAY §20.1: every requirement MUST be traced AND tested (100%). @@ -499,16 +535,46 @@ jobs: name: coverage-info path: . - - name: LCOV structural coverage report (DO-178C) + # Artifact generation only, not a gate: this project's stub bridge + # implementations cannot reach the 100%-branch DAL-B threshold this + # command grades against, so `cpfusa coverage` exits non-zero by + # design today (same reasoning as release.yml's gap-report steps). + # `|| true` is intentional here so the job can still produce and + # upload coverage-report.json for inspection; it does not mean "DAL-B + # coverage is enforced". The step immediately below is what actually + # gates on this job's data (issues #67/#68). + - name: LCOV structural coverage report (DO-178C, artifact only — non-gating) run: /tmp/cpfusa/build/cpfusa coverage --profile coverage.info --dal DAL-B --output coverage-report.json || true - - name: Full compliance report (JSON) + # Real gate (issues #67/#68): a coverage report with totalBranches==0 + # is not "100% branch coverage met", it's "no branch data was + # collected at all" -- a 0/0 ratio that would otherwise vacuously + # satisfy any branch-coverage threshold check. Fail hard if that + # happens, independent of whether the DAL-B step above is gating. + - name: Assert branch coverage data is non-vacuous + run: | + total_branches=$(jq '.summary.totalBranches' coverage-report.json) + echo "coverage-report.json summary.totalBranches=${total_branches}" + if [ "$total_branches" -le 0 ]; then + echo "::error::coverage-report.json reports totalBranches=${total_branches} — no branch coverage data was collected, so any branch-coverage threshold is being met vacuously (0/0), not genuinely. See issues #67/#68." + exit 1 + fi + + # Artifact generation only, not a gate: these convert the aggregated + # findings (already gated individually by the cpfusa-check / -lint / + # -cyber / -trace / -verify jobs this job `needs:`) into downloadable + # JSON/SARIF report formats. `|| true` reflects that intent, not a + # bug — there is nothing pass/fail-shaped about a format conversion. + - name: Full compliance report (JSON, artifact only — non-gating) run: /tmp/cpfusa/build/cpfusa report --format json --output report.json || true - - name: Full compliance report (SARIF) + - name: Full compliance report (SARIF, artifact only — non-gating) run: /tmp/cpfusa/build/cpfusa report --format sarif --output results.sarif || true - - name: Safety metrics snapshot + # Artifact generation only, not a gate: records a point-in-time safety + # metrics snapshot for trend tracking. There is no pass/fail threshold + # associated with recording a snapshot. + - name: Safety metrics snapshot (artifact only — non-gating) run: /tmp/cpfusa/build/cpfusa metrics record || true - name: Upload compliance report diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df4c014..1145be8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,7 @@ on: permissions: contents: write + pull-requests: write jobs: regenerate-safety-artifacts: @@ -45,15 +46,25 @@ jobs: - name: Build project run: cmake --build build-cov --parallel + # Kept in sync with ci.yml's `coverage` job (issues #67/#68): branch + # coverage collection and /usr/include system-header exclusion were + # both missing here too, since this step duplicates the same lcov + # invocation. Without --rc lcov_branch_coverage=1 on both the + # --capture and --remove calls, coverage-report.json's + # totalBranches/hitBranches are always 0/0, vacuously "passing" any + # branch-coverage threshold rather than genuinely meeting it. - name: Run tests and collect coverage run: | ctest --test-dir build-cov --output-on-failure -j1 lcov --capture \ --directory build-cov \ --gcov-tool gcov-12 \ + --rc lcov_branch_coverage=1 \ --output-file coverage.info lcov --remove coverage.info \ '*/tests/*' '*/catch2/*' '*/FetchContent/*' \ + '/usr/include/*' \ + --rc lcov_branch_coverage=1 \ --output-file coverage.info - name: Regenerate dFMEA (with cybersecurity analysis) @@ -62,8 +73,23 @@ jobs: - name: Regenerate safety case (GSN) run: /tmp/cpfusa/build/cpfusa safety-case - - name: Regenerate TARA (ISO 21434 Ch. 9) - run: /tmp/cpfusa/build/cpfusa tara + # issues #78/#79 (cpp-RCP-10/11): `cpfusa tara` analyzes cpp-FuSa's OWN + # tooling/CI/supply-chain surface (release binary, .fusa.json, CMake + # deps) — it is not a protocol-level TARA for RCP itself. The real + # product-level TARA is the hand-authored TARA-ANALYSIS.md. Having + # both an ISO-21434-labeled `tara.json`/`tara.md` and TARA-ANALYSIS.md + # at the repo root with no distinguishing signal made it ambiguous + # which a certification reviewer should trust, so `cpfusa tara`'s + # output is relocated under tooling/ immediately after generation + # (cpfusa itself has no --output flag for this command, so the file is + # moved rather than generated in place) to make its tooling-only scope + # self-evident and keep that distinction stable across every future + # regeneration on a tag, not just this one. + - name: Regenerate TARA (ISO 21434 Ch. 9, cpp-FuSa tooling-process TARA — not the product TARA) + run: | + /tmp/cpfusa/build/cpfusa tara + mv tara.json tooling/tara.json + mv tara.md tooling/tara.md - name: Regenerate SBOM, provenance, and artifact manifest run: /tmp/cpfusa/build/cpfusa release @@ -74,25 +100,29 @@ jobs: # design — but the workflow's job is to GENERATE the report artifact, not # to gate on it (gating lives in ci.yml). `|| true` keeps the artifact # while letting the report record the gaps. - - name: Regenerate ISO 26262 gap report (ASIL-B) + # + # (issue #81 / cpp-RCP-13: the step names below spell out + # "artifact only — non-gating" explicitly, so this intent doesn't rely + # solely on the comment above surviving future edits.) + - name: Regenerate ISO 26262 gap report (ASIL-B, artifact only — non-gating) run: | /tmp/cpfusa/build/cpfusa iso26262 \ --asil ASIL-B \ --output iso26262-gap-report.json || true - - name: Regenerate IEC 61508 gap report (SIL-2) + - name: Regenerate IEC 61508 gap report (SIL-2, artifact only — non-gating) run: | /tmp/cpfusa/build/cpfusa iec61508 \ --sil SIL-2 \ --output iec61508-gap-report.json || true - - name: Regenerate DO-178C gap report (DAL-B) + - name: Regenerate DO-178C gap report (DAL-B, artifact only — non-gating) run: | /tmp/cpfusa/build/cpfusa do178 \ --dal DAL-B \ --output do178-gap-report.json || true - - name: Regenerate structural coverage report (DO-178C) + - name: Regenerate structural coverage report (DO-178C, artifact only — non-gating) run: | /tmp/cpfusa/build/cpfusa coverage \ --profile coverage.info \ @@ -111,7 +141,9 @@ jobs: - name: Generate safety badge (Shields.io SVG) run: /tmp/cpfusa/build/cpfusa badge - - name: Generate full compliance report + # Artifact only — non-gating (issue #81): format conversion of already + # -generated findings, nothing pass/fail-shaped about it. + - name: Generate full compliance report (JSON + HTML, artifact only — non-gating) run: | /tmp/cpfusa/build/cpfusa report --format json --output report.json || true /tmp/cpfusa/build/cpfusa report --format html --output report.html || true @@ -124,13 +156,25 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - - name: Commit regenerated artifacts + # issue #82 / cpp-RCP-14: this used to `git push origin HEAD:refs/heads/main` + # directly as github-actions[bot] with `[skip ci]`, landing regenerated + # evidence on main with no review and no CI run. Several of this + # repo's own docs (AUDIT_PACK.md, sas.md, safety-case.md, tara.md) point + # at these files by their root-level path and ROADMAP.md's milestone-61 + # entry explicitly relies on release.yml's regeneration commit landing + # on main on every tag (see e.g. commit e7a8178, the v2.17.0 + # regeneration) — so these files are relied on being live in the tree + # at main HEAD, ruling out artifact-upload-only as a fix. Instead, the + # regenerated evidence now lands on a dedicated branch and goes through + # a normal reviewed PR (opened in the next step) rather than a direct + # unreviewed bot push, while still ending up on main once merged. + - name: Commit regenerated artifacts to a review branch run: | TAG="${{ github.ref_name }}" git add \ fmea.csv fmea.json \ safety-case.json safety-case.md safety-case.mermaid \ - tara.json tara.md \ + tooling/tara.json tooling/tara.md \ sbom.json provenance.json artifact-manifest.json \ iso26262-gap-report.json \ iec61508-gap-report.json \ @@ -145,11 +189,43 @@ jobs: || true if git diff --cached --quiet; then echo "No artifact changes for $TAG — nothing to commit." + echo "ARTIFACTS_CHANGED=false" >> "$GITHUB_ENV" else + BRANCH="safety-artifacts/${TAG}" + git checkout -B "$BRANCH" git commit \ - -m "chore: regenerate safety artifacts for $TAG [skip ci]" \ + -m "chore: regenerate safety artifacts for $TAG" \ -m "Signed-off-by: github-actions[bot] " - git push origin HEAD:refs/heads/main + git push --force origin "$BRANCH" + echo "ARTIFACTS_CHANGED=true" >> "$GITHUB_ENV" + echo "ARTIFACTS_BRANCH=$BRANCH" >> "$GITHUB_ENV" + fi + + - name: Open review PR for regenerated artifacts + if: env.ARTIFACTS_CHANGED == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + TAG="${{ github.ref_name }}" + BRANCH="${{ env.ARTIFACTS_BRANCH }}" + if gh pr view "$BRANCH" --json url >/dev/null 2>&1; then + echo "PR for $BRANCH already exists, skipping create." + else + { + echo "Automated regeneration of cpp-FuSa safety/compliance evidence artifacts" + echo "(FMEA, safety case, TARA, SBOM, gap reports, coverage report, SAS/SCI," + echo "audit pack, badge, compliance report, qualification report) for release" + echo "${TAG}." + echo "" + echo "This PR is opened by \`release.yml\`'s \`regenerate-safety-artifacts\` job" + echo "instead of pushing directly to \`main\`, so a human reviews the diff (and" + echo "CI runs against it) before these files land -- see issue #82 / cpp-RCP-14." + } > "$RUNNER_TEMP/pr-body.md" + gh pr create \ + --base main \ + --head "$BRANCH" \ + --title "chore: regenerate safety artifacts for $TAG" \ + --body-file "$RUNNER_TEMP/pr-body.md" fi - name: Upload release artifacts @@ -159,7 +235,7 @@ jobs: path: | fmea.json safety-case.json - tara.json + tooling/tara.json sbom.json provenance.json artifact-manifest.json diff --git a/HARA.md b/HARA.md index c617235..7c9a9b6 100644 --- a/HARA.md +++ b/HARA.md @@ -2,7 +2,7 @@ **Standard:** ISO 26262:2018 Part 3 **System:** cpp-RCP — C++ implementation of the OPEN Alliance TC18 Remote Control Protocol -**Target ASIL:** ASIL-B (derived) +**Target ASIL:** ASIL-C (derived) **Source:** `.fusa-hara.json` (machine-readable authoritative source) This revision (ROADMAP.md milestone 62, "Certification Refresh", v2.18.0) @@ -32,7 +32,7 @@ Phase 13-16 work are appended as H-011/SG-011. | ID | Hazard | Severity | Exposure | Controllability | ASIL | Safety Goals | |----|--------|----------|----------|-----------------|------|--------------| -| H-001 | Loss of request delivery to a safety-critical endpoint (e.g. a braking-adjacent actuator) | S3 | E4 | C2 | ASIL-B | SG-001 | +| H-001 | Loss of request delivery to a safety-critical endpoint (e.g. a braking-adjacent actuator) | S3 | E4 | C2 | ASIL-C | SG-001 | | H-002 | Request misaddressed to the wrong endpoint via a stale or corrupted stream_id/byte_bus_id pairing | S2 | E3 | C2 | ASIL-B | SG-002 | | H-003 | Per-stream watchdog not kicked, leading to an unintended safe-state entry or endpoint reset | S2 | E4 | C2 | ASIL-B | SG-003 | | H-004 | Replay or out-of-order re-delivery of a stale request from a previous session | S2 | E3 | C2 | ASIL-B | SG-004 | @@ -50,13 +50,13 @@ Phase 13-16 work are appended as H-011/SG-011. | ID | Safety Goal | ASIL | Addressed By | |----|-------------|------|--------------| -| SG-001 | Requests to safety-critical endpoints shall be delivered within the configured watchdog period or a fault shall be signalled. | ASIL-B | `watchdog::Manager`/`StreamWatchdog`, `deadline::Monitor` | +| SG-001 | Requests to safety-critical endpoints shall be delivered within the configured watchdog period or a fault shall be signalled. | ASIL-C | `watchdog::Manager`/`StreamWatchdog`, `deadline::Monitor` | | SG-002 | Requests shall only be dispatched to the endpoint they are addressed to (stream_id + byte_bus_id); misaddressed requests shall be rejected. | ASIL-B | `acf::AcfMessageInfo` byte_bus_id decode, RC Server dispatch (e.g. `mock::Server::dispatch`) | -| SG-003 | A per-stream watchdog kick shall be recorded for every accepted inbound request, regardless of request kind or safety tag. | ASIL-B | `watchdog::StreamWatchdog::kick_from_request`, `e2e::RxWatchdog` | +| SG-003 | A per-stream watchdog kick shall be recorded for every accepted inbound request, regardless of request kind or safety tag. | ASIL-C | `watchdog::StreamWatchdog::kick_from_request`, `e2e::RxWatchdog` | | SG-004 | A request stream configured with rx_enforce_seq shall reject any sequence number that is not strictly greater than the last accepted one. | ASIL-B | `e2e::RxSequenceGuard::check` | | SG-005 | Cancellation and triggered requests shall never be delayed by a standard or compound request queued earlier on the same stream. | ASIL-B | `request::SequencerTable`/`RequestLedger` execution-priority ordering | | SG-006 | Transport authentication (mTLS on the UDP/IP variant, or link-layer authentication on native Ethernet) and per-endpoint access policy shall be enforced on every external RC Server connection. | ASIL-B | `tls::SecureClient`/`SecureServer`, `authz::AccessPolicy`, `discovery::DiscoveryClaim` | -| SG-007 | An RC Server that stops responding shall be detected as unreachable within the configured liveness deadline. | ASIL-B | `deadline::Monitor`/`LivenessTracker` | +| SG-007 | An RC Server that stops responding shall be detected as unreachable within the configured liveness deadline. | ASIL-C | `deadline::Monitor`/`LivenessTracker` | | SG-008 | An RC Server shall only be treated as operational after its lifecycle state machine reports RCP_CONFIGURED following a successful wake sequence. | ASIL-B | `lifecycle::ServerLifecycle`, `powerstate::PowerManager` | | SG-009 | Fault injection rules shall not persist beyond the lifetime of the injecting process. | ASIL-A | `faultinject::Controller` in-process state only | | SG-010 | A stream's watchdog/safe-state status shall be deterministically derivable from its own kick/overflow/latch history alone. | ASIL-B | `e2e::RxWatchdog`, `watchdog::StreamWatchdog` deterministic state | @@ -66,11 +66,11 @@ Phase 13-16 work are appended as H-011/SG-011. ## ASIL Decomposition Rationale -### H-001 (ASIL-B): Loss of request delivery +### H-001 (ASIL-C): Loss of request delivery The RC Client sends requests to RC Servers over an automotive Ethernet network. Network faults, ECU resets, or HPC process crashes can prevent -request delivery. The required ASIL is B because: +request delivery. The required ASIL is C because: - **S3**: loss of actuation on a braking-adjacent endpoint during emergency deceleration could be life-threatening. @@ -78,14 +78,22 @@ request delivery. The required ASIL is B because: - **C2**: the driver may not be able to react in time if actuation is silently lost. -**Decomposition**: ASIL-B is achieved via: -1. `watchdog::Manager`/`StreamWatchdog` (ASIL-B): detects a stream whose +Per ISO 26262-3's severity/exposure/controllability-to-ASIL determination +table, S3/E4/C2 maps to ASIL-C, not ASIL-B. This row previously stated +ASIL-B without claiming any decomposition to justify the reduction; it is +corrected here to ASIL-C, with the correction propagated to SG-001, +SG-003, and SG-007 (the request-delivery/watchdog/liveness chain that +implements this hazard's mitigation, per the decomposition below) and to +this document's stated target ASIL. + +**Decomposition**: ASIL-C is achieved via: +1. `watchdog::Manager`/`StreamWatchdog` (ASIL-C): detects a stream whose `rx_wd_timeout_interval` has elapsed with no accepted request. -2. `deadline::Monitor` (ASIL-B): detects an RC Server whose liveness +2. `deadline::Monitor` (ASIL-C): detects an RC Server whose liveness signal (response/ack-queue heartbeat or EP0 lifecycle-state-changed trigger) has gone silent past its configured deadline. -3. No decomposition into lower ASIL-A + ASIL-A elements is claimed; the - above mechanisms together satisfy ASIL-B. +3. No decomposition into lower ASIL elements is claimed; the above + mechanisms together satisfy ASIL-C directly. ### H-002 (ASIL-B): Endpoint misaddressing diff --git a/tara.json b/tooling/tara.json similarity index 96% rename from tara.json rename to tooling/tara.json index 573217f..7f8ed45 100644 --- a/tara.json +++ b/tooling/tara.json @@ -25,8 +25,6 @@ "safety": "major" }, "mitigations": [ - "sign — HMAC-SHA256 artifact signing", - "SLSA provenance verification (slsa command)", "Release workflow requires branch-protected, reviewed PRs before tagging" ], "risk": "medium", @@ -66,7 +64,6 @@ }, "mitigations": [ "audit-pack — hashed manifest over every evidence artifact", - "sign --verify re-checks HMAC signatures before submission", "sci — Software Configuration Index records a per-file sha256 at release time" ], "risk": "medium", @@ -74,7 +71,7 @@ "treatment": "mitigate" }, { - "asset": "Third-party CMake dependencies (FetchContent: CLI11, nlohmann/json, Catch2)", + "asset": "Third-party CMake dependencies (FetchContent: Catch2)", "attackFeasibility": "very-low", "attackVector": "Supply-chain compromise of an upstream GitHub repository or its release tags fetched by FetchDeps.cmake.", "id": "TARA-004", diff --git a/tara.md b/tooling/tara.md similarity index 90% rename from tara.md rename to tooling/tara.md index d9bb0a3..445aa2b 100644 --- a/tara.md +++ b/tooling/tara.md @@ -9,7 +9,7 @@ Coverage: 8/8 assets (100.0%) | TARA-001 | cpp-RCP release binary | Attacker replaces a signed release binary with a trojaned build that suppresses safety findings before it reaches a downstream project's CI. | low | medium | mitigate | | TARA-002 | .fusa.json / .fusa-hara.json project config | Attacker (or careless commit) weakens the configured ASIL or replaces genuine hazard analysis with a stale/placeholder template, silently lowering the safety bar a downstream project believes it is held to. | medium | high | mitigate | | TARA-003 | Generated evidence artifacts (fmea.json, tara.json, safety-case.json, check-report.json) | Insider or compromised CI step edits a generated evidence artifact after generation to hide a known defect from a certification reviewer. | low | medium | mitigate | -| TARA-004 | Third-party CMake dependencies (FetchContent: CLI11, nlohmann/json, Catch2) | A compromised upstream dependency introduces malicious code that runs inside cpfusa's own analysis process, potentially tampering with the findings it produces. | very-low | medium | mitigate | +| TARA-004 | Third-party CMake dependencies (FetchContent: Catch2) | A compromised upstream dependency introduces malicious code that runs inside cpfusa's own analysis process, potentially tampering with the findings it produces. | very-low | medium | mitigate | | TARA-005 | qualify-report.json tool-qualification evidence | Attacker modifies qualify-report.json to hide a failing qualification case, making an unqualified tool appear qualified for its intended safety use (ISO 26262-8 Clause 11). | low | medium | mitigate | | TARA-006 | CI pipeline (ci.yml / release.yml) | Attacker disables or bypasses the `check` gate step in CI, allowing a change with open ERROR findings to merge and release. | medium | high | mitigate | | TARA-007 | Source requirement annotations (//fusa:req, //fusa:test) | Developer removes or mistypes an annotation to hide a requirement-traceability gap from `trace`'s coverage gate. | medium | medium | mitigate | @@ -18,8 +18,6 @@ Coverage: 8/8 assets (100.0%) ## Impact (SFOP) & Mitigations - **TARA-001** — safety=major financial=moderate operational=moderate privacy=negligible - - sign — HMAC-SHA256 artifact signing - - SLSA provenance verification (slsa command) - Release workflow requires branch-protected, reviewed PRs before tagging - **TARA-002** — safety=major financial=moderate operational=negligible privacy=negligible - FUSA-STUB001 deny-list scan flags untouched hazard/goal templates (§1.6.1) @@ -27,7 +25,6 @@ Coverage: 8/8 assets (100.0%) - hara --format json's completeness block surfaces missing ASIL/fssrRefs - **TARA-003** — safety=major financial=moderate operational=moderate privacy=negligible - audit-pack — hashed manifest over every evidence artifact - - sign --verify re-checks HMAC signatures before submission - sci — Software Configuration Index records a per-file sha256 at release time - **TARA-004** — safety=major financial=moderate operational=moderate privacy=negligible - vuln — scans CMake dependency manifests for known vulnerabilities