go-defensive — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited go-defensive (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Hardening Go code is not paranoia — it is the discipline of making your boundaries honest. Copy what crosses them, clean up what you opened, model time and randomness honestly, and never let a panic escape a package.
f, err := os.Open(...); defer f.Close().var _ I = (*T)(nil).time.Now() directly in production code.When you touch an exported function or method, walk this list in order:
| # | Check |
|---|---|
| 1 | Return errors, don't panic across boundaries |
| 2 | Copy slices/maps you'll retain |
| 3 | Copy slices/maps you'll return if internal state aliases them |
| 4 | defer Close / Unlock / cancel right after the acquire |
| 5 | Compile-time interface satisfaction check |
| 6 | time.Time / time.Duration types, injected clock |
| 7 | Enum zero = invalid (iota + 1) |
| 8 | crypto/rand for any secret material |
// Receiving: copy a slice we'll retain
func (d *Driver) SetTrips(trips []Trip) {
d.trips = make([]Trip, len(trips))
copy(d.trips, trips)
}
// Returning: copy a map so callers can't mutate our state
func (s *Stats) Snapshot() map[string]int {
out := make(map[string]int, len(s.counters))
for k, v := range s.counters {
out[k] = v
}
return out
}Read references/boundary-copying.md when deciding which boundaries actually need copies (and when copying is wasted work).
defer evaluates arguments at the defer statement and runs the call when the surrounding function returns (LIFO order):
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close()Place defer immediately after the acquire — the proximity makes pair-correctness reviewable at a glance.
For locks:
mu.Lock()
defer mu.Unlock()Beware of defer inside loops — accumulated defers run only when the function returns, not when the iteration ends.
var _ http.Handler = (*Handler)(nil)If (*Handler) ever stops satisfying http.Handler, the build fails. The line costs nothing at runtime and gives you a free contract.
// Bad — what unit is timeout?
type Config struct {
Timeout int
}
// Good
type Config struct {
Timeout time.Duration
}For wall-clock work, inject the clock so tests can pin time:
type Signer struct {
now func() time.Time
}
func NewSigner() *Signer {
return &Signer{now: time.Now}
}
// In tests:
s := &Signer{now: func() time.Time { return fixedTime }}Read references/time-and-enums.md for monotonic time, time zones, struct tags, and embedding tradeoffs.
import "crypto/rand"
// Go 1.24+
func APIKey() string { return rand.Text() }math/rand and math/rand/v2 are predictable from a seed — never use them for keys, tokens, nonces, or any secret material.
Must* helpers panic on error. They are appropriate only at program initialization, where failure means the program cannot start:
var (
validID = regexp.MustCompile(`^[a-z][a-z0-9-]{0,62}$`)
tmpl = template.Must(template.ParseFiles("index.html"))
)Don't write MustFoo for runtime call sites — it shifts an error condition into a crash.
Read references/must-and-panic.md for writing customMust*, recovering at goroutine boundaries, and distinguishingpanicfromlog.Fatal.
// Bad — testing requires save/restore dance
var DB *sql.DB
// Good — pass the dependency
type Service struct {
db *sql.DB
}Constants and once-initialized lookup tables are fine. Mutable package-level vars are a code smell.
| Anti-pattern | Why it hurts | Do this instead |
|---|---|---|
| Storing the caller's slice without copying | Mutation aliasing | make + copy |
| Returning the internal map directly | External mutation of state | Return a snapshot |
time.Now() in business logic | Hostile to tests | Inject now func() time.Time |
var Timeout = 5 read as seconds elsewhere | Ambiguous unit | time.Duration |
math/rand for keys | Predictable from seed | crypto/rand |
panic to signal a domain error | Crashes the caller | Return an error |
defer inside a tight loop | Defers stack until function return | Wrap loop body in a function |
Open/Lock has a defer Close/Unlock next to ittime.Duration, timestamps are time.Time; clock is injectedmath/randpanic across library boundariesMust* helpers, recover at boundaries, panic vs log.Fatal~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.