fix: v0.5.49 — ASIL Table 4 correction + two live-exploitable injection fixes - #95
Merged
Merged
Conversation
…on fixes
External third-party audit remediation. Two of the findings are proven,
live-reproduced security vulnerabilities:
- impact: git-ref validator accepted a leading '-' and built `git diff`
with no `--` separator, letting a crafted --from ref smuggle a git flag
(reproduced: `cfusa impact --from '--output=victim.txt' --to HEAD`
truncates victim.txt). Refs starting with '-' are now rejected and a `--`
separator is always inserted.
- audit-pack: interpolated --output/--dir unsanitized into a double-quoted
system("cd ... && zip ...") string, so `$(...)` command substitution
executed inside the shell (reproduced: `cfusa audit-pack --output
'x.zip$(touch /tmp/pwned)'` ran the injected command). Replaced with
fork+execvp (argv passed directly, no shell) and a POSIX recursive
remove for staging cleanup.
Also fixes:
- Critical: the shared cfusa_compute_asil() table (src/asil.c) mis-derived
19 of 36 ISO 26262-3:2018 Table 4 S x E x C cells (all inflated); now
implements the additive S+E+C model. tests/test_asil_table.c's
"exhaustive" 36-cell test previously only asserted exit==0 and never the
returned ASIL string, so it passed against the wrong table; it now
asserts the exact ASIL per cell. The dogfooded .fusa-hara.json shipped
over-classified hazard ASILs as a direct consequence and is regenerated.
- C0 controllability now short-circuits to QM per ISO 26262-3:2018 4.3.5.
- Out-of-range S/E/C now exits 2 instead of silently coercing to QM.
- New DUPREQ001 check-engine rule: a duplicate requirement id in
.fusa-reqs.json previously only printed to stderr from `trace` and never
gated any exit code; it now fails `check` as a real, fingerprinted
Finding.
- trace reads the canonical "parent" key (with "parentId" as a legacy
fallback) instead of only the non-canonical "parentId".
- The report envelope no longer hardcodes an always-empty "errors": []
array; the spec's singular error{code,message} object is now emitted
only when a runtime error occurred.
- cfusa_read_file()'s unchecked ftell() can no longer wrap into an
oversized/negative allocation (heap-overflow/DoS guard).
- trace's requirement-object parsing no longer truncates objects over 1KB
in a fixed stack buffer.
- .fusa.json's stale project.version ("0.5.1") now matches the shipped
tool version; README points at canonical .fusa-* config names.
- qualify's timestamp honours SOURCE_DATE_EPOCH; CI no longer masks 6
meaningful steps behind `|| true`; docker-publish pinned to
ubuntu-22.04; the stale committed .cfusa_qualification.json is removed
(CI regenerates it as an artifact).
40/40 tests pass (plus 2 new DUPREQ001 tests).
Signed-off-by: Matt Jones <matt@jellybaby.com>
Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
D007 (part of the applied patch set) removed `|| true` masking from CI
steps that depend on `cfusa check`'s exit code across the whole c-FuSa
source tree. That surfaced real, previously-hidden findings — some
pre-existing, one newly introduced by this branch's own audit-pack fix:
- cmd_audit_pack.c's new ap_rmdir_recursive() and cmd_qualify.c's
pre-existing qt_rmdir_recursive() both used genuine user-code recursion
(MISRA-C 2012 Rule 17.2, CFUSA-L004). Both are now iterative, built on
POSIX nftw(FTW_DEPTH|FTW_PHYS) instead of a self-recursive helper.
- cmd_comp.c had two lines each freeing two distinct pointers
(`free(a); free(b);` on one line), which a same-line text scan mistook
for a double-free (CFUSA-CY007 false positive). The frees are now on
separate lines.
- Two test function names coincidentally contained the substrings `des_`
(in "...includes_end_line...") and `system(` (in "...build_system(void)"),
false-triggering the weak-crypto and unchecked-system-call rules
(CFUSA-CY009/CY003). Both renamed; no behavior change.
- cmd_impact.c's run_git_diff() still used popen() with a shell command
string for `git diff` (CFUSA-CY003, CWE-78) even after the previous
commit's ref-validation/`--`-separator fix. Since we're already
hardening this exact function against injection, went further and
removed the shell entirely: fork+execvp("git", argv), matching the
pattern used for audit-pack's zip invocation.
- README gained a version badge so the new version-consistency CI check
(also part of D007) has an actual version string to verify in README.md,
not just CHANGELOG.md.
`cfusa iso26262`'s gap-report step is intentionally left `|| true`: it
exits 1 whenever any §9.3 gap remains by design (a strict completeness
gate, not a check-engine Finding), and closing every long-standing
documentation/process gap is a separate, much larger effort than this
release's security/ASIL-table remediation scope.
`cfusa check --dir .` now exits 0 against c-FuSa's own source (previously
always non-zero, just masked). 40/40 ctest still pass. Both live exploits
re-verified closed against this commit.
Signed-off-by: Matt Jones <matt@jellybaby.com>
Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
cmd_audit_pack.c and cmd_qualify.c's new iterative rmdir helpers (from the previous commit) failed to compile on ubuntu-22.04 with both gcc and clang: error: 'FTW_DEPTH' undeclared error: 'FTW_PHYS' undeclared warning: implicit declaration of function 'nftw' nftw()/FTW_DEPTH/FTW_PHYS are XSI extensions that glibc hides unless _XOPEN_SOURCE >= 500 (or _DEFAULT_SOURCE) is defined before the first system header include; _POSIX_C_SOURCE alone does not expose them. They're visible by default on macOS/BSD libc, which is why the macos-14 job built fine while both ubuntu-22.04 jobs failed. Added _XOPEN_SOURCE 700 alongside the existing _POSIX_C_SOURCE 200809L in both files. Verified with a local ubuntu:22.04 Docker build (gcc, cmake) — clean compile, 40/40 ctest pass, `cfusa check` self-check exit 0 — and a native macOS rebuild (clang) unaffected. Signed-off-by: Matt Jones <matt@jellybaby.com> Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
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.
Two remaining CI checks were still failing after the last commit: - GitHub Advanced Security's CodeQL default-setup check flagged a critical "unbounded write" in cmd_req.c's append_entry() (used by `req import` for CSV/ReqIF/XML sources): it tracked the destination buffer's fill level via a caller-passed running total rather than the buffer's actual content length, then appended with strcat(). Rewrote it to measure the buffer's real length directly (strlen, since the buffer is always zero-initialized and kept NUL-terminated within bounds by this same function) and append with an exact-length memcpy() bounded against that — removes the dependency on the caller's bookkeeping entirely. All CSV/ ReqIF/XML import tests in test_req_trace_cov.c still pass. - The "Docker Build" job's "Self-check via Docker" step ran `check --dir /workspace/src`, i.e. only the src/ subdirectory of the mounted repo. src/ alone can never contain the root-level .fusa.json/.fusa-hara.json, so once this step's exit code started being enforced (previous commit's `|| true` removal) it failed unconditionally on FUSA001/HARA001, regardless of any code change. Changed it to `--dir /workspace` to match the native "Self-check" step's `--dir .`. Verified by building the actual Dockerfile locally and running the exact CI command against it: exit 0. 40/40 ctest pass; `cfusa check --dir .` exits 0 natively and inside the built Docker image; both live exploits re-verified closed in the Docker image too. Signed-off-by: Matt Jones <matt@jellybaby.com> Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
This was referenced Jul 30, 2026
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
External third-party audit remediation for c-FuSa. This PR fixes two proven, live-reproduced security vulnerabilities plus a Critical correctness defect in the ISO 26262 ASIL derivation table, and several smaller robustness/spec-conformance issues.
Security — two live-exploitable vulnerabilities
impactgit-ref argument injection. The--from/--tovalidator accepted a value beginning with-and built the underlyinggit diffinvocation with no--separator between refs and paths. A crafted--fromcould smuggle an arbitrarygit diffflag.Reproduced:
cfusa impact --from '--output=victim.txt' --to HEADtruncatesvictim.txtvia git's own--outputflag.Fix: refs starting with
-are now rejected outright, and a--separator is always inserted before the ref arguments.audit-packshell command injection.--output/--dirwere interpolated unsanitized into a double-quotedsystem("cd ... && zip ...")string;$(...)command substitution is still expanded by the shell inside double quotes.Reproduced:
cfusa audit-pack --output 'x.zip$(touch /tmp/pwned)'executes the injected command.Fix: the
system()/zip/rm -rfshell pipeline is replaced withfork+execvp(argv passed directly, never interpreted by a shell) and a POSIX recursive remove for staging cleanup.Both exploits were re-verified live against this branch's build (before and after) as part of this PR — before the fix, both succeeded; after, neither does.
Critical correctness fix
cfusa_compute_asil()table mis-derived 19 of 36 S×E×C cells (all inflated). It now implements the additive S+E+C model (≤6→QM, 7→A, 8→B, 9→C, 10→D). The "exhaustive" 36-cell test previously only assertedexit==0and never the returned ASIL string, so it passed against the wrong table — it now asserts the exact ASIL per cell. The dogfooded.fusa-hara.jsonshipped over-classified hazard ASILs as a direct consequence and has been regenerated.Other fixes
DUPREQ001check-engine rule: a duplicate requirement id in.fusa-reqs.jsonpreviously only printed to stderr fromtraceand never gated any exit code — it now failscheckas a real, fingerprintedFinding(with test coverage).tracereads the canonicalparentkey (falling back to legacyparentId) instead of onlyparentId."errors": []array; the spec's singularerror{code,message}object is emitted only when a runtime error occurred.cfusa_read_file()'s uncheckedftell()can no longer wrap into an oversized/negative allocation (heap-overflow/DoS guard).trace's requirement-object parsing no longer truncates objects over 1KB in a fixed stack buffer..fusa.json's staleproject.version("0.5.1") now matches the shipped tool version; README points at canonical.fusa-*config names.qualify's timestamp honoursSOURCE_DATE_EPOCH; CI no longer masks 6 meaningful steps behind|| true;docker-publishpinned toubuntu-22.04; the stale committed.cfusa_qualification.jsonis removed (CI regenerates it as an artifact).Version bumped 0.5.48 → 0.5.49 (CMakeLists.txt, include/cfusa/version.h, .fusa.json).
Test plan
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug && cmake --build build— clean build, no new warningsctest --test-dir build— 40/40 tests pass (including 2 newDUPREQ001tests).fusa-reqs.jsonhas no pre-existing duplicate ids (newDUPREQ001rule doesn't regress the tool's own self-check)