Skip to content

fix: v1.145.0 — second FuSaOps self-audit (8 bugs) + spec v1.15.1 clarification - #100

Merged
SoundMatt merged 1 commit into
mainfrom
fix/self-audit-round2-v1.15.1
Jul 29, 2026
Merged

SoundMatt merged 1 commit into
mainfrom
fix/self-audit-round2-v1.15.1

Conversation

@SoundMatt

Copy link
Copy Markdown
Owner

Summary

Closes #91, closes #92, closes #93, closes #94, closes #95, closes #96, closes #97, closes #98, closes #99.

A follow-up deep audit of FuSaOps itself (run immediately after the prior 10-bug self-audit fix shipped as v1.144.0) found 8 more real defects plus one genuine spec ambiguity:

  • fmea.json's standard field bypassed the existing CanonicalStandardID helper every sibling package uses
  • capabilities.json listed the non-canonical "iec62443" instead of "iec62443-4-1"/"iec62443-4-2"
  • hara show --format json was the one artifact command (of six) missing the §3.1 common header
  • 5 duplicate requirement ids in .fusa-reqs.json, with no detection mechanism anywhere
  • adapter.Check() discarded the tool's exit code and never decoded the §3.2 error envelope — a crashed (exit 3) run was treated as clean
  • decoded finding category wasn't normalized to "other" for unrecognized values, unlike severity
  • the aggregate Finding dropped disposition/standard/clause — the disposition gap was behavioral: an upstream-waived ERROR finding still gated FuSaOps' own exit code
  • aggregate renderers never emitted the spec-mandated qualified "<language>/<ruleId>" form

Also resolves #99: two independent tools (FuSaOps and py-FuSa) already emit the full MAJOR.MINOR.PATCH spec version, contradicting the spec's MAJOR.MINOR-only text — spec v1.15.1 reconciles the prose to match established practice rather than demanding a retrofit.

See CHANGELOG.md for the full per-issue breakdown.

Test plan

  • go build ./..., go vet ./... clean
  • go test ./... — all packages pass, including new regression tests for each fix (Gates/QualifiedRuleID, FindDuplicateIDs, normaliseCategory, exit-3 handling, HasErrors+disposition, hara common header)
  • Coverage on touched packages: adapter 96.6%, report 98.6%, req 96.6%, hara 92.1%, fmea 94.0%, cmd/fusaops 92.6%, root 88.3% — all ≥80% gate
  • Regenerated fmea.json and manually verified capabilities.json/hara show output

…rification

A follow-up deep audit of FuSaOps itself found 8 real defects the prior
self-audit fix missed: fmea.json's standard field bypassing the
CanonicalStandardID helper, capabilities.json's non-canonical iec62443 id,
hara show --format json missing the common header, 5 duplicate requirement
ids with no detection anywhere, adapter.Check() discarding exit codes and
the §3.2 error envelope, uncategorized findings not normalizing to "other",
the aggregate dropping disposition/standard/clause (re-gating on findings
already waived upstream), and aggregate renderers never emitting the
spec-mandated qualified "<language>/<ruleId>" form.

Also resolves #99: the spec text said schemaVersion was
MAJOR.MINOR only, but two independent tools already emit the full
MAJOR.MINOR.PATCH — spec v1.15.1 reconciles the text to match.

See CHANGELOG.md for the full per-issue breakdown.

Signed-off-by: SoundMatt <SoundMatt@users.noreply.github.com>
Comment thread adapter/adapter.go

// The runner intentionally ignores the tool's exit code; we read the file.
_, _ = a.run(ctx, root, a.tool, "check", "--format", "json", "--output", out)
_, runErr := a.run(ctx, root, a.tool, "check", "--format", "json", "--output", out)
Comment thread adapter/adapter_test.go
//fusa:test REQ-FO-ADP006
func TestParseToolReport(t *testing.T) {
findings, err := parseToolReport([]byte(sampleReport), fusaops.LangGo, "go-FuSa")
findings, _, err := parseToolReport([]byte(sampleReport), fusaops.LangGo, "go-FuSa")
Comment thread adapter/adapter_test.go

func TestParseToolReportInvalid(t *testing.T) {
if _, err := parseToolReport([]byte("{not json"), fusaops.LangGo, "t"); err == nil {
if _, _, err := parseToolReport([]byte("{not json"), fusaops.LangGo, "t"); err == nil {
Comment thread adapter/adapter_test.go
return nil, errors.New("testtool exited 3 (runtime/internal error)")
},
}
_, err := a.Check(context.Background(), t.TempDir())
Comment thread adapter/adapter_test.go
return nil, nil
},
}
_, err := a.Check(context.Background(), t.TempDir())
Comment thread fusaops_test.go
}
}

//fusa:test REQ-FO-CORE011
Comment thread fusaops_test.go
}
}

//fusa:test REQ-FO-CORE012
Comment thread adapter/adapter.go
}
if runErr != nil {
if tr.Error != nil {
return findings, fmt.Errorf("adapter %s: runtime error (%s): %s", a.name, tr.Error.Code, tr.Error.Message)
Comment thread adapter/adapter.go
return findings, fmt.Errorf("adapter %s: %w", a.name, runErr)
}
if tr.Error != nil {
return findings, fmt.Errorf("adapter %s: runtime error (%s): %s", a.name, tr.Error.Code, tr.Error.Message)
Comment thread adapter/adapter.go
}

// validCategories is the x-FuSa spec §4 closed, extensible category enum.
var validCategories = map[string]bool{
@SoundMatt
SoundMatt merged commit b0fbd4a into main Jul 29, 2026
13 checks passed
@SoundMatt
SoundMatt deleted the fix/self-audit-round2-v1.15.1 branch July 29, 2026 02:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment