feat: add golangci linter rules

This commit is contained in:
drev74
2025-10-11 22:36:20 +03:00
parent 8d5af6be5f
commit 27abae69ea
16 changed files with 171 additions and 44 deletions

122
.golangci.yml Normal file
View File

@@ -0,0 +1,122 @@
version: "2"
run:
issues-exit-code: 1
tests: false
build-tags:
- nobadger
- nomysql
- nopgx
output:
formats:
text:
path: stdout
print-linter-name: true
print-issued-lines: true
linters:
default: none
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- decorder
- dogsled
- dupl
- dupword
- durationcheck
- errcheck
- errname
- exhaustive
- gosec
- govet
- importas
- ineffassign
- misspell
- prealloc
- promlinter
- sloglint
- sqlclosecheck
- staticcheck
- testableexamples
- testifylint
- tparallel
- unconvert
- unused
- wastedassign
- whitespace
- zerologlint
settings:
staticcheck:
checks: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022", "-QF1006", "-QF1008"] # default, and exclude 1 more undesired check
errcheck:
exclude-functions:
- fmt.*
- (go.uber.org/zap/zapcore.ObjectEncoder).AddObject
- (go.uber.org/zap/zapcore.ObjectEncoder).AddArray
exhaustive:
ignore-enum-types: reflect.Kind|svc.Cmd
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- gosec
text: G115 # TODO: Either we should fix the issues or nuke the linter if it's bad
- linters:
- gosec
text: G107 # we aren't calling unknown URL
- linters:
- gosec
text: G203 # as a web server that's expected to handle any template, this is totally in the hands of the user.
- linters:
- gosec
text: G204 # we're shelling out to known commands, not relying on user-defined input.
- linters:
- gosec
# the choice of weakrand is deliberate, hence the named import "weakrand"
path: modules/caddyhttp/reverseproxy/selectionpolicies.go
text: G404
- linters:
- gosec
path: modules/caddyhttp/reverseproxy/streaming.go
text: G404
- linters:
- dupl
path: modules/logging/filters.go
- linters:
- dupl
path: modules/caddyhttp/matchers.go
- linters:
- dupl
path: modules/caddyhttp/vars.go
- linters:
- errcheck
path: _test\.go
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gci
- gofmt
- gofumpt
- goimports
settings:
gci:
sections:
- standard # Standard section: captures all standard packages.
- default # Default section: contains all imports that could not be matched to another section type.
- prefix(github.com/caddyserver/caddy/v2/cmd) # ensure that this is always at the top and always has a line break.
- prefix(github.com/caddyserver/caddy) # Custom section: groups all imports with the specified Prefix.
custom-order: true
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@@ -25,16 +25,16 @@ import (
"strings"
"sync"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/fsnotify/fsnotify"
"github.com/oschwald/maxminddb-golang"
trie "github.com/phemmer/go-iptrie"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/fsnotify/fsnotify"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
// ==================== Constants and Globals ====================
@@ -120,7 +120,7 @@ func (m *Middleware) Provision(ctx caddy.Context) error {
fileCfg.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
fileEncoder := zapcore.NewJSONEncoder(fileCfg.EncoderConfig)
fileSync, err := os.OpenFile(m.LogFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
fileSync, err := os.OpenFile(m.LogFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
m.logger.Warn("Failed to open log file, logging only to console", zap.String("path", m.LogFilePath), zap.Error(err))
m.logger = zap.New(zapcore.NewCore(consoleEncoder, consoleSync, logLevel))

View File

@@ -2,15 +2,14 @@ package caddywaf
import (
"context"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/caddyserver/caddy/v2"
"github.com/stretchr/testify/assert"
"github.com/caddyserver/caddy/v2"
)
func TestMiddleware_Provision(t *testing.T) {

View File

@@ -7,8 +7,9 @@ import (
"strings"
"time"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"go.uber.org/zap"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
)
// ConfigLoader structure to encapsulate loading and parsing logic

View File

@@ -2,15 +2,14 @@
package caddywaf
import (
"path/filepath"
"os"
"path/filepath"
"testing"
"time"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"go.uber.org/zap"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
)
func TestNewConfigLoader(t *testing.T) {

14
doc.go
View File

@@ -15,14 +15,16 @@
// - Dynamic configuration reloading
//
// Installation:
// xcaddy build --with github.com/fabriziosalmi/caddy-waf
//
// xcaddy build --with github.com/fabriziosalmi/caddy-waf
//
// Basic usage in Caddyfile:
// waf {
// rule_file rules.json
// ip_blacklist_file blacklist.txt
// metrics_endpoint /waf_metrics
// }
//
// waf {
// rule_file rules.json
// ip_blacklist_file blacklist.txt
// metrics_endpoint /waf_metrics
// }
//
// For complete documentation, see: https://github.com/fabriziosalmi/caddy-waf
package caddywaf

View File

@@ -101,7 +101,6 @@ func (gh *GeoIPHandler) GetCountryCode(remoteAddr string, geoIP *maxminddb.Reade
}
func (gh *GeoIPHandler) isCountryInListWithCache(ip string, parsedIP net.IP, countryList []string, geoIP *maxminddb.Reader) (bool, error) {
// Check cache first
if gh.geoIPCache != nil {
gh.geoIPCacheMutex.RLock()
@@ -127,7 +126,6 @@ func (gh *GeoIPHandler) isCountryInListWithCache(ip string, parsedIP net.IP, cou
}
func (gh *GeoIPHandler) getCountryCodeWithCache(ip string, parsedIP net.IP, geoIP *maxminddb.Reader) string {
// Check cache first for GetCountryCode as well for consistency and potential perf gain
if gh.geoIPCache != nil {
gh.geoIPCacheMutex.RLock()

View File

@@ -5,14 +5,17 @@ import (
"net/http"
"strings"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/google/uuid"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
type ContextKeyLogId string
type ContextKeyRule string
type (
ContextKeyLogId string
ContextKeyRule string
)
// ServeHTTP implements caddyhttp.Handler.
// handler.go
@@ -154,7 +157,6 @@ func (m *Middleware) handleResponseBodyPhase(recorder *responseRecorder, r *http
if m.processRuleMatch(recorder, r, &rule, body, state) {
return
}
}
}
}
@@ -274,7 +276,7 @@ func (m *Middleware) handlePhase(w http.ResponseWriter, r *http.Request, phase i
}
if phase == 1 {
m.logger.Debug("Checking for IP blacklisting", zap.String("remote_addr", r.RemoteAddr)) //Added log for checking before to isIPBlacklisted call
m.logger.Debug("Checking for IP blacklisting", zap.String("remote_addr", r.RemoteAddr)) // Added log for checking before to isIPBlacklisted call
xForwardedFor := r.Header.Get("X-Forwarded-For")
if xForwardedFor != "" {
ips := strings.Split(xForwardedFor, ",")
@@ -293,7 +295,6 @@ func (m *Middleware) handlePhase(w http.ResponseWriter, r *http.Request, phase i
}
} else {
m.logger.Debug("X-Forwarded-For header present but empty or invalid")
}
} else {

View File

@@ -12,10 +12,11 @@ import (
"testing"
"time"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
trie "github.com/phemmer/go-iptrie"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
func TestBlockedRequestPhase1_DNSBlacklist(t *testing.T) {
@@ -496,6 +497,7 @@ func TestBlockedRequestPhase1_HeaderRegex_EmptyHeader(t *testing.T) {
assert.Equal(t, http.StatusOK, w.Code, "Expected status code 200")
assert.Empty(t, w.Body.String(), "Response body should be empty")
}
func TestBlockedRequestPhase1_HeaderRegex_MissingHeader(t *testing.T) {
logger := zap.NewNop()
middleware := &Middleware{
@@ -546,7 +548,6 @@ func TestBlockedRequestPhase1_HeaderRegex_MissingHeader(t *testing.T) {
assert.False(t, state.Blocked, "Request should not be blocked because header is missing")
assert.Equal(t, http.StatusOK, w.Code, "Expected status code 200")
assert.Empty(t, w.Body.String(), "Response body should be empty")
}
func TestBlockedRequestPhase1_HeaderRegex_ComplexPattern(t *testing.T) {

View File

@@ -105,7 +105,6 @@ func (m *Middleware) logRequest(level zapcore.Level, msg string, r *http.Request
)
m.logger.Log(level, msg, allFields...)
}
}
// redactSensitiveFields redacts sensitive information in the log fields.

View File

@@ -11,11 +11,12 @@ import (
"testing"
"time"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
trie "github.com/phemmer/go-iptrie"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
)
func TestExtractValue(t *testing.T) {

View File

@@ -62,7 +62,6 @@ func NewResponseRecorder(w http.ResponseWriter) *responseRecorder {
func (r *responseRecorder) WriteHeader(statusCode int) {
r.statusCode = statusCode
r.ResponseWriter.WriteHeader(statusCode)
}
// Header returns the response headers.

View File

@@ -187,7 +187,7 @@ func TestLoadRules(t *testing.T) {
"action": "block"
}
]`
os.WriteFile(validRuleFile, []byte(validRules), 0644)
os.WriteFile(validRuleFile, []byte(validRules), 0o644)
invalidRuleFile := filepath.Join(tmpDir, "invalid_rules.json")
invalidRules := `[
@@ -199,7 +199,7 @@ func TestLoadRules(t *testing.T) {
"score": -1
}
]`
os.WriteFile(invalidRuleFile, []byte(invalidRules), 0644)
os.WriteFile(invalidRuleFile, []byte(invalidRules), 0o644)
tests := []struct {
name string

5
tor.go
View File

@@ -9,8 +9,9 @@ import (
"strings"
"time"
"github.com/caddyserver/caddy/v2"
"go.uber.org/zap"
"github.com/caddyserver/caddy/v2"
)
var torExitNodeURL = "https://check.torproject.org/torbulkexitlist"
@@ -134,7 +135,7 @@ func (t *TorConfig) readExistingBlacklist() ([]string, error) {
// writeBlacklist writes the updated IP blacklist to the file.
func (t *TorConfig) writeBlacklist(ips []string) error {
data := strings.Join(ips, "\n")
err := os.WriteFile(t.TORIPBlacklistFile, []byte(data), 0644)
err := os.WriteFile(t.TORIPBlacklistFile, []byte(data), 0o644)
if err != nil {
return fmt.Errorf("failed to write IP blacklist file %s: %w", t.TORIPBlacklistFile, err) // Improved error message with filename
}

View File

@@ -6,9 +6,10 @@ import (
"os"
"testing"
"github.com/caddyserver/caddy/v2"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"github.com/caddyserver/caddy/v2"
)
func TestTorConfig_Provision(t *testing.T) {

View File

@@ -5,13 +5,14 @@ import (
"sync"
"time"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/oschwald/maxminddb-golang"
trie "github.com/phemmer/go-iptrie"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
// Package caddywaf is a Caddy module providing web application firewall functionality.
@@ -27,8 +28,10 @@ var (
)
// Define custom types for rule hits
type RuleID string
type HitCount int
type (
RuleID string
HitCount int
)
// RuleCache caches compiled regex patterns for rules.
type RuleCache struct {