Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

71 lines
2.5 KiB
Go

package review
import (
"crypto/sha256"
"encoding/hex"
"path/filepath"
"strings"
)
// IdentityKey computes a stable, line-drift-invariant identity for a finding.
//
// The key is a SHA-256 over the tuple
//
// rule + category + normalized(file) + symbol + content-anchor(source line)
//
// deliberately EXCLUDING the line number: the same flagged code dismissed at
// line 10 keeps the same key after the file shifts it to line 40, so a
// suppression survives unrelated edits above it. Two findings differ in key
// whenever any of rule / category / symbol / normalized-file / trimmed source
// text differs.
//
// When the generator did not pass the flagged line's text (Finding.SourceLine
// empty) the content anchor falls back to the empty string, so the key reduces
// to (rule, category, file, symbol) — coarser, and liable to over-suppress
// sibling findings on the same symbol, but still stable.
func IdentityKey(f Finding) string {
h := sha256.New()
// A newline separator that cannot appear inside any single component keeps
// the concatenation unambiguous (a trailing/leading boundary can't migrate
// content from one field into another).
write := func(parts ...string) {
for _, p := range parts {
h.Write([]byte(p))
h.Write([]byte{'\n'})
}
}
write(
strings.TrimSpace(f.Rule),
normalizeCategory(f.Category),
normalizeIdentityPath(f.File),
strings.TrimSpace(f.SymbolID),
lineContentAnchor(f.File, f.Line, f.SourceLine),
)
return hex.EncodeToString(h.Sum(nil))
}
// lineContentAnchor derives the drift-invariant content component of the
// identity key from the flagged line's source text. It folds away leading /
// trailing whitespace and collapses interior runs of whitespace to a single
// space, so a reflow / re-indent of the same statement keeps the same anchor.
// The file path and line number are accepted for symmetry with the spec'd
// signature and to let a future anchor strategy fold them in; the line number
// is intentionally NOT hashed here — that is what makes the key line-stable.
// An empty source line yields an empty anchor (the coarse fallback).
func lineContentAnchor(file string, line int, src string) string {
_ = file
_ = line
return strings.Join(strings.Fields(src), " ")
}
// normalizeIdentityPath canonicalises a file path to forward-slash form and
// strips a leading "./" so two spellings of the same path produce one key.
func normalizeIdentityPath(p string) string {
p = strings.TrimSpace(p)
if p == "" {
return ""
}
p = filepath.ToSlash(filepath.Clean(p))
return strings.TrimPrefix(p, "./")
}