mirror of
https://github.com/fabriziosalmi/caddy-waf.git
synced 2025-12-23 22:27:46 -05:00
feat: add golangci linter rules
This commit is contained in:
122
.golangci.yml
Normal file
122
.golangci.yml
Normal 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$
|
||||
12
caddywaf.go
12
caddywaf.go
@@ -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))
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
2
doc.go
2
doc.go
@@ -15,9 +15,11 @@
|
||||
// - Dynamic configuration reloading
|
||||
//
|
||||
// Installation:
|
||||
//
|
||||
// xcaddy build --with github.com/fabriziosalmi/caddy-waf
|
||||
//
|
||||
// Basic usage in Caddyfile:
|
||||
//
|
||||
// waf {
|
||||
// rule_file rules.json
|
||||
// ip_blacklist_file blacklist.txt
|
||||
|
||||
2
geoip.go
2
geoip.go
@@ -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()
|
||||
|
||||
13
handler.go
13
handler.go
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
5
tor.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
13
types.go
13
types.go
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user