fix

package
v0.5.8 Latest Latest
Warning

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

Go to latest
Published: Jun 5, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Registry = map[string]Patcher{
	"client-side-llm-key": ClientSideLlmKeyPatcher,
	"dependency-cve":      DependencyCvePatcher,
	"insecure-cors":       InsecureCorsPatcher,
	"insecure-random":     InsecureRandomPatcher,
	"open-redirect":       OpenRedirectPatcher,
	"unbounded-stream":    UnboundedStreamPatcher,
	"weak-crypto":         WeakCryptoPatcher,
	"xss":                 XssPatcher,
}

Registry maps a security category to its deterministic patcher. A category absent from this map has no auto-fix locally — surface as explanation-only, never invent a patch. New patchers register here.

Mirrors workers/src/fix/patchers/index.ts (`patchers`).

Functions

This section is empty.

Types

type ApplyOutcome

type ApplyOutcome struct {
	Detected      Detected
	OK            bool
	Description   string
	Reason        string
	ManualCommand string
}

ApplyOutcome is the per-Detected result of an engine run. One entry per detected pattern, whether we ended up rewriting the file or not.

type Detected

type Detected struct {
	Category    string
	FilePath    string
	LineStart   int
	LineEnd     int
	MatchedSpan string
}

Detected is one fixable-pattern hit. Mirrors the relevant subset of scan.Finding so the engine can drive patchers without depending on the SAST package.

func Detect

func Detect(workdir string, logf func(string, ...any)) ([]Detected, error)

Detect walks workdir, applies each detectorRule to every eligible line, and returns the union of hits. Errors on individual files are logged via logf but never abort the walk — partial coverage is more useful than total failure.

type EngineOptions

type EngineOptions struct {
	// Workdir is the directory the detector walks and the apply path
	// writes into. Must already be a directory; the engine doesn't
	// create it.
	Workdir string
	// DryRun = true: detect + run patchers but do NOT write to disk and
	// do NOT create a backup directory. Used for `--apply` previewing.
	DryRun bool
	// Logf is the optional progress logger (printed to stderr by the CLI).
	Logf func(format string, args ...any)
	// Now is injected for testability; nil = time.Now().
	Now func() time.Time
}

EngineOptions configures one engine run.

type EngineResult

type EngineResult struct {
	BackupDir string
	Outcomes  []ApplyOutcome
}

EngineResult is what `fix --local-only` reports back to the CLI for printing. Holds the backup path + the per-pattern outcomes.

func Run

func Run(opts EngineOptions) (EngineResult, error)

Run drives the full pipeline: detect → group by file → for each file, in stable order, run every patcher whose detection landed there, accumulating the new source — then write once at the end.

Backup writes ALWAYS precede source writes, so an abort partway through still leaves a usable rollback artifact.

func (EngineResult) Stats

func (r EngineResult) Stats() (applied, declined, manual int, filesTouched int)

Stats summarises the result for the CLI's footer line.

type MatchSite

type MatchSite struct {
	LineStart   int
	LineEnd     int
	MatchedSpan string
}

MatchSite is the location a detector emitted — same 1-based line numbers and matched-text shape the regex passes already use.

type Patcher

type Patcher func(PatcherInput) PatcherResult

Patcher is the contract the registry maps category → impl against.

func Lookup

func Lookup(category string) Patcher

Lookup returns the patcher for a category, or nil if none is registered. nil-return is the explicit "no auto-fix locally" signal — callers must check it before invoking.

type PatcherInput

type PatcherInput struct {
	// Source is the full content of the file under patch.
	Source string
	// FilePath is the workdir-relative path. Used only for messages —
	// the patcher does not perform any filesystem operation with it.
	FilePath string
	// Match is the detector-emitted location to operate on.
	Match MatchSite
	// Context carries detector-specific fields a small subset of
	// patchers need (e.g. dependency-cve reads packageName +
	// fixedVersion). Most patchers ignore it. Mirrors the hosted
	// PatcherInput.context contract.
	Context map[string]string
}

PatcherInput is everything a patcher needs to do its work. No embedded *os.File, no path resolution — the engine reads the file once and hands it in.

type PatcherResult

type PatcherResult struct {
	OK bool
	// Patched is the full file content after the patch — only set when OK.
	// The engine computes the diff for display; patchers don't emit unified
	// diffs themselves.
	Patched string
	// Description is the dev-readable summary: what was changed, what
	// follow-up the dev needs to do. Surfaced in the CLI summary after
	// `getdebug fix --local-only --apply`.
	Description string
	// Reason is the explain-why-not when OK is false. Always present in
	// the decline case so the CLI can render an honest skip note.
	Reason string
	// ManualCommand is an optional copy-pastable shell command for the
	// "structurally unfixable but the dev can fix it" case (e.g.
	// dependency-cve hitting a JS lockfile whose integrity hash the
	// patcher can't recompute). When set on a decline (OK=false), the
	// CLI prints it as a suggested follow-up instead of just dropping
	// the finding silently. Mirrors workers PatcherResult.manualCommand.
	ManualCommand string
}

PatcherResult is the sum type a patcher returns. Either it produced a new file content + a dev-readable explanation (OK = true), or it declined to patch with a reason the engine surfaces honestly (OK = false). A patcher never panics on bad input — drift, oversized files, and structurally unfixable shapes are all decline-with-reason cases.

func ClientSideLlmKeyPatcher

func ClientSideLlmKeyPatcher(in PatcherInput) PatcherResult

ClientSideLlmKeyPatcher rewrites a single public-prefixed env var reference and prepends a three-line explainer comment. The matched span must be present at the matched line — drift declines cleanly.

func DependencyCvePatcher

func DependencyCvePatcher(in PatcherInput) PatcherResult

DependencyCvePatcher rewrites a single `==`-pin in requirements.txt to the advisory's fixed version. Lockfile and go.mod hits return a manual-command result for the CLI to surface.

func InsecureCorsPatcher

func InsecureCorsPatcher(in PatcherInput) PatcherResult

InsecureCorsPatcher rewrites a single wildcard Access-Control-Allow- Origin pair on the matched line to an env lookup that fails closed.

func InsecureRandomPatcher

func InsecureRandomPatcher(in PatcherInput) PatcherResult

InsecureRandomPatcher rewrites a single Math.random() on the matched line. Returns ok=true when exactly one rewrite was made; declines on zero (drift / alias / non-literal) or multiple (ambiguous).

func OpenRedirectPatcher

func OpenRedirectPatcher(in PatcherInput) PatcherResult

OpenRedirectPatcher wraps the redirect target in a same-origin path guard. Reads `arg` four times — safe because the regex rules out function calls (no side effects in identifier-chain expressions).

func UnboundedStreamPatcher

func UnboundedStreamPatcher(in PatcherInput) PatcherResult

UnboundedStreamPatcher adds an AbortController to a streaming LLM call. Refuses to patch a `decoder.decode({ stream: true })` line — that's the Web Streams API and a known false-positive shape on the detector side.

func WeakCryptoPatcher

func WeakCryptoPatcher(in PatcherInput) PatcherResult

WeakCryptoPatcher rewrites a single MD5/SHA-1 hash call on the matched line. Returns ok=true when exactly one rewrite was made; declines on zero (file drift) or multiple (ambiguous).

func XssPatcher

func XssPatcher(in PatcherInput) PatcherResult

XssPatcher rewrites a single `.innerHTML =` assignment on the matched line to `.textContent =`. Declines on zero matches (drift / wrong shape) or multiple matches (ambiguous).

Jump to

Keyboard shortcuts

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