Skip to content

hardcodedfilepath precision: hasFormatVerb misses flag/width/precision verbs (%05d, %-10s, %8.2f) and 9 verb letters (%x %t %c % [Content truncated due to length] #38788

@github-actions

Description

@github-actions

Summary

The new hardcodedfilepath linter (#38742) is intended to skip format-template strings — ADR docs/adr/38742-...md states it "skips const-declaration sites and format-template strings." The implementation that enforces this, hasFormatVerb, is an incomplete substring check, so several real format templates that look path-like slip through and are falsely reported as hard-coded paths to extract.

Evidence — pkg/linters/hardcodedfilepath/hardcodedfilepath.go:88-93

func hasFormatVerb(val string) bool {
	return strings.ContainsAny(val, "%") &&
		(strings.Contains(val, "%s") || strings.Contains(val, "%d") ||
			strings.Contains(val, "%v") || strings.Contains(val, "%q") ||
			strings.Contains(val, "%w") || strings.Contains(val, "%f"))
}

Two gaps:

  1. Flag/width/precision verbs evade detection. The check looks for the literal substrings %s/%d/etc., so a verb with a flag, width, or precision does not match:
    • "/tmp/run-%05d/out"strings.Contains("...%05d...", "%d") is false ⇒ not treated as a template ⇒ flagged (prefix /tmp/, len ≥ 8).
    • "/var/log/%-10s.log", "/home/%8.2f", "/etc/%+d.conf" — same failure.
  2. Nine verb letters are omitted entirely: %x %X %t %c %b %o %e %E %g %G %p %U. e.g. "/tmp/cache-%x/blob" (hash hex), "/var/run/%p.sock" are templates but get flagged.

Both gaps cause false positives: a fmt.Sprintf/fmt.Fprintf template argument is reported as a literal path that should become a named constant, which is nonsensical (you cannot const a template that contains runtime values).

Impact

  • The linter is actively run by automation (e.g. the lint-monster "Extract hard-coded file paths to constants (120 instances)" issues), so FP noise lands in real triage queues.
  • Directly contradicts the ADR's stated design that format-template strings are skipped.
  • No current production violation matches the regex today (latent), so this is a safe, pre-emptive precision fix before the linter is enforced in CI.

Recommendation

Replace the substring heuristic with a verb-aware scan that recognizes the standard fmt verb grammar %[flags][width][.precision]<verb-letter> while ignoring escaped %%. A compiled package-level regexp is the smallest change:

// matches a real fmt verb: %, optional flags/width/precision, then a verb letter.
var fmtVerbRe = regexp.MustCompile(`%[-+# 0]*[0-9]*(?:\.[0-9]+)?[vTtbcdoqxXUeEfFgGsp]`)

func hasFormatVerb(val string) bool {
	// strip escaped percent signs so "100%%done" is not seen as a verb
	return fmtVerbRe.MatchString(strings.ReplaceAll(val, "%%", ""))
}

(Compile the regexp once at package level so it does not trip the sibling regexpcompileinfunction linter.) Add testdata cases: /tmp/run-%05d/out, /var/log/%-10s.log, /tmp/cache-%x/blob, and a true positive like /tmp/gh-aw/agent plus a literal-percent path /tmp/100%%/x (should still flag).

Validation checklist

  • New testdata: width/flag verbs and %x/%t/%c templates are NOT reported.
  • Plain path literals (no verbs) still reported.
  • %%-only literals still reported (not treated as a template).
  • go test ./pkg/linters/hardcodedfilepath/... passes.

Effort: Small — single function + testdata.

References

Generated by 🤖 Sergo - Serena Go Expert · 290.8 AIC · ⌖ 12.6 AIC · ⊞ 6.3K ·

  • expires on Jun 18, 2026, 9:28 PM UTC-08:00

Metadata

Metadata

Labels

cookieIssue Monster Loves Cookies!sergo

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions