diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..523304d --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,131 @@ +# golangci-lint v2 configuration for a security-critical IAM system. +# Principle: fail loudly. Security and correctness issues are errors, not warnings. + +version: "2" + +run: + timeout: 5m + # Include test files so security rules apply to test helpers too. + tests: true + +linters: + default: none + enable: + # --- Correctness --- + # Unhandled errors are silent failures; in auth code they become vulnerabilities. + - errcheck + # go vet: catches printf-verb mismatches, unreachable code, suspicious constructs. + - govet + # Detects assignments whose result is never used; dead writes hide logic bugs. + - ineffassign + # Detects variables and functions that are never used. + - unused + + # --- Error handling --- + # Enforces proper error wrapping (errors.Is/As instead of == comparisons) and + # prevents accidental discard of wrapped sentinel errors. + - errorlint + + # --- Security --- + # Primary security scanner: hardcoded secrets, weak RNG, insecure crypto + # (MD5/SHA1/DES/RC4), SQL injection, insecure TLS, file permission issues, etc. + - gosec + # Deep static analysis: deprecated APIs, incorrect mutex use, unreachable code, + # incorrect string conversions, simplification suggestions, and hundreds of other checks. + # (gosimple was merged into staticcheck in golangci-lint v2) + - staticcheck + + # --- Style / conventions (per CLAUDE.md) --- + # Enforces Go naming conventions and selected style rules. + - revive + + settings: + errcheck: + # Do NOT flag blank-identifier assignments: `_ = rows.Close()` in defers, + # `_ = tx.Rollback()` after errors, and `_ = fs.Parse(args)` with ExitOnError + # are all legitimate patterns where the error is genuinely unrecoverable or + # irrelevant. The default errcheck (without check-blank) still catches + # unchecked returns that have no assignment at all. + check-blank: false + # Flag discarded ok-value in type assertions: `c, _ := x.(*T)` — the ok + # value should be checked so a failed assertion is not silently treated as nil. + check-type-assertions: true + + govet: + # Enable all analyzers except shadow. The shadow analyzer flags the idiomatic + # `if err := f(); err != nil { ... }` pattern as shadowing an outer `err`, + # which is ubiquitous in Go and does not pose a security risk in this codebase. + enable-all: true + disable: + - shadow + + gosec: + # Treat all gosec findings as errors, not warnings. + severity: medium + confidence: medium + excludes: + # G104 (errors unhandled) overlaps with errcheck; let errcheck own this. + - G104 + + errorlint: + errorf: true + asserts: true + comparison: true + + revive: + rules: + # error-return and unexported-return are correctness/API-safety rules. + - name: error-return + severity: error + - name: unexported-return + severity: error + # Style rules. + - name: error-strings + severity: warning + - name: if-return + severity: warning + - name: increment-decrement + severity: warning + - name: var-naming + severity: warning + - name: range + severity: warning + - name: time-naming + severity: warning + - name: indent-error-flow + severity: warning + - name: early-return + severity: warning + # exported and package-comments are omitted: this is a personal project, + # not a public library; godoc completeness is not a CI requirement. + +formatters: + enable: + # Enforces gofmt formatting. Non-formatted code is a CI failure. + - gofmt + # Manages import grouping and formatting; catches stray debug imports. + - goimports + +issues: + # Do not cap the number of reported issues; in security code every finding matters. + max-issues-per-linter: 0 + max-same-issues: 0 + + exclusions: + paths: + - vendor + rules: + # In test files, allow hardcoded test credentials (gosec G101) since they are + # intentional fixtures, not production secrets. + - path: "_test\\.go" + linters: + - gosec + text: "G101" + + # G101: Event-type string constants (e.g. "pgcred_updated") and environment + # variable name constants (e.g. "MCIAS_MASTER_PASSPHRASE") are not credentials. + # gosec pattern-matches on substrings like "cred" and "pass", causing false positives. + - linters: + - gosec + text: "G101" + source: "(Event|PassphraseEnv)"