findingspec

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2026 License: MIT Imports: 5 Imported by: 0

README

findingspec

findingspec is a Go module defining a domain-neutral model for findings — anything that needs attention — across multiple problem domains: security, accessibility (a11y), internationalization (i18n), and quality engineering (qe).

A single canonical Finding type carries the fields common to every domain (identity, severity, status, location, evidence, remediation). Each domain package defines richer, semantically-typed findings and projects them into a Finding via a ToFinding method — so producers keep domain-specific detail while consumers reason over one uniform representation.

import "github.com/plexusone/findingspec"

Model

findingspec              canonical Finding, FindingSet, and shared vocabulary
├── security             vulnerabilities & secrets, CVSS/CWE/CVE, residual risk
├── a11y                 WCAG accessibility issues
├── i18n                 internationalization / localization issues
└── qe                   quality-engineering findings (E2E tests, journeys)

The core package's only external dependency is the shared priority-frameworks severity framework, and it does not import any domain package, keeping the shared vocabulary neutral. Only domain packages depend on the core.

Shared vocabulary
  • Severity — critical, high, medium, low, informational (canonical IDs; display names via Severity.Name()), backed by the shared priority-frameworks Severity framework for parsing, ordering, and CVSS mapping
  • Status — open, confirmed, remediated, accepted, false_positive, resolved
  • Confidence — high, medium, low
  • Location — code (File/Line), runtime (URL/Selector), or a node inside a structured document (Pointer = RFC 6901 JSON Pointer + DocFormat, e.g. OpenAPI/Postman), plus Repo and Component
  • Evidence, Remediation (with an AgentPrompt for AI builders), Reference

The security domain also models detected secrets (security.Secret) — API keys, tokens, and private keys — including secrets located inside OpenAPI specs or Postman collections via Location.Pointer, without ever storing the raw value.

Example

package main

import (
	"fmt"

	"github.com/plexusone/findingspec"
	"github.com/plexusone/findingspec/a11y"
	"github.com/plexusone/findingspec/security"
)

func main() {
	set := findingspec.NewFindingSet()

	set.Add(security.Vulnerability{
		ID:       "VULN-1",
		Title:    "SQL injection in search API",
		Severity: findingspec.SeverityHigh,
		CWEs:     []string{"CWE-89"},
	}.ToFinding())

	set.Add(a11y.Issue{
		ID:        "A11Y-1",
		Title:     "Text has insufficient contrast",
		Criterion: "1.4.3",
		Level:     a11y.LevelAA,
		Impact:    a11y.ImpactSerious,
	}.ToFinding())

	set.SortBySeverity()
	fmt.Println(set.CountByDomain()) // map[a11y:1 security:1]
}

Documentation

Full documentation is built with MkDocs (Material) from the docs/ directory and published at https://plexusone.github.io/findingspec/. Build it locally with:

mkdocs serve   # live preview at http://127.0.0.1:8000
mkdocs build   # render to ./site

Specifications live in docs/specs/ (PRD, TRD, Roadmap).

Relationship to govex

The security domain's concepts (inherent vs. residual severity, compensating controls, exceptions, CVSS/CWE/CVE) were first prototyped in github.com/grokify/govex. findingspec re-expresses them cleanly against a shared, multi-domain vocabulary and does not depend on govex.

License

MIT

Documentation

Overview

Package findingspec defines a domain-neutral model for representing a finding — anything that needs attention — across multiple problem domains: security, accessibility (a11y), internationalization (i18n), and quality engineering (qe).

The canonical Finding type carries the fields common to every domain (identity, severity, status, location, evidence, remediation). Domain packages under this module (security, a11y, i18n, qe) define richer, semantically-typed findings and project them into a Finding via a ToFinding method, so producers keep domain-specific detail while consumers can reason over a single, uniform representation.

The core package's only external dependency is the shared priority-frameworks severity framework (github.com/grokify/priority-frameworks); it does not import any domain package, keeping the shared vocabulary neutral.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DetailAs

func DetailAs[T any](f Finding) (T, error)

DetailAs decodes a finding's Detail subsection into a value of type T, using the finding's Type as the discriminator the caller keys on. It returns the zero value of T when Detail is empty.

Types

type Confidence

type Confidence string

Confidence expresses how certain a producer is that a finding is a true positive. It is distinct from severity, which measures impact.

const (
	ConfidenceHigh   Confidence = "high"
	ConfidenceMedium Confidence = "medium"
	ConfidenceLow    Confidence = "low"
)

func (Confidence) Valid

func (c Confidence) Valid() bool

Valid reports whether c is a known confidence level.

type Domain

type Domain string

Domain identifies the problem space a Finding belongs to. Each domain has a corresponding package in this module that defines its semantic finding types.

const (
	// DomainSecurity covers vulnerabilities, misconfigurations, and other
	// security weaknesses.
	DomainSecurity Domain = "security"
	// DomainA11y covers accessibility (WCAG) conformance issues.
	DomainA11y Domain = "a11y"
	// DomainI18n covers internationalization and localization issues.
	DomainI18n Domain = "i18n"
	// DomainQE covers quality-engineering findings such as failed end-to-end
	// tests or broken user journeys.
	DomainQE Domain = "qe"
	// DomainCompliance covers control-conformance findings against a framework
	// (e.g. CIS, STIG, FedRAMP, SOC 2, ISO 27001). A failed control is a
	// finding; the overall audit or scan run that produced it is a higher-level
	// assessment (see assessmentspec).
	DomainCompliance Domain = "compliance"
	// DomainObservability covers runtime findings such as SLO/SLA violations,
	// error-rate or latency regressions, and failing web vitals, derived from
	// APM, synthetic monitoring, or RUM signals.
	DomainObservability Domain = "observability"
)

func Domains

func Domains() []Domain

Domains returns all known domains in a stable order.

func (Domain) Valid

func (d Domain) Valid() bool

Valid reports whether d is a known domain.

type Evidence

type Evidence struct {
	// Kind categorizes the evidence, e.g. "screenshot", "html", "log",
	// "trace", "http-response", "code".
	Kind string `json:"kind"`
	// Summary is a short human-readable description of the evidence.
	Summary string `json:"summary,omitempty"`
	// Data holds inline evidence content; binary content should be base64.
	Data string `json:"data,omitempty"`
	// URI points to externally-stored evidence when it is not inlined.
	URI string `json:"uri,omitempty"`
	// MediaType is the IANA media type of Data or the URI target, if known.
	MediaType string `json:"mediaType,omitempty"`
}

Evidence is a supporting artifact that substantiates a finding, such as a screenshot, an HTTP response, a stack trace, or a code excerpt.

type Finding

type Finding struct {
	// ID is a stable identifier for this finding, unique within its producer.
	ID string `json:"id"`
	// Domain is the broad problem space this finding belongs to.
	Domain Domain `json:"domain"`
	// Type is the finding class within the domain — the scanner or test kind,
	// e.g. "sast", "sca", "secret", "container", "wcag", "e2e", "apm", "cis".
	// Together with Domain it forms the two-level type taxonomy and, when set,
	// tells a consumer how to decode Detail.
	Type string `json:"type,omitempty"`
	// RuleID identifies the domain rule or check that produced the finding,
	// e.g. a CWE ID, a WCAG success criterion, or a lint rule name.
	RuleID string `json:"ruleId,omitempty"`
	// Title is a short human-readable summary.
	Title string `json:"title"`
	// Description explains the finding in detail.
	Description string `json:"description,omitempty"`
	// Severity is the impact of the finding on the shared severity scale.
	Severity Severity `json:"severity"`
	// Confidence expresses certainty that the finding is a true positive.
	Confidence Confidence `json:"confidence,omitempty"`
	// Status is the lifecycle state of the finding.
	Status Status `json:"status,omitempty"`
	// Source identifies the producing tool.
	Source Source `json:"source,omitempty"`
	// Location is where the finding was observed.
	Location *Location `json:"location,omitempty"`
	// Evidence holds supporting artifacts.
	Evidence []Evidence `json:"evidence,omitempty"`
	// Remediation describes how to fix the finding.
	Remediation *Remediation `json:"remediation,omitempty"`
	// References links to external material.
	References []Reference `json:"references,omitempty"`
	// Tags are free-form labels for filtering and grouping.
	Tags []string `json:"tags,omitempty"`
	// DetectedAt is when the finding was observed.
	DetectedAt *time.Time `json:"detectedAt,omitempty"`
	// Detail is the typed, per-type subsection carrying the domain- and
	// type-specific payload (e.g. package/CVE/CVSS for an SCA finding, or
	// detector/entropy for a secret). It is opaque at the core layer; decode it
	// with DetailAs once Type is known. Set it with SetDetail.
	Detail json.RawMessage `json:"detail,omitempty"`
}

Finding is the canonical, domain-neutral representation of something that needs attention. Domain packages (security, a11y, i18n, qe) produce their own semantic types and project them into a Finding via a ToFinding method.

func (*Finding) SetDetail

func (f *Finding) SetDetail(v any) error

SetDetail marshals v into the finding's Detail subsection. Passing nil clears it.

func (Finding) Validate

func (f Finding) Validate() error

Validate reports whether the finding carries the minimum required fields: an ID, a valid domain, a title, and a valid severity.

type FindingSet

type FindingSet struct {
	Findings []Finding `json:"findings"`
}

FindingSet is a collection of findings with convenience helpers for filtering, counting, and ordering across domains.

func NewFindingSet

func NewFindingSet(findings ...Finding) *FindingSet

NewFindingSet returns a FindingSet containing the provided findings.

func (*FindingSet) Add

func (s *FindingSet) Add(findings ...Finding)

Add appends findings to the set.

func (*FindingSet) CountBy

func (s *FindingSet) CountBy(key func(Finding) string) map[string]int

CountBy groups findings by a caller-provided key function, returning the count per distinct key. It is the general form behind the typed CountBy* helpers; use it for any dimension that lacks a dedicated helper, e.g. by rule:

set.CountBy(func(f findingspec.Finding) string { return f.RuleID })

func (*FindingSet) CountByDomain

func (s *FindingSet) CountByDomain() map[Domain]int

CountByDomain returns the number of findings in each domain.

func (*FindingSet) CountByFile

func (s *FindingSet) CountByFile() map[string]int

CountByFile returns the number of findings per file (Location.File). Findings without a location or file are counted under "".

func (*FindingSet) CountByRepo

func (s *FindingSet) CountByRepo() map[string]int

CountByRepo returns the number of findings per top-level location — the repository or workspace (Location.Repo). Findings without a location or repo are counted under "".

func (*FindingSet) CountBySeverity

func (s *FindingSet) CountBySeverity() map[Severity]int

CountBySeverity returns the number of findings at each severity.

func (*FindingSet) CountBySource

func (s *FindingSet) CountBySource() map[string]int

CountBySource returns the number of findings per producing tool (Source.Tool). Findings with no tool are counted under "".

func (*FindingSet) CountByStatus

func (s *FindingSet) CountByStatus() map[Status]int

CountByStatus returns the number of findings in each status.

func (*FindingSet) Domain

func (s *FindingSet) Domain(d Domain) *FindingSet

Domain returns a new set containing only findings in the given domain.

func (*FindingSet) Len

func (s *FindingSet) Len() int

Len returns the number of findings in the set.

func (*FindingSet) Open

func (s *FindingSet) Open() *FindingSet

Open returns a new set containing only findings whose status still requires attention.

func (*FindingSet) Severity

func (s *FindingSet) Severity(sev Severity) *FindingSet

Severity returns a new set containing only findings of the given severity.

func (*FindingSet) SortBySeverity

func (s *FindingSet) SortBySeverity()

SortBySeverity orders the findings in place from most to least severe, with ties broken by domain then ID for stable output.

func (*FindingSet) Summary

func (s *FindingSet) Summary() Summary

Summary computes an analytics rollup over the set in a single pass.

type Location

type Location struct {
	// Repo identifies the repository or workspace a finding belongs to, when
	// a producer scans more than one (e.g. a multi-repo sweep, or a monorepo
	// workspace) — a path, URL, or module name, at the producer's discretion.
	// Empty when a producer only ever scans a single, implicit repository.
	Repo string `json:"repo,omitempty"`
	// File is a repository-relative source path.
	File string `json:"file,omitempty"`
	// Line is a 1-indexed line number within File.
	Line int `json:"line,omitempty"`
	// Column is a 1-indexed column within Line.
	Column int `json:"column,omitempty"`
	// URL is the address of the page or endpoint where the finding was observed.
	URL string `json:"url,omitempty"`
	// Selector is a CSS or XPath selector identifying a DOM element.
	Selector string `json:"selector,omitempty"`
	// Component is a logical component, module, or capability name.
	Component string `json:"component,omitempty"`
	// Pointer is an RFC 6901 JSON Pointer identifying a node within a structured
	// document (JSON/YAML), e.g. "/servers/0/variables/apiKey/default" in an
	// OpenAPI document or "/item/2/request/header/1/value" in a Postman
	// collection. It is a stable, format-independent locator preferable to
	// Line/Column for structured files.
	Pointer string `json:"pointer,omitempty"`
	// DocFormat names the structured-document format the File and Pointer refer
	// to, when relevant, e.g. "openapi", "postman", "json", "yaml".
	DocFormat string `json:"docFormat,omitempty"`
	// Snippet is a short excerpt of the offending code or markup.
	Snippet string `json:"snippet,omitempty"`
}

Location describes where a finding was observed. Its fields are intentionally domain-flexible: a code-level finding (security, i18n) typically uses File and Line, while a runtime UI finding (a11y, qe) typically uses URL and Selector. Component names the logical module or capability regardless of representation.

func (Location) Empty

func (l Location) Empty() bool

Empty reports whether the location carries no information.

type Reference

type Reference struct {
	Title string `json:"title,omitempty"`
	URL   string `json:"url"`
}

Reference is an external link supporting a finding or its remediation, such as an advisory, standard, or documentation page.

type Remediation

type Remediation struct {
	// Summary is a concise statement of the fix.
	Summary string `json:"summary"`
	// Detail is an optional longer explanation.
	Detail string `json:"detail,omitempty"`
	// AgentPrompt is an instruction suitable for handing to an AI code builder
	// (e.g. "fix this without changing application behavior").
	AgentPrompt string `json:"agentPrompt,omitempty"`
	// Effort is a coarse estimate, e.g. "low", "medium", "high".
	Effort string `json:"effort,omitempty"`
	// References supports the remediation with external links.
	References []Reference `json:"references,omitempty"`
}

Remediation describes how to resolve a finding. It carries both a human-readable summary and an optional AgentPrompt — an instruction phrased for an AI builder to apply the fix directly.

type Severity

type Severity string

Severity is a severity level identifier drawn from the canonical priority-frameworks Severity framework: Critical, High, Medium, Low, Informational. Values are the framework's lower-case level IDs; use Name for the display form.

const (
	SeverityCritical      Severity = "critical"
	SeverityHigh          Severity = "high"
	SeverityMedium        Severity = "medium"
	SeverityLow           Severity = "low"
	SeverityInformational Severity = "informational"
)

func ParseSeverity

func ParseSeverity(s string) (Severity, bool)

ParseSeverity normalizes a severity string — a level ID, display name, or alias (e.g. "High", "high", "HIGH", "S2") — to a canonical Severity. It returns false if the value is not recognized.

func Severities

func Severities() []Severity

Severities returns all severities ordered from most to least severe.

func SeverityFromCVSS

func SeverityFromCVSS(score float64) Severity

SeverityFromCVSS maps a CVSS base score (0.0–10.0) to the canonical severity using the standard CVSS bands (Critical ≥9.0, High ≥7.0, Medium ≥4.0, Low ≥0.1, Informational 0.0). Scores outside 0.0–10.0 map to Informational.

func (Severity) Abbreviation

func (s Severity) Abbreviation() string

Abbreviation returns a short display form (e.g. "CRIT" for critical), for a caller that wants a compact rendering (e.g. a text-report column) instead of Name's full form. It returns the raw value if the severity is unknown.

func (Severity) Actionable

func (s Severity) Actionable() bool

Actionable reports whether items at this severity require action. Informational is not actionable.

func (Severity) MoreSevereThan

func (s Severity) MoreSevereThan(other Severity) bool

MoreSevereThan reports whether s is strictly more severe than other.

func (Severity) Name

func (s Severity) Name() string

Name returns the canonical display name for the severity, e.g. "Critical". It returns the raw value if the severity is unknown.

func (Severity) Rank

func (s Severity) Rank() int

Rank returns the ordering rank of s (lower is more severe). Unknown severities rank after all known ones.

func (Severity) Valid

func (s Severity) Valid() bool

Valid reports whether s is a known severity level.

type Source

type Source struct {
	// Tool is the producer name, e.g. "govex", "agent-a11y", "w3pilot".
	Tool string `json:"tool"`
	// Version is the producer version, if known.
	Version string `json:"version,omitempty"`
	// RuleSet names the rule catalog or standard the producer applied, e.g.
	// "WCAG-2.2", "CWE", "OWASP-ASVS".
	RuleSet string `json:"ruleSet,omitempty"`
}

Source identifies the tool or producer that generated a finding.

type Status

type Status string

Status is the lifecycle state of a Finding.

const (
	// StatusOpen is a newly-reported finding awaiting triage.
	StatusOpen Status = "open"
	// StatusConfirmed is a finding verified as a true positive.
	StatusConfirmed Status = "confirmed"
	// StatusRemediated is a finding whose fix has been applied.
	StatusRemediated Status = "remediated"
	// StatusAccepted is a finding whose risk has been formally accepted.
	StatusAccepted Status = "accepted"
	// StatusFalsePositive is a finding determined not to be a real issue.
	StatusFalsePositive Status = "false_positive"
	// StatusResolved is a finding confirmed fixed after re-assessment.
	StatusResolved Status = "resolved"
)

func Statuses

func Statuses() []Status

Statuses returns all known statuses in a stable order.

func (Status) Open

func (s Status) Open() bool

Open reports whether the finding still requires attention. A finding is open unless it has been explicitly closed (remediated, resolved, accepted, or dismissed as a false positive); an unset or unknown status is treated as open, so freshly-detected findings count as needing attention by default.

func (Status) Valid

func (s Status) Valid() bool

Valid reports whether s is a known status.

type Summary

type Summary struct {
	// Total is the number of findings.
	Total int `json:"total"`
	// Open is the number of findings whose status still needs attention.
	Open int `json:"open"`
	// Actionable is the number of findings whose severity is actionable
	// (i.e. not Informational).
	Actionable int `json:"actionable"`
	// BySeverity counts findings per severity.
	BySeverity map[Severity]int `json:"bySeverity,omitempty"`
	// ByDomain counts findings per domain (the finding "type").
	ByDomain map[Domain]int `json:"byDomain,omitempty"`
	// ByStatus counts findings per lifecycle status.
	ByStatus map[Status]int `json:"byStatus,omitempty"`
	// ByRepo counts findings per top-level location (Location.Repo).
	ByRepo map[string]int `json:"byRepo,omitempty"`
}

Summary is a computed, JSON-serializable analytics rollup for a FindingSet. It answers the common questions at a glance — how many findings, and how they break down by severity, domain (type), status, and top-level location (repo) — without the caller assembling each map. For dimensions not covered here, use FindingSet.CountBy.

Directories

Path Synopsis
Package a11y defines semantic types for accessibility findings tied to WCAG success criteria and projects them into the domain-neutral findingspec.Finding.
Package a11y defines semantic types for accessibility findings tied to WCAG success criteria and projects them into the domain-neutral findingspec.Finding.
adapters
axe
Package axe ingests axe-core accessibility results (the JSON produced by axe.run(), as emitted by @axe-core/cli, Playwright/axe, CI reporters, etc.) and normalizes them into findingspec Findings (a11y domain, type "wcag").
Package axe ingests axe-core accessibility results (the JSON produced by axe.run(), as emitted by @axe-core/cli, Playwright/axe, CI reporters, etc.) and normalizes them into findingspec Findings (a11y domain, type "wcag").
gitleaks
Package gitleaks ingests gitleaks JSON report output and normalizes it into findingspec Findings (security domain, type "secret").
Package gitleaks ingests gitleaks JSON report output and normalizes it into findingspec Findings (security domain, type "secret").
govulncheck
Package govulncheck ingests `govulncheck -json` output and normalizes it into findingspec Findings (security domain, type "sca").
Package govulncheck ingests `govulncheck -json` output and normalizes it into findingspec Findings (security domain, type "sca").
Package i18n defines semantic types for internationalization and localization findings and projects them into the domain-neutral findingspec.Finding.
Package i18n defines semantic types for internationalization and localization findings and projects them into the domain-neutral findingspec.Finding.
Package qe defines semantic types for quality-engineering findings — failed end-to-end tests and broken user journeys — and projects them into the domain-neutral findingspec.Finding.
Package qe defines semantic types for quality-engineering findings — failed end-to-end tests and broken user journeys — and projects them into the domain-neutral findingspec.Finding.
Package security defines semantic types for security findings — vulnerabilities in code, dependencies, and configuration — and projects them into the domain-neutral findingspec.Finding.
Package security defines semantic types for security findings — vulnerabilities in code, dependencies, and configuration — and projects them into the domain-neutral findingspec.Finding.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL