lintfix

package
v0.74.7 Latest Latest
Warning

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

Go to latest
Published: Aug 10, 2026 License: MIT Imports: 3 Imported by: 0

README

lintfix

Structured lint remediation database for Go projects using golangci-lint.

Overview

The lintfix package provides:

  • 📋 Remediation database - Embedded JSON database mapping lint rules to fixes
  • 🔧 Helper references - Links to mogo helper functions for code fixes
  • 📝 Nolint generators - Properly formatted nolint comments with documented reasons
  • 📚 Documentation - Version-specific caveats and best practices

Quick Start

import (
    "github.com/grokify/mogo/lintfix"
    "github.com/grokify/mogo/lintfix/gosec"
)

// Query the remediation database
db := lintfix.MustLoadRemediations()
fix := db.GetGosec("G120")
fmt.Println(fix.Remediation.Summary)
// "Use http.MaxBytesReader inline before parsing form data"

// Generate nolint comments
comment := gosec.NolintG117(gosec.CommonReasons.OAuthTokenResponse)
// "//nolint:gosec // G117: OAuth token response per RFC 6749"

Remediation Types

Type Description Example
code Add/modify code with helper functions G120: Use http.MaxBytesReader
nolint Add nolint annotation with reason G117: OAuth token response
refactor Broader code changes needed G101: Move secrets to env vars

Supported Linters

  • gosec - Security-focused rules (G101, G112, G115, G117, G118, G120, G122, G124, G401, G501, G601, G703, G704, G705, G706, G710)
  • staticcheck - Static analysis (SA1019, SA4006, QF1012)
  • errcheck - Error handling
  • govet - Inline remediation notes
  • dupl - Duplicate code detection

G703: Path Traversal

G703 warns about file paths constructed from user input. The fix depends on where your code lives:

In cmd/ (CLI entry points) - User explicitly provides the path, use nolint:

// User provides path via CLI flag - they own the risk
cleanPath := filepath.Clean(userPath)
if err := os.WriteFile(cleanPath, data, 0600); err != nil { //nolint:gosec // G703: Path from CLI flag
    return err
}

In library code - Use secure functions that reject .. sequences:

import "github.com/grokify/mogo/os/osutil"

// Library code - reject paths with traversal sequences
data, err := osutil.ReadFileSecure(path)
if err != nil {
    // Returns: "path contains '..' traversal sequence: ../etc/passwd"
    return err
}

if err := osutil.WriteFileSecure(path, data, 0600); err != nil {
    return err
}

Error returned: osutil.ErrPathTraversal is returned when a path contains ..:

// errors.Is check
if errors.Is(err, osutil.ErrPathTraversal) {
    log.Println("Invalid path:", err)
}

G101: Config Struct Fields Set From Parameters

G101 also fires on struct literals with credential-named fields (ClientSecret, APIKey, Password, Token, ...) even when the values come from caller-supplied parameters, not literals - a common shape for any OAuth/API-client config constructor:

func (s *OAuthService) ConfigureGoogle(clientID, clientSecret, redirectURL string) {
	s.RegisterProvider(&OAuthProvider{ //nolint:gosec // G101: ClientID/ClientSecret are set from caller-supplied parameters, not hardcoded literals
		Name:         "google",
		ClientID:     clientID,
		ClientSecret: clientSecret,
		RedirectURL:  redirectURL,
	})
}

There is no code fix here - the struct shape is the point, and gosec cannot see that the values are parameters rather than literals. nolint is the correct remediation.

G706: Log Injection

G706 warns when a value derived from client input (request Host, headers, path, etc.) is written directly to a log call, since an unescaped newline or control character lets an attacker forge fake log lines (CWE-117).

Verified fix - wrap with strconv.Quote, not just the %q verb:

import "strconv"

// Correct: strconv.Quote is a recognized sanitizer, clears the finding
log.Printf("Proxy error for %s: %v", strconv.Quote(r.Host), err)
// Does NOT clear the finding: gosec inspects the argument expression, not the
// format verb, so the raw tainted value is still flagged even with %q
log.Printf("Proxy error for %q: %v", r.Host, err) // still G706

Prefer this code fix over nolint in library code - it's a real fix (escapes injected control characters), not just linter appeasement, and it's what gosec.NolintG706 is documented to defer to.

G710: Open Redirect

G710 warns when an http.Redirect target is built by concatenating request-derived data (e.g. "https://" + r.Host + r.RequestURI), since an attacker who controls the Host header could make the server redirect anywhere (CWE-601).

Verified fix - build the target with net/url.URL, not string concatenation:

import "net/url"

// Correct: url.URL{}.String() is the recognized safe code shape, clears the finding
target := url.URL{Scheme: "https", Host: r.Host, Path: r.URL.Path, RawQuery: r.URL.RawQuery}
http.Redirect(w, r, target.String(), http.StatusMovedPermanently)

Important - this clears the linter, not the actual vulnerability. Verified empirically: gosec accepts the url.URL{} construction on its own, with no host validation at all. The real security fix is a separate step - validate the host against a known allowlist (e.g. the backends your proxy actually serves) before redirecting:

if !isKnownHost(r.Host) { // e.g. rp.findProxy(r.Host) != nil in a reverse proxy
    http.NotFound(w, r)
    return
}
target := url.URL{Scheme: "https", Host: r.Host, Path: r.URL.Path, RawQuery: r.URL.RawQuery}
http.Redirect(w, r, target.String(), http.StatusMovedPermanently)

Do both. Do not treat "gosec is clean" as evidence that a request-derived redirect target is actually safe.

Nolint Generators

The gosec subpackage provides type-safe nolint comment generators:

gosec.NolintG101(reason)  // Hardcoded credentials (false positive)
gosec.NolintG115(reason)  // Integer overflow (bounded value)
gosec.NolintG117(reason)  // Secret in JSON response
gosec.NolintG118(reason)  // context.Background in goroutine
gosec.NolintG122(reason)  // filepath.Walk TOCTOU race (cmd/ entry point only)
gosec.NolintG124(reason)  // Insecure cookie attributes (set dynamically/from config)
gosec.NolintG703(reason)  // Path traversal (CLI entry point only)
gosec.NolintG704(reason)  // SSRF (trusted URL)
gosec.NolintG705(reason)  // XSS (trusted content)
gosec.NolintG706(reason)  // Log injection (prefer the strconv.Quote code fix instead)
gosec.NolintG710(reason)  // Open redirect (prefer the url.URL{} code fix instead)
Common Reasons

Pre-written reason strings for common scenarios:

gosec.CommonReasons.OAuthTokenResponse        // G117
gosec.CommonReasons.ShutdownHandler           // G118
gosec.CommonReasons.PathFromCLIFlag           // G703
gosec.CommonReasons.HttptestServer            // G704
gosec.CommonReasons.BoundedByValidation       // G115
gosec.CommonReasons.ParameterNotLiteral       // G101 - config struct field set from a parameter
gosec.CommonReasons.TestControlledInputNoUntrustedSource // G706 - nolint fallback only; prefer strconv.Quote

Documentation

Adding New Rules

Edit remediations.json to add new rules:

{
  "linters": {
    "gosec": {
      "G999": {
        "name": "Rule name",
        "description": "What the rule detects",
        "severity": "high|medium|low",
        "category": "security|correctness|maintenance",
        "remediation": {
          "type": "code|nolint|refactor",
          "summary": "Brief fix description",
          "example": "Code example"
        }
      }
    }
  }
}

Documentation

Overview

Package lintfix provides a structured database of lint rule remediations for Go projects using golangci-lint.

This package serves as a "data overlay" that maps lint errors to:

  • Remediation strategies (code fix, nolint annotation, refactor)
  • Helper packages that provide actual fixes (within mogo)
  • Pre-written nolint comments with proper documentation
  • Example code and explanations

Usage

Load the remediation database and query for specific rules:

db := lintfix.MustLoadRemediations()
fix := db.GetGosec("G120")
fmt.Println(fix.Remediation.Summary)
// "Use http.MaxBytesReader before parsing form data"

Remediation Types

The database categorizes remediations into three types:

  • "code": Fix by adding/changing code (e.g., LimitRequestBody for G120)
  • "nolint": Fix by adding a nolint annotation with proper documentation
  • "refactor": Fix requires broader code changes (e.g., removing hardcoded secrets)

Nolint Generators

For rules that require nolint annotations, use the gosec subpackage:

comment := gosec.NolintG117(gosec.CommonReasons.OAuthTokenResponse)
// Returns: "//nolint:gosec // G117: OAuth token response per RFC 6749"

Helper Package References

Code-based remediations reference helper packages within mogo:

fix := db.GetGosec("G120")
fmt.Println(fix.Remediation.Package)
// "github.com/grokify/mogo/net/http/httputilmore"
fmt.Println(fix.Remediation.Function)
// "LimitRequestBody"

Supported Linters

Currently supported:

  • gosec: Security-focused linter
  • staticcheck: Go static analysis
  • errcheck: Error handling checks

Documentation

For detailed guides including version-specific caveats, see: https://github.com/grokify/mogo/tree/main/docs/lintfix

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Remediation

type Remediation struct {
	Type        string   `json:"type"` // "code", "nolint", "refactor"
	Summary     string   `json:"summary"`
	Pattern     string   `json:"pattern,omitempty"`
	Package     string   `json:"package,omitempty"`
	Function    string   `json:"function,omitempty"`
	Example     string   `json:"example,omitempty"`
	Explanation string   `json:"explanation,omitempty"`
	When        string   `json:"when,omitempty"`
	Avoid       []string `json:"avoid,omitempty"`
	Caveats     []string `json:"caveats,omitempty"`
}

Remediation contains the actual fix information.

type RemediationDB

type RemediationDB struct {
	Version     string                         `json:"version"`
	Description string                         `json:"description"`
	Linters     map[string]map[string]*RuleFix `json:"linters"`
}

RemediationDB is the top-level structure for the remediation database.

func LoadRemediations

func LoadRemediations() (*RemediationDB, error)

LoadRemediations loads and parses the embedded remediation database.

func MustLoadRemediations

func MustLoadRemediations() *RemediationDB

MustLoadRemediations loads the remediation database or panics.

func (*RemediationDB) Get

func (db *RemediationDB) Get(linter, code string) *RuleFix

Get retrieves a remediation by linter and rule code. Returns nil if not found.

func (*RemediationDB) GetGosec

func (db *RemediationDB) GetGosec(code string) *RuleFix

GetGosec is a convenience method for getting gosec remediations.

func (*RemediationDB) GetStaticcheck

func (db *RemediationDB) GetStaticcheck(code string) *RuleFix

GetStaticcheck is a convenience method for getting staticcheck remediations.

func (*RemediationDB) ListLinters

func (db *RemediationDB) ListLinters() []string

ListLinters returns all linters in the database.

func (*RemediationDB) ListRules

func (db *RemediationDB) ListRules(linter string) []string

ListRules returns all rule codes for a given linter.

type RuleFix

type RuleFix struct {
	Name        string       `json:"name"`
	Description string       `json:"description"`
	Severity    string       `json:"severity,omitempty"`
	Category    string       `json:"category,omitempty"`
	Remediation *Remediation `json:"remediation"`
	References  []string     `json:"references,omitempty"`
}

RuleFix contains remediation information for a specific lint rule.

func (*RuleFix) HasHelper

func (rf *RuleFix) HasHelper() bool

HasHelper returns true if this remediation has a helper function.

func (*RuleFix) String

func (rf *RuleFix) String() string

String returns a formatted description of the rule fix.

Directories

Path Synopsis
Package gosec provides helpers for generating nolint comments for gosec rules.
Package gosec provides helpers for generating nolint comments for gosec rules.

Jump to

Keyboard shortcuts

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