fix: 2026-07-28 deep-audit bug-fix sprint (issues #82-91) - #92
Merged
Conversation
…is c-FuSa x-FuSa spec §2.4.1 (MUST): the `standard` field is a canonical lowercase id used identically everywhere in the JSON, never a display string — a clause reference belongs in the separate `clause` field. `check`/`lint`/ `analyze`/`cyber` findings were instead emitting combined display strings like "MISRA-C:2012 R15.1" or "ISO 21434 / CERT-C STR31-C" straight from the rule registry, with `clause` always empty. `cfusa_rule_t` now carries `standard_id` (canonical id) and `clause` (rule/clause reference) as two separate fields instead of one combined `standard` string, and every rule table (lint/analyze/cyber/safety) is updated to populate them. `Finding.standard`/`Finding.clause` in the JSON report, and the SARIF rule declarations' help text, are derived from the new fields. Also fixes §2.9's SARIF mapping MUST: `tool.driver.name` was emitting the binary name "cfusa" instead of the §1.1 tool name "c-FuSa" — every other JSON output already used "c-FuSa" in the common header; SARIF was the one holdout, which made GitHub Code Scanning attribute findings to "cfusa" instead of "c-FuSa". Closes #84, closes #91. Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
…suffix
`cfusa check`/`cfusa lint`'s CFUSA-L004 rule (MISRA-C 2012 Rule 17.2, no
recursion) flagged a local function as "recursive" whenever an unrelated
function it called merely had a name that *ends with* the local
function's name — e.g. a static `evaluate()` calling an external
`rcp_e2e_wd_evaluate()` or `helper_evaluate()`, neither of which is a
self-call or forms a call cycle.
The self-call scan looked for the literal substring "fn_name(" in each
line of the function body with no identifier-boundary check on the left
side of the match, so any callee name merely ending with the caller's
name matched too. Add `l004_self_call()`, a small word-boundary-aware
scanner: it still skips string/character literals, but additionally
requires the character immediately before a candidate match to not be an
identifier character (or be the start of line).
Real recursion (a function actually calling itself) is unaffected.
Closes #82.
Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
x-FuSa spec §1.2.5 defines `.fusa-hara.json` as an INPUT file authored by
a project (like `.fusa-reqs.json`), with a plain schema of
`{project, standard, createdAt, operationalSituations, hazards,
safetyGoals, attestation?}` — no `schemaVersion`/`kind`/`tool`/
`toolVersion`/`language` fields, since those §3.1 common-header fields
belong to *report documents*, not this input file. §3.1's `kind` enum is
also closed and does not contain a bare `"hara"` value (the corresponding
report kind is `"hara-report"`, used by `hara --format json`'s separate
output path).
`hara init`'s scaffold wrote all five report-envelope fields (including
`"kind": "hara"`, not even a member of the closed enum) straight into the
input file it creates, unlike c-FuSa's own hand-authored `.fusa-hara.json`
at the repo root, which correctly has none of them. Drop the envelope
fields from the scaffold so a freshly-initialised file matches the §1.2.5
schema exactly.
Closes #90.
Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
…to sas.md x-FuSa spec §9.3: `sas --format json` writes `sas.json`, and a tool MUST also write the human-readable `sas.md` companion — `sas.json` is not a replacement for it. `--output`'s default was hardcoded to `"sas.md"` regardless of `--format`, so `cfusa sas --format json --dir .` (no `--output`) wrote the JSON body into a file literally named `sas.md`. Worse, the later "did we already write sas.md?" companion-generation guard compared only the output path, saw `out_path == "sas.md"`, and concluded the real Markdown companion had already been produced — so neither of the two files §9.3 requires ever actually existed; there was one file, named sas.md, containing JSON. Make the default `--output` path depend on `--format` when not given explicitly: "sas.json" for `--format json`, "sas.md" for md/text/default. The existing companion-generation logic then does the right thing unmodified — it writes JSON to sas.json and the real Markdown companion to sas.md. Closes #89. Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
… than cwd x-FuSa spec §2.2: `--dir <path>` (project root, default cwd) applies to "all" commands, and §6's own documented `qualify` CLI surface includes it explicitly. `cmd_qualify`'s `getopt_long` table had no `"dir"`/`-d` entry at all, so `cfusa qualify --dir <anything>` failed unconditionally with exit `2` (usage error) — the flag simply didn't exist, regardless of position. Since FuSaOps orchestrates every x-FuSa tool by invoking it with `--dir <project-root>` against whatever project it is analysing (almost never its own cwd), and `qualify` is one of the §9.1 commands FuSaOps consumes, this broke `qualify` for every project other than "run cfusa from inside the target directory". Add `--dir`/`-d` to `cmd_qualify`, and resolve a relative `--binary` path against it rather than always against the process cwd. Closes #88. Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
x-FuSa spec §3.1's `kind` enum is closed — `"misra-coverage"` is not a
member (the closest match is `"gap-report"`). §2.4.1 requires `standard`
to be a canonical lowercase id, never a display string — `misra` emitted
`"MISRA C:2012"` instead of the registry's `"misra-c"`. And §9.3 requires
a standards command's JSON to use the canonical `objectives[]` +
`summary{total,satisfied,partial,gaps}` shape, not an ad hoc `rules[]`
array with a per-rule `"rule"` field (the same anti-pattern already fixed
for the other six standards commands).
`misra --format json` violated all three: `"kind": "misra-coverage"`,
`"standard": "MISRA C:2012"`, and a `rules[]`/`covered`/`gaps` payload
shape with no `summary` object.
Closes #87.
Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
x-FuSa spec §9.3 (MUST): a standards command's JSON must carry a
`summary{total,satisfied,partial,gaps}` object satisfying
`satisfied + partial + gaps == total`. `iso26262`/`iec61508`/`do178`/
`iso21434`/`unece`/`iec62443` each emitted ad hoc top-level counters
(`covered`/`gaps`[/`na`][/`mandatoryGaps`/`recommendedGaps`]) with no
`summary` object at all — only `slsa` implemented the canonical shape
correctly. `iso26262` additionally had a latent counting bug: its
per-objective status already distinguished a mandatory "gap" from a
recommended "partial", but the top-level counter combined both into one
`gaps` value, so the two were inconsistent within the same document; it
now tracks them the same way `iec61508`/`iec62443` already did, and
exposes the recommended count as a new `recommendedGaps` field.
`iso21434` and `unece` additionally have `"manual"`/`"na"` per-objective
statuses for organisational evidence that isn't tool-checkable — not
members of §9.3's closed `satisfied|partial|gap` enum. Per that section's
own consumer fail-safe rule ("map any unrecognised status to gap"), the
new summary counts them as gaps for consistency.
Also, x-FuSa spec §2.4.1: `iec62443`'s gap-report emitted the non-registry
command name `"iec62443"` as its `standard` id instead of the split
`"iec62443-4-1"`/`"iec62443-4-2"` registry ids (issue #12 predates that
split). This command only implements the Component Requirements (-4-2)
part, so its `standard` is now `"iec62443-4-2"`.
Closes #85, closes #86.
Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
…change x-FuSa spec §1.6.2 (MUST — carry-forward across regeneration): an artifact-producing command must not silently discard an existing `attestation` object when it regenerates the artifact — it must carry the prior attestation forward unchanged, so staleness is detected via the carried-forward `contentHash` no longer matching the fresh content (falling back to "heuristic"), rather than the review record vanishing outright. `fmea`/`tara`/`safety-case`/`sas` each read the prior attestation back correctly, but then gated *emission* of the `"attestation"` block on `cfusa_qb_attestation_valid()` (independence + non-stale hash) instead of on `attestation.present`. The moment the artifact's analysed content changed — exactly the case §1.6.2 exists for — the attestation was dropped from the regenerated document entirely instead of being carried forward and re-evaluated as stale, leaving no audit trail that a review had ever happened. Gate emission on `attestation.present` (carrying the loaded object's actual `status` forward verbatim) in all four commands; validity/ staleness stays a *consumer*-side concern, as the spec text says. Closes #83. Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
Adds tests/test_xfusa_audit_20260728.c, one (or a small group of) test(s) per issue fixed in this sprint (#82-#91): the CFUSA-L004 suffix false positive and its real-recursion negative case, fmea's attestation carry-forward-as-stale across a content change, the canonical standard/clause fields on a lint finding plus the SARIF driver name, summary{} presence and the satisfied+partial+gaps==total invariant across all six standards commands (iso26262/iec61508/do178/iso21434/unece plus iec62443's canonical id), misra's canonical gap-report schema, qualify accepting --dir, sas writing both sas.json and a real sas.md companion, and hara init's envelope-free §1.2.5 scaffold. Each test reproduces its issue's failure mode directly against the library code (not by shelling out to the built binary), and was verified to fail against the pre-fix code before the corresponding fix commit. Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
Three synthetic argv[] arrays were passed to cmd_fmea()/cmd_sas() with argc one less than the array's actual element count (excluding the NULL terminator) — e.g. a 7-element argv (including "--attest" and its value) called with argc=6. This under-count wasn't visible to getopt_long on macOS/BSD libc, but on glibc (the CI Linux runners) it made getopt_long report "--attest requires an argument" and left the command in an inconsistent state that went on to segfault, failing the Linux legs of CI while passing locally. Verified against a real Ubuntu 22.04 container (matching the CI image) with both gcc and the system toolchain: all 40 suites now pass there, where test_xfusa_audit_20260728 previously segfaulted. Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
There was a problem hiding this comment.
c-FuSa found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
CodeQL (3 high-severity) flagged three fopen(path, "w") call sites in
tests/test_xfusa_audit_20260728.c as creating world-writable files —
the same class of finding the repo already fixed elsewhere by routing
file creation through cfusa_fopen_write() (restrictive mode + O_EXCL-ish
semantics) instead of raw fopen(). Switch all three sites to it.
c-FuSa's own self-check (CFUSA-CY003 / CWE-78) flagged l004_reset_dir()'s
system("rm -rf ...") cleanup helper as OS command execution with a
non-literal argument. The helper never needed shell semantics in the
first place — each fixture subdirectory only ever holds one specific,
statically-known file — so replace it with direct remove()/rmdir() calls
instead of shelling out.
Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes all 10 concrete, actionable defects filed by the 2026-07-28/29 deep audit (issue #66 is a separable large feature request — AUTOSAR rule pack + Coverity import + CBMC proof coverage — and is intentionally left open/out of scope for this bug-fix pass).
tool.driver.nameis now"c-FuSa"(the §1.1 tool name), not the binary name"cfusa"(§2.9 MUST).check/lint/analyze/cyberfindings now emit a canonicalstandardid (§2.4.1) with a separateclausefield, instead of a combined display string like"MISRA-C:2012 R15.1".CFUSA-L004no longer false-positives when a callee's name merely has the caller's name as a suffix (e.g.evaluate()callinghelper_evaluate()).hara initnow scaffolds.fusa-hara.jsonper the §1.2.5 input schema — no report envelope, no"kind": "hara"(not in the closedkindenum).sas --format json(no--output) now writes realsas.jsonand a realsas.mdcompanion, instead of raw JSON into a file literally namedsas.md.qualifynow accepts--dir(§2.2: applies to all commands) instead of exiting2for any project other than the cwd.misranow uses the canonical §9.3 gap-report schema (kind: "gap-report",standard: "misra-c",objectives[]/summary{}).iso26262/iec61508/do178/iso21434/unece/iec62443gap-report JSON now carries asummary{total,satisfied,partial,gaps}object satisfying the total invariant (§9.3 MUST);iec62443'sstandardid is now the registry value"iec62443-4-2"instead of the bare command name.fmea/tara/safety-case/sasnow carry a priorattestationforward verbatim (preserved-as-stale) when content changes, instead of silently dropping it (§1.6.2 MUST).Test plan
cmake -B build -DCMAKE_BUILD_TYPE=Debug && cmake --build build --parallel— clean build, no warnings introducedctest --test-dir build— 40/40 suites pass, including a newtests/test_xfusa_audit_20260728.cwith one (or a small group of) regression test(s) per issue, each verified to fail against the pre-fix code-fsanitize=address,undefined(no leak-detector, unsupported on macOS)./build/cfusa check --dir .self-check still runs cleanly (exit 1 on pre-existing findings, no crash)Closes #82, closes #83, closes #84, closes #85, closes #86, closes #87, closes #88, closes #89, closes #90, closes #91.