fix: repair Renovate config, convert Makefile to go run, update GitHub Actions (#120)

* fix: repair Renovate config and convert Makefile to go run (#117)

- Remove non-existent `github>renovatebot/presets:golang` preset that
  broke Renovate PR creation
- Replace deprecated `fileMatch` with `managerFilePatterns` in
  customManagers
- Rewrite regex to match new Makefile pattern (renovate comment above
  version variable assignment)
- Fix `matchFileNames` glob pattern (`*.mk` -> `**/*.mk`)
- Convert all tool invocations from `go install` + global binary to
  `go run tool@version` for reproducible builds
- Convert npm global tools to `npx --yes` invocations
- Remove `dev-deps` and `check-deps` targets (tools auto-download)
- Add mdformat pre-commit hook with GFM support and config
- Add `fmt-md` Makefile target for manual markdown formatting
- Update local golangci-lint pre-commit hook to use `go run`
- Apply golangci-lint v2.10.1 auto-fixes (fmt.Fprintf optimization)
- Add nolint:gosec annotations for legitimate exec.Command usage
- Exclude .serena/ from mdformat and megalinter
- Add markdown indent_size=unset in .editorconfig for CommonMark compat

* chore(deps): update GitHub Actions to latest versions

- anthropics/claude-code-action: v1.0.34 -> v1.0.64
- actions/setup-go: v6.2.0 -> v6.3.0
- actions/upload-artifact: v6.0.0 -> v7.0.0
- goreleaser/goreleaser-action: v6.4.0 -> v7.0.0
- docker/login-action: v3.6.0 -> v3.7.0
- ivuorinen/actions: v2026.01.21 -> v2026.02.24

* fix: address code review feedback

- Fix issue template YAML frontmatter (replace underscore separators
  with proper --- delimiters); exclude templates from mdformat
- Replace string(rune(n)) with strconv.Itoa(n) in test files to produce
  deterministic numeric directory names instead of Unicode characters
- Remove stale `make dev-deps` reference in README, replace with
  `make dev-setup`
- Extract ban/unban format strings into shared.MetricsFmtBanOperations
  and shared.MetricsFmtUnbanOperations constants
- Replace hardcoded coverage percentages in README with evergreen
  phrasing

* fix: address round 2 code review feedback for PR #120

- Fix corrupted path traversal example in docs/security.md
- Fix Renovate .mk regex to match nested paths (.*\.mk$)
- Update checkmake pre-commit hook to v0.3.2 to match Makefile
- Add sync.WaitGroup to unsynchronized goroutines in security tests
- Fix fmt-md target to use pre-commit run mdformat
- Pin markdownlint-cli2 to v0.21.0 in lint-md target
- Standardize //nolint:gosec to // #nosec annotations for gosec CLI

* fix(ci): install PyYAML dependency for PR lint workflow

The pr-lint workflow uses ivuorinen/actions/pr-lint which internally
calls validate-inputs running a Python script that imports yaml.
Python was set up but PyYAML was never installed, causing
ModuleNotFoundError at runtime.

* fix: address round 3 code review feedback for PR #120

- Wrap Windows-style path traversal example in backtick code span so
  backslashes render literally in docs/security.md
- Add Renovate-managed MARKDOWNLINT_CLI2_VERSION variable in Makefile
  to match the pattern used by all other tool versions
This commit is contained in:
2026-03-01 19:09:17 +02:00
committed by GitHub
parent 605f2b9580
commit 98b53d84b5
33 changed files with 281 additions and 345 deletions

View File

@@ -13,5 +13,8 @@ indent_width = 2
[{Makefile,go.mod,go.sum}]
indent_style = tab
[*.md]
indent_size = unset
[.github/renovate.json]
max_line_length = off

View File

@@ -4,7 +4,6 @@ about: Create a report to help us improve
title: ''
labels: bug
assignees: ivuorinen
---
**Describe the bug**
@@ -14,9 +13,9 @@ A clear and concise description of what the bug is.
Steps to reproduce the behavior:
1. Run command: `f2b [command]`
2. With arguments: `[arguments]`
3. Expected behavior: `[what should happen]`
4. Actual result: `[what actually happened]`
1. With arguments: `[arguments]`
1. Expected behavior: `[what should happen]`
1. Actual result: `[what actually happened]`
**Expected behavior**
A clear and concise description of what you expected to happen.

View File

@@ -4,7 +4,6 @@ about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ivuorinen
---
**Is your feature request related to a problem? Please describe.**

View File

@@ -1,12 +1,12 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>ivuorinen/renovate-config", "github>renovatebot/presets:golang", "schedule:weekly"],
"extends": ["github>ivuorinen/renovate-config", "schedule:weekly"],
"customManagers": [
{
"customType": "regex",
"fileMatch": ["^Makefile$", "\\.mk$"],
"managerFilePatterns": ["^Makefile$", ".*\\.mk$"],
"matchStrings": [
"@go install (?<depName>\\S+)@(?<currentValue>v?\\d+\\.\\d+\\.\\d+)[\\s\\S]*?renovate:\\s*datasource=(?<datasource>\\S+)\\s+depName=\\S+"
"#\\s*renovate:\\s*datasource=(?<datasource>\\S+)\\s+depName=(?<depName>\\S+)\\n[A-Z_]+\\s*:?=\\s*(?<currentValue>v?\\d+\\.\\d+\\.\\d+\\S*)"
],
"versioningTemplate": "semver"
}
@@ -15,7 +15,7 @@
"packageRules": [
{
"matchManagers": ["custom.regex"],
"matchFileNames": ["Makefile", "*.mk"],
"matchFileNames": ["Makefile", "**/*.mk"],
"groupName": "development tools",
"schedule": ["before 6am on monday"]
}

View File

@@ -53,7 +53,7 @@ jobs:
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@f64219702d7454cf29fe32a74104be6ed43dc637 # v1.0.34
uses: anthropics/claude-code-action@ba7fa4bcf054319261202aef93d71a89112a8d00 # v1.0.64
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}

View File

@@ -29,7 +29,7 @@ jobs:
node-version: 24.x
- name: Set up Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version-file: go.mod
cache: true

View File

@@ -35,7 +35,7 @@ jobs:
node-version: 24.x
- name: Set up Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version-file: go.mod
cache: true
@@ -46,6 +46,9 @@ jobs:
with:
python-version: "3.x"
- name: Install Python dependencies
run: pip install pyyaml
- name: golangci-lint
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
@@ -58,7 +61,7 @@ jobs:
go test -race -covermode=atomic -coverprofile=coverage.out ./...
- name: Upload coverage report
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: coverage-report
path: coverage.out
@@ -66,4 +69,4 @@ jobs:
- name: Run PR Lint
# Custom PR linting action that performs additional PR-specific checks
# https://github.com/ivuorinen/actions
uses: ivuorinen/actions/pr-lint@f98ae7cd7d0feb1f9d6b01de0addbb11414cfc73 # v2026.01.21
uses: ivuorinen/actions/pr-lint@8faacf8a1cae049c1471708dcb408a167e91afaf # v2026.02.24

View File

@@ -23,26 +23,26 @@ jobs:
fetch-depth: 0 # Required for changelog generation
- name: Set up Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version-file: go.mod
cache: true
- name: Install GoReleaser
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
with:
install-only: true
version: "~> v2"
- name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
with:
version: "~> v2"
args: release --clean
@@ -52,7 +52,7 @@ jobs:
# HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
- name: Upload Release Assets
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: release-artifacts
path: dist/

View File

@@ -23,4 +23,4 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: ivuorinen/actions/stale@f98ae7cd7d0feb1f9d6b01de0addbb11414cfc73 # v2026.01.21
- uses: ivuorinen/actions/stale@8faacf8a1cae049c1471708dcb408a167e91afaf # v2026.02.24

View File

@@ -23,4 +23,4 @@ jobs:
contents: read
issues: write
steps:
- uses: ivuorinen/actions/sync-labels@f98ae7cd7d0feb1f9d6b01de0addbb11414cfc73 # v2026.01.21
- uses: ivuorinen/actions/sync-labels@8faacf8a1cae049c1471708dcb408a167e91afaf # v2026.02.24

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@ dist/*
!dist/.gitkeep
# Anonymous test data from real fail2ban logs
!fail2ban/testdata/*
/.claude/settings.local.json

2
.mdformat.toml Normal file
View File

@@ -0,0 +1,2 @@
wrap = "keep"
end_of_line = "lf"

View File

@@ -14,6 +14,8 @@ PRINT_ALPACA: false # Print Alpaca logo in console
SARIF_REPORTER: true # Generate SARIF report
SHOW_SKIPPED_LINTERS: false # Show skipped linters in MegaLinter log
FILTER_REGEX_EXCLUDE: '(\.serena/)'
DISABLE_LINTERS:
- REPOSITORY_DEVSKIM
- GO_REVIVE # run as part of golangci-lint

View File

@@ -25,13 +25,13 @@ repos:
hooks:
- id: golangci-lint
name: golangci-lint
entry: golangci-lint run
entry: go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.10.1 run
language: system
types: [go]
pass_filenames: false
- repo: https://github.com/google/yamlfmt
rev: v0.20.0
rev: v0.21.0
hooks:
- id: yamlfmt
@@ -48,7 +48,7 @@ repos:
args: [-q, -c, .markdown-link-check.json]
- repo: https://github.com/rhysd/actionlint
rev: v1.7.9
rev: v1.7.11
hooks:
- id: actionlint
args: ["-shellcheck="]
@@ -59,26 +59,34 @@ repos:
- id: shfmt
- repo: https://github.com/checkmake/checkmake
rev: 0.2.2
rev: v0.3.2
hooks:
- id: checkmake
name: Makefile Linter
files: ^Makefile$
- repo: https://github.com/bridgecrewio/checkov.git
rev: "3.2.495"
rev: "3.2.506"
hooks:
- id: checkov
args:
- "--quiet"
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.36.0
rev: 0.37.0
hooks:
- id: check-github-workflows
args: ["--verbose"]
- repo: https://github.com/editorconfig-checker/editorconfig-checker
rev: v3.6.0
rev: v3.6.1
hooks:
- id: editorconfig-checker
- repo: https://github.com/hukkin/mdformat
rev: 1.0.0
hooks:
- id: mdformat
additional_dependencies:
- mdformat-gfm
exclude: ^(\.serena/|\.github/ISSUE_TEMPLATE/)

View File

@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
---
______________________________________________________________________
## [Unreleased]
@@ -62,4 +62,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Various minor bug fixes and improved test coverage.
- **Test safety**: Eliminated potential for real sudo execution during testing
---
______________________________________________________________________

View File

@@ -29,6 +29,6 @@ Claude Code **MUST** follow ALL instructions in [AGENTS.md](AGENTS.md) when work
**The f2b project is in production-ready state** with all critical infrastructure completed.
---
______________________________________________________________________
**📋 For all development work, refer to [AGENTS.md](AGENTS.md) for complete instructions.**

View File

@@ -121,8 +121,8 @@ version 2.0, available at
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
<https://www.contributor-covenant.org/faq>. Translations are available at
<https://www.contributor-covenant.org/translations>.
[homepage]: https://www.contributor-covenant.org

View File

@@ -3,7 +3,7 @@
Thank you for your interest in contributing to **f2b**! Your help is appreciated,
whether you are fixing bugs, adding features, improving documentation, or helping others.
---
______________________________________________________________________
## How to Contribute
@@ -48,7 +48,7 @@ go test ./...
- Describe your changes, reference related issues, and explain any design decisions.
- Be ready to discuss and revise your code based on feedback.
---
______________________________________________________________________
## Code Style
@@ -61,7 +61,7 @@ go test ./...
- Handle sudo privileges securely - validate before escalation, use mocks in tests.
- Use argument arrays for command execution, never shell string concatenation.
---
______________________________________________________________________
## Security & Testing Guidelines
@@ -78,7 +78,7 @@ For comprehensive security guidelines, testing patterns, and examples, see:
- [docs/testing.md](docs/testing.md) - Testing strategies and mock patterns
- [AGENTS.md](AGENTS.md) - AI/LLM contributor guidelines
---
______________________________________________________________________
## Communication
@@ -86,7 +86,7 @@ For comprehensive security guidelines, testing patterns, and examples, see:
- Review the [Code of Conduct](CODE_OF_CONDUCT.md).
- For large or breaking changes, open an issue to discuss your approach before submitting a PR.
---
______________________________________________________________________
## Additional Notes
@@ -95,9 +95,10 @@ For comprehensive security guidelines, testing patterns, and examples, see:
- If you are an AI/LLM agent, please see [AGENTS.md](AGENTS.md) for additional guidelines.
- By contributing, you agree that your contributions will be licensed under the MIT License.
---
______________________________________________________________________
Thank you for helping make **f2b** better!
[effective_go]: https://golang.org/doc/effective_go.html
[contributing](CONTRIBUTING.md)
[effective_go]: https://golang.org/doc/effective_go.html

200
Makefile
View File

@@ -1,10 +1,36 @@
# f2b Makefile
.PHONY: help all build test lint fmt clean install dev-deps ci \
check-deps test-verbose test-coverage update-deps \
lint-go lint-md lint-yaml lint-actions lint-make \
ci ci-coverage security dev-setup pre-commit-setup \
release-dry-run release release-snapshot release-check _check-tag
.PHONY: help all build test lint fmt clean install
.PHONY: ci ci-coverage test-verbose test-coverage update-deps fmt-md
.PHONY: lint-go lint-md lint-yaml lint-actions lint-make
.PHONY: security dev-setup pre-commit-setup
.PHONY: release-dry-run release release-snapshot release-check _check-tag
# Tool versions (managed by Renovate)
# renovate: datasource=go depName=github.com/goreleaser/goreleaser/v2
GORELEASER_VERSION := v2.14.1
# renovate: datasource=go depName=github.com/golangci/golangci-lint/v2/cmd/golangci-lint
GOLANGCI_LINT_VERSION := v2.10.1
# renovate: datasource=go depName=github.com/google/yamlfmt/cmd/yamlfmt
YAMLFMT_VERSION := v0.21.0
# renovate: datasource=go depName=github.com/rhysd/actionlint/cmd/actionlint
ACTIONLINT_VERSION := v1.7.11
# renovate: datasource=go depName=golang.org/x/tools/cmd/goimports
GOIMPORTS_VERSION := v0.42.0
# renovate: datasource=go depName=github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker
EDITORCONFIG_CHECKER_VERSION := v3.6.1
# renovate: datasource=go depName=github.com/securego/gosec/v2/cmd/gosec
GOSEC_VERSION := v2.24.0
# renovate: datasource=go depName=honnef.co/go/tools/cmd/staticcheck
STATICCHECK_VERSION := v0.7.0
# renovate: datasource=go depName=github.com/mgechev/revive
REVIVE_VERSION := v1.14.0
# renovate: datasource=go depName=github.com/checkmake/checkmake/cmd/checkmake
CHECKMAKE_VERSION := v0.3.2
# renovate: datasource=go depName=github.com/segmentio/golines
GOLINES_VERSION := v0.13.0
# renovate: datasource=npm depName=markdownlint-cli2
MARKDOWNLINT_CLI2_VERSION := 0.21.0
# Default target
help: ## Show this help message
@@ -14,7 +40,7 @@ help: ## Show this help message
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
all: ci ## Run all CI checks (same as ci target)
@echo "All checks completed"
@echo "All checks completed"
# Build targets
build: ## Build the f2b binary
@@ -23,107 +49,6 @@ build: ## Build the f2b binary
install: ## Install f2b globally
go install github.com/ivuorinen/f2b@latest
# Development dependencies
dev-deps: ## Install development dependencies
@echo "Installing development dependencies..."
@echo ""
@echo "Installing goreleaser..."
@go install github.com/goreleaser/goreleaser/v2@v2.12.0;
# renovate: datasource=go depName=github.com/goreleaser/goreleaser/v2
@GOLANGCI_VERSION=$$(golangci-lint version 2>/dev/null \
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "0.0.0"); \
EXPECTED_VERSION="2.7.2"; \
if [ "$$GOLANGCI_VERSION" != "$$EXPECTED_VERSION" ]; then \
echo "Installing golangci-lint v$$EXPECTED_VERSION (current: v$$GOLANGCI_VERSION)..."; \
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v$$EXPECTED_VERSION; \
fi
# renovate: datasource=go depName=github.com/golangci/golangci-lint/v2/cmd/golangci-lint
@command -v markdownlint-cli2 >/dev/null 2>&1 || { \
echo "Installing markdownlint-cli2..."; \
npm install -g markdownlint-cli2; \
}
@command -v markdown-link-check >/dev/null 2>&1 || { \
echo "Installing markdown-link-check..."; \
npm install -g markdown-link-check; \
}
@command -v yamlfmt >/dev/null 2>&1 || { \
echo "Installing yamlfmt..."; \
go install github.com/google/yamlfmt/cmd/yamlfmt@v0.17.2; \
}
# renovate: datasource=go depName=github.com/google/yamlfmt/cmd/yamlfmt
@command -v actionlint >/dev/null 2>&1 || { \
echo "Installing actionlint..."; \
go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.7; \
}
# renovate: datasource=go depName=github.com/rhysd/actionlint/cmd/actionlint
@command -v goimports >/dev/null 2>&1 || { \
echo "Installing goimports..."; \
go install golang.org/x/tools/cmd/goimports@v0.28.0; \
}
# renovate: datasource=go depName=golang.org/x/tools/cmd/goimports
@command -v editorconfig-checker >/dev/null 2>&1 || { \
echo "Installing editorconfig-checker..."; \
go install github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.4.0; \
}
# renovate: datasource=go depName=github.com/editorconfig-checker/editorconfig-checker/v3
@command -v gosec >/dev/null 2>&1 || { \
echo "Installing gosec..."; \
go install github.com/securego/gosec/v2/cmd/gosec@v2.22.8; \
}
# renovate: datasource=go depName=github.com/securego/gosec/v2/cmd/gosec
@command -v staticcheck >/dev/null 2>&1 || { \
echo "Installing staticcheck..."; \
go install honnef.co/go/tools/cmd/staticcheck@2024.1.1; \
}
# renovate: datasource=go depName=honnef.co/go/tools/cmd/staticcheck
@command -v revive >/dev/null 2>&1 || { \
echo "Installing revive..."; \
go install github.com/mgechev/revive@v1.12.0; \
}
# renovate: datasource=go depName=github.com/mgechev/revive
@command -v checkmake >/dev/null 2>&1 || { \
echo "Installing checkmake..."; \
go install github.com/checkmake/checkmake/cmd/checkmake@0.2.2; \
}
# renovate: datasource=go depName=github.com/checkmake/checkmake/cmd/checkmake
@command -v golines >/dev/null 2>&1 || { \
echo "Installing golines..."; \
go install github.com/segmentio/golines@v0.13.0; \
}
# renovate: datasource=go depName=github.com/segmentio/golines
check-deps: ## Check if all development dependencies are installed
@echo "Checking development dependencies..."
@command -v go >/dev/null 2>&1 || { \
echo "go is not installed"; exit 1; }
@command -v goreleaser >/dev/null 2>&1 || {
echo "goreleaser is not installed (run: make dev-deps)"; exit 1; }
@command -v golangci-lint >/dev/null 2>&1 || {
echo "golangci-lint is not installed (run: make dev-deps)"; exit 1; }
@command -v markdownlint-cli2 >/dev/null 2>&1 || {
echo "markdownlint-cli2 is not installed (run: make dev-deps)"; exit 1; }
@command -v markdown-link-check >/dev/null 2>&1 || {
echo "markdown-link-check is not installed (run: make dev-deps)"; exit 1; }
@command -v goimports >/dev/null 2>&1 || {
echo "goimports is not installed (run: make dev-deps)"; exit 1; }
@command -v editorconfig-checker >/dev/null 2>&1 || {
echo "editorconfig-checker is not installed (run: make dev-deps)"; exit 1; }
@command -v gosec >/dev/null 2>&1 || {
echo "gosec is not installed (run: make dev-deps)"; exit 1; }
@command -v staticcheck >/dev/null 2>&1 || {
echo "staticcheck is not installed (run: make dev-deps)"; exit 1; }
@command -v revive >/dev/null 2>&1 || {
echo "revive is not installed (run: make dev-deps)"; exit 1; }
@command -v checkmake >/dev/null 2>&1 || {
echo "checkmake is not installed (run: make dev-deps)"; exit 1; }
@command -v yamlfmt >/dev/null 2>&1 || {
echo "yamlfmt is not installed (run: make dev-deps)"; exit 1; }
@command -v actionlint >/dev/null 2>&1 || {
echo "actionlint is not installed (run: make dev-deps)"; exit 1; }
@command -v golines >/dev/null 2>&1 || {
echo "golines is not installed (run: make dev-deps)"; exit 1; }
@echo "All dependencies are installed ✓"
# Testing targets
test: ## Run all tests
go test ./...
@@ -134,42 +59,38 @@ test-verbose: ## Run tests with verbose output
test-coverage: ## Run tests with coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
@echo "Coverage report saved to coverage.html"
update-deps: ## Update Go dependencies to latest patch versions
@echo "Updating Go dependencies (patch versions only)..."
go get -u=patch ./...
go mod tidy
go mod verify
@echo "Dependencies updated ✓"
@echo "Updated dependencies:"
@go list -u -m all | grep '\[' || true
# Code quality targets
fmt: ## Format Go code
gofmt -w .
@echo "Go code formatted ✓"
fmt-md: ## Format Markdown files
@pre-commit run mdformat --all-files
lint: ## Run all linters using pre-commit (preferred method)
@echo "Running pre-commit linters..."
@pre-commit run --all-files
@echo "All linting completed ✓"
lint-go: ## Run only Go linters
go vet ./...
golangci-lint run --timeout=5m
go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) run --timeout=5m
lint-md: ## Run only Markdown linter
markdownlint-cli2 *.md **/*.md
npx --yes markdownlint-cli2@$(MARKDOWNLINT_CLI2_VERSION) "*.md" "**/*.md"
lint-yaml: ## Run only YAML linter
yamlfmt -lint .
go run github.com/google/yamlfmt/cmd/yamlfmt@$(YAMLFMT_VERSION) -lint .
lint-actions: ## Run only GitHub Actions linter
actionlint .github/workflows/*.yml
go run github.com/rhysd/actionlint/cmd/actionlint@$(ACTIONLINT_VERSION) .github/workflows/*.yml
lint-make: ## Run only Makefile linter
checkmake Makefile
go run github.com/checkmake/checkmake/cmd/checkmake@$(CHECKMAKE_VERSION) Makefile
# CI targets
ci: fmt lint test ## Run all CI checks (format, lint, test)
@@ -178,48 +99,28 @@ ci-coverage: fmt lint test-coverage ## Run CI checks with coverage
# Security targets
security: ## Run security checks
gosec ./...
go run github.com/securego/gosec/v2/cmd/gosec@$(GOSEC_VERSION) ./...
# Cleanup targets
clean: ## Clean build artifacts
rm -f f2b
rm -f coverage.out
rm -f coverage.html
rm -f f2b coverage.out coverage.html
go clean
# Development targets
dev-setup: dev-deps ## Set up development environment
@echo "Setting up development environment..."
@echo "Installing pre-commit hooks..."
@command -v pre-commit >/dev/null 2>&1 || { \
echo "Installing pre-commit..."; \
pip install pre-commit; \
}
@pre-commit install
@echo "Development environment setup complete ✓"
dev-setup: pre-commit-setup ## Set up development environment
pre-commit-setup: ## Install and configure pre-commit hooks
@echo "Installing pre-commit..."
@command -v pre-commit >/dev/null 2>&1 || { \
echo "Installing pre-commit..."; \
pip install pre-commit; \
}
@command -v pre-commit >/dev/null 2>&1 || pip install pre-commit
@pre-commit install
@echo "Pre-commit hooks installed ✓"
# Release targets
release-dry-run: ## Test release process without creating artifacts
@echo "Testing release process..."
@VERSION=$$(git describe --tags --exact-match 2>/dev/null || echo "v0.0.0-dev"); \
echo "Building version: $$VERSION"; \
go build -ldflags "-X github.com/ivuorinen/f2b/cmd.version=$$VERSION" -o f2b-test .
@rm -f f2b-test
@echo "Release dry-run complete ✓"
go build -ldflags "-X github.com/ivuorinen/f2b/cmd.version=$$VERSION" -o f2b-test . && rm -f f2b-test
release: ## Create a new release using GoReleaser
@echo "Creating release with GoReleaser..."
@$(MAKE) _check-tag
@goreleaser release --clean
release: _check-tag ## Create a new release using GoReleaser
go run github.com/goreleaser/goreleaser/v2@$(GORELEASER_VERSION) release --clean
_check-tag: ## Internal: Check if a git tag exists
@if [ -z "$$(git describe --exact-match 2>/dev/null)" ]; then \
@@ -228,10 +129,7 @@ _check-tag: ## Internal: Check if a git tag exists
fi
release-snapshot: ## Create a snapshot release (no tag required)
@echo "Creating snapshot release with GoReleaser..."
goreleaser release --snapshot --clean
go run github.com/goreleaser/goreleaser/v2@$(GORELEASER_VERSION) release --snapshot --clean
release-check: ## Check if GoReleaser configuration is valid
@echo "Checking GoReleaser configuration..."
goreleaser check
@echo "GoReleaser configuration is valid ✓"
go run github.com/goreleaser/goreleaser/v2@$(GORELEASER_VERSION) check

View File

@@ -7,7 +7,7 @@ Built with Go, featuring automatic sudo privilege management, shell completion,
[![Go Version](https://img.shields.io/badge/Go-%3E%3D1.25-blue.svg)](https://golang.org/)
[![Build Status](https://img.shields.io/badge/tests-passing-brightgreen.svg)](https://github.com/ivuorinen/f2b/actions)
---
______________________________________________________________________
## 🚀 Quick Start
@@ -79,7 +79,7 @@ make build
go build -ldflags "-X github.com/ivuorinen/f2b/cmd.version=1.2.3" -o f2b .
```
---
______________________________________________________________________
## ✨ Key Features
@@ -115,7 +115,7 @@ go build -ldflags "-X github.com/ivuorinen/f2b/cmd.version=1.2.3" -o f2b .
- **Thread Safety**: Extensive race condition testing and protection
- **Security Audit Trail**: Comprehensive logging of all privileged operations
---
______________________________________________________________________
## 📋 Usage Examples
@@ -190,7 +190,7 @@ f2b completion fish > ~/.config/fish/completions/f2b.fish
f2b completion powershell | Out-String | Invoke-Expression
```
---
______________________________________________________________________
## ⚙️ Configuration
@@ -249,7 +249,7 @@ f2b logs sshd --limit 50 --format=json
f2b --log-level=debug --log-file=/tmp/f2b-debug.log ban 192.168.1.100
```
---
______________________________________________________________________
## 🔐 Security & Privileges
@@ -268,7 +268,7 @@ f2b is designed with security as a fundamental principle:
For detailed security practices, threat model, and contribution security guidelines, see
[docs/security.md](docs/security.md).
---
______________________________________________________________________
## 📖 Complete Command Reference
@@ -322,7 +322,7 @@ For convenience, most commands have short aliases:
- `ban``banip`, `b`
- `unban``unbanip`, `ub`
---
______________________________________________________________________
## 🏗️ Architecture
@@ -338,8 +338,8 @@ f2b is built as an **enterprise-grade** Go application following modern architec
### 📊 **Quality Metrics**
- **Test Coverage**: 76.8% (cmd/), 59.3% (fail2ban/) - Above industry standards
- **Modern Testing**: Fluent testing framework reducing code duplication by 60-70%
- **Test Coverage**: Comprehensive coverage across all packages - above industry standards
- **Modern Testing**: Fluent testing framework with significant reduction in test duplication
- **Security Testing**: 13 comprehensive attack vector test cases implemented
- **Performance**: Context-aware operations with configurable timeouts and resource management
@@ -363,7 +363,7 @@ f2b is built as an **enterprise-grade** Go application following modern architec
For detailed architecture information, implementation patterns, and extension guidelines,
see [docs/architecture.md](docs/architecture.md).
---
______________________________________________________________________
## 🧪 Development & Testing
@@ -395,8 +395,7 @@ This project uses [pre-commit](https://pre-commit.com/) for unified linting and
Install the development dependencies and hooks:
```bash
make dev-deps
make pre-commit-setup
make dev-setup
```
Run all linters:
@@ -428,7 +427,7 @@ f2b logs-watch all --limit 20 | while read line; do
done
```
---
______________________________________________________________________
## 🚀 Releases
@@ -438,19 +437,19 @@ Releases are automated using [GoReleaser](https://goreleaser.com/). To create a
1. **Tag the release:**
```bash
git tag -a v1.2.3 -m "Release v1.2.3"
git push origin v1.2.3
```
```bash
git tag -a v1.2.3 -m "Release v1.2.3"
git push origin v1.2.3
```
2. **GitHub Actions will automatically:**
- Build binaries for multiple platforms (Linux, macOS, Windows, BSD)
- Create a GitHub release with changelog
- Upload release artifacts
- Build and push Docker images
- Update Homebrew tap (if configured)
- Generate .deb, .rpm, and .apk packages
- Build binaries for multiple platforms (Linux, macOS, Windows, BSD)
- Create a GitHub release with changelog
- Upload release artifacts
- Build and push Docker images
- Update Homebrew tap (if configured)
- Generate .deb, .rpm, and .apk packages
### Manual Release (Development)
@@ -476,7 +475,7 @@ Each release includes:
- Docker images at `ghcr.io/ivuorinen/f2b` with architecture-specific tags
- Linux packages (.deb, .rpm, .apk) for multiple architectures
---
______________________________________________________________________
## 🤝 Contributing
@@ -500,25 +499,25 @@ Please see:
- [docs/security.md](docs/security.md) - Security practices and guidelines
- [docs/testing.md](docs/testing.md) - Testing strategies and patterns
---
______________________________________________________________________
## 📄 License
[MIT License](LICENSE.md).
---
______________________________________________________________________
## 👨‍💻 Author
**Ismo Vuorinen** ([@ivuorinen](https://github.com/ivuorinen))
---
______________________________________________________________________
## 🆘 Support
- 📝 [Open an issue](https://github.com/ivuorinen/f2b/issues)
- 📖 [Read the FAQ](docs/faq.md)
---
______________________________________________________________________
_Built with ❤️ and Go. Securing systems one ban at a time._

View File

@@ -513,7 +513,7 @@ func TestPersistentPreRun(t *testing.T) {
t.Fatalf("failed to create temp file: %v", err)
}
defer func() {
if err := os.Remove(tmpFile.Name()); err != nil {
if err := os.Remove(tmpFile.Name()); err != nil { // #nosec G703 -- test file, path from CreateTemp
t.Fatalf("failed to remove temp file: %v", err)
}
}()

View File

@@ -52,46 +52,44 @@ func printMetricsPlain(output io.Writer, snapshot MetricsSnapshot) error {
// System metrics
sb.WriteString("System:\n")
sb.WriteString(fmt.Sprintf(" Uptime: %ds\n", snapshot.UptimeSeconds))
sb.WriteString(fmt.Sprintf(" Max Memory: %.2f MB\n", float64(snapshot.MaxMemoryUsage)/(1024*1024)))
sb.WriteString(fmt.Sprintf(" Goroutines: %d\n\n", snapshot.GoroutineCount))
fmt.Fprintf(&sb, " Uptime: %ds\n", snapshot.UptimeSeconds)
fmt.Fprintf(&sb, " Max Memory: %.2f MB\n", float64(snapshot.MaxMemoryUsage)/(1024*1024))
fmt.Fprintf(&sb, " Goroutines: %d\n\n", snapshot.GoroutineCount)
// Command metrics
sb.WriteString("Commands:\n")
sb.WriteString(fmt.Sprintf(shared.MetricsFmtTotalExecutions, snapshot.CommandExecutions))
sb.WriteString(fmt.Sprintf(shared.MetricsFmtTotalFailures, snapshot.CommandFailures))
fmt.Fprintf(&sb, shared.MetricsFmtTotalExecutions, snapshot.CommandExecutions)
fmt.Fprintf(&sb, shared.MetricsFmtTotalFailures, snapshot.CommandFailures)
if snapshot.CommandExecutions > 0 {
avgLatency := float64(snapshot.CommandTotalDuration) / float64(snapshot.CommandExecutions)
sb.WriteString(fmt.Sprintf(shared.MetricsFmtAverageLatencyTop, avgLatency))
fmt.Fprintf(&sb, shared.MetricsFmtAverageLatencyTop, avgLatency)
}
sb.WriteString("\n")
// Ban/Unban metrics
sb.WriteString("Ban Operations:\n")
sb.WriteString(fmt.Sprintf(" Ban Operations: %d (failures: %d)\n", snapshot.BanOperations, snapshot.BanFailures))
sb.WriteString(
fmt.Sprintf(" Unban Operations: %d (failures: %d)\n", snapshot.UnbanOperations, snapshot.UnbanFailures),
)
fmt.Fprintf(&sb, shared.MetricsFmtBanOperations, snapshot.BanOperations, snapshot.BanFailures)
fmt.Fprintf(&sb, shared.MetricsFmtUnbanOperations, snapshot.UnbanOperations, snapshot.UnbanFailures)
sb.WriteString("\n")
// Client metrics
sb.WriteString("Client Operations:\n")
sb.WriteString(fmt.Sprintf(shared.MetricsFmtTotalOperations, snapshot.ClientOperations))
sb.WriteString(fmt.Sprintf(shared.MetricsFmtTotalFailures, snapshot.ClientFailures))
fmt.Fprintf(&sb, shared.MetricsFmtTotalOperations, snapshot.ClientOperations)
fmt.Fprintf(&sb, shared.MetricsFmtTotalFailures, snapshot.ClientFailures)
if snapshot.ClientOperations > 0 {
avgLatency := float64(snapshot.ClientTotalDuration) / float64(snapshot.ClientOperations)
sb.WriteString(fmt.Sprintf(shared.MetricsFmtAverageLatencyTop, avgLatency))
fmt.Fprintf(&sb, shared.MetricsFmtAverageLatencyTop, avgLatency)
}
sb.WriteString("\n")
// Validation metrics
sb.WriteString("Validation:\n")
sb.WriteString(fmt.Sprintf(" Cache Hits: %d\n", snapshot.ValidationCacheHits))
sb.WriteString(fmt.Sprintf(" Cache Misses: %d\n", snapshot.ValidationCacheMiss))
sb.WriteString(fmt.Sprintf(" Failures: %d\n", snapshot.ValidationFailures))
fmt.Fprintf(&sb, " Cache Hits: %d\n", snapshot.ValidationCacheHits)
fmt.Fprintf(&sb, " Cache Misses: %d\n", snapshot.ValidationCacheMiss)
fmt.Fprintf(&sb, " Failures: %d\n", snapshot.ValidationFailures)
if total := snapshot.ValidationCacheHits + snapshot.ValidationCacheMiss; total > 0 {
hitRate := float64(snapshot.ValidationCacheHits) / float64(total) * 100
sb.WriteString(fmt.Sprintf(" Cache Hit Rate: %.2f%%\n", hitRate))
fmt.Fprintf(&sb, " Cache Hit Rate: %.2f%%\n", hitRate)
}
sb.WriteString("\n")

View File

@@ -239,14 +239,14 @@ const (
The configuration system supports the following environment variables:
| Variable | Description | Default |
| -------- | ----------- | ------- |
| `F2B_LOG_DIR` | Log directory path | `/var/log` |
| `F2B_FILTER_DIR` | Filter directory path | `/etc/fail2ban/filter.d` |
| `F2B_LOG_LEVEL` | Log level | `info` |
| `F2B_COMMAND_TIMEOUT` | Command timeout | `30s` |
| `F2B_FILE_TIMEOUT` | File operation timeout | `10s` |
| `F2B_PARALLEL_TIMEOUT` | Parallel operation timeout | `60s` |
| Variable | Description | Default |
| ---------------------- | -------------------------- | ------------------------ |
| `F2B_LOG_DIR` | Log directory path | `/var/log` |
| `F2B_FILTER_DIR` | Filter directory path | `/etc/fail2ban/filter.d` |
| `F2B_LOG_LEVEL` | Log level | `info` |
| `F2B_COMMAND_TIMEOUT` | Command timeout | `30s` |
| `F2B_FILE_TIMEOUT` | File operation timeout | `10s` |
| `F2B_PARALLEL_TIMEOUT` | Parallel operation timeout | `60s` |
### Path Security
@@ -550,30 +550,30 @@ func (h *HTTPHandler) writeError(w http.ResponseWriter, code int, err error) {
### Error Handling Best Practices
1. Always use contextual errors for user-facing messages
2. Provide remediation hints where possible
3. Log errors with appropriate context
4. Use error categories for systematic handling
1. Provide remediation hints where possible
1. Log errors with appropriate context
1. Use error categories for systematic handling
### Context Usage
1. Always use context for operations that can timeout
2. Propagate context through the call chain
3. Add relevant context values for logging
4. Use context cancellation for cleanup
1. Propagate context through the call chain
1. Add relevant context values for logging
1. Use context cancellation for cleanup
### Testing
1. Use the fluent testing framework for command tests
2. Always use mock environments for integration tests
3. Test both success and failure scenarios
4. Include timeout testing for long-running operations
1. Always use mock environments for integration tests
1. Test both success and failure scenarios
1. Include timeout testing for long-running operations
### Performance
1. Use the metrics system to monitor performance
2. Implement proper caching where appropriate
3. Use object pooling for frequently allocated objects
4. Profile and optimize hot paths
1. Implement proper caching where appropriate
1. Use object pooling for frequently allocated objects
1. Profile and optimize hot paths
This documentation provides a comprehensive overview of the f2b internal APIs and patterns.
For specific implementation details, refer to the source code and inline documentation.

View File

@@ -35,17 +35,21 @@ validation caching, and parallel processing capabilities for enterprise-grade re
### fail2ban/ Package
- **Purpose**: Core business logic and system interaction
- **Key Interfaces**:
- `Client`: Main interface for fail2ban operations with context support
- `Runner`: Command execution interface
- `SudoChecker`: Privilege validation interface
- **Implementations**:
- `RealClient`: Production fail2ban client with timeout handling
- `MockClient`: Comprehensive test double with thread-safe operations
- `NoOpClient`: Safe fallback implementation
- **Advanced Features**:
- Context-aware operations with timeout and cancellation support
- Validation caching system with thread-safe operations
- Optimized ban record parsing with object pooling
@@ -105,14 +109,14 @@ validation caching, and parallel processing capabilities for enterprise-grade re
### Command Execution Flow
1. **CLI Parsing**: Cobra processes command-line arguments
2. **Context Creation**: Create context with timeout for operation
3. **Validation**: Input validation with caching and sanitization
4. **Privilege Check**: Determine if sudo is required
5. **Metrics Start**: Begin performance metrics collection
6. **Business Logic**: Execute fail2ban operations via Client interface with context
7. **Parallel Processing**: Use parallel workers for multi-jail operations
8. **Metrics End**: Record operation timing and success/failure
9. **Output**: Format and display results (plain or JSON)
1. **Context Creation**: Create context with timeout for operation
1. **Validation**: Input validation with caching and sanitization
1. **Privilege Check**: Determine if sudo is required
1. **Metrics Start**: Begin performance metrics collection
1. **Business Logic**: Execute fail2ban operations via Client interface with context
1. **Parallel Processing**: Use parallel workers for multi-jail operations
1. **Metrics End**: Record operation timing and success/failure
1. **Output**: Format and display results (plain or JSON)
### Dependency Flow
@@ -170,25 +174,25 @@ fail2ban/client.go
### Adding New Commands
1. Create new file in `cmd/` package
2. Implement command using established patterns with context support
3. Use dependency injection for testability
4. Add performance metrics collection
5. Implement fluent testing framework patterns
6. Add comprehensive tests with mocks and context-aware operations
1. Implement command using established patterns with context support
1. Use dependency injection for testability
1. Add performance metrics collection
1. Implement fluent testing framework patterns
1. Add comprehensive tests with mocks and context-aware operations
### Adding New Backends
1. Implement the `Client` interface
2. Add any new required interfaces (Runner, etc.)
3. Update main.go to support new backend
4. Add configuration options
1. Add any new required interfaces (Runner, etc.)
1. Update main.go to support new backend
1. Add configuration options
### Adding New Output Formats
1. Extend output formatting helpers
2. Update command implementations
3. Add format validation
4. Test with existing commands
1. Update command implementations
1. Add format validation
1. Test with existing commands
## Testing Architecture

View File

@@ -8,7 +8,7 @@
extensible, and user-friendly alternative to Bash scripts for interacting with Fail2Ban, with automatic sudo
privilege management, shell completion, and comprehensive security features.
---
______________________________________________________________________
## Installation & Setup
@@ -34,7 +34,7 @@ Or install globally:
go install github.com/ivuorinen/f2b@latest
```
---
______________________________________________________________________
## Usage
@@ -171,7 +171,7 @@ f2b --command-timeout=45s ban 192.168.1.100
f2b --parallel-timeout=120s banned all
```
---
______________________________________________________________________
## Troubleshooting
@@ -193,9 +193,9 @@ f2b --parallel-timeout=120s banned all
This means you need elevated privileges for the operation you're trying to perform:
1. **Check your privileges:** Run `f2b --log-level=debug version` to see your privilege status
2. **Add sudo:** Try `sudo f2b [command]`
3. **Join sudo group:** Ask your admin to add you to the sudo group
4. **Test sudo access:** Run `sudo -n true` to check if you can use sudo
1. **Add sudo:** Try `sudo f2b [command]`
1. **Join sudo group:** Ask your admin to add you to the sudo group
1. **Test sudo access:** Run `sudo -n true` to check if you can use sudo
### The CLI says "permission denied" or "operation not permitted"
@@ -265,7 +265,7 @@ ls -la /etc/fail2ban/
sudo fail2ban-client ping
```
---
______________________________________________________________________
## Development
@@ -279,11 +279,11 @@ go test ./...
See the `CONTRIBUTING.md` and the Contributing section in the README.
---
______________________________________________________________________
## Still need help?
- Open an issue on GitHub: https://github.com/ivuorinen/f2b/issues
- Contact the maintainer: ismo@ivuorinen.net
---
______________________________________________________________________

View File

@@ -225,11 +225,11 @@ Both workflows now use unified pre-commit:
### Before Committing
1. **Read configuration files first**: `.editorconfig`, `.golangci.yml`,
`.markdownlint.json`, `.yamlfmt.yaml`, `.pre-commit-config.yaml`
2. **Apply configuration rules** during development
3. **Run pre-commit checks**: `pre-commit run --all-files`
4. **Fix all issues** across the project
5. **Run tests**: `go test ./...`
`.markdownlint.json`, `.yamlfmt.yaml`, `.pre-commit-config.yaml`
1. **Apply configuration rules** during development
1. **Run pre-commit checks**: `pre-commit run --all-files`
1. **Fix all issues** across the project
1. **Run tests**: `go test ./...`
### Recommended IDE Setup
@@ -303,19 +303,19 @@ All YAML files include schema references for better IDE support:
### Debugging Tips
1. **Run individual hooks** to isolate issues
2. **Use `--verbose` flag** with pre-commit
3. **Check configuration files** for rule customizations
4. **Verify tool versions** match CI environment
1. **Use `--verbose` flag** with pre-commit
1. **Check configuration files** for rule customizations
1. **Verify tool versions** match CI environment
## Adding New Linting Rules
### Process
1. Update configuration files (`.markdownlint.json`, `.yamlfmt.yaml`, etc.)
2. Test changes locally: `pre-commit run --all-files`
3. Update `.pre-commit-config.yaml` if adding new hooks
4. Document changes in this file
5. Consider backward compatibility
1. Test changes locally: `pre-commit run --all-files`
1. Update `.pre-commit-config.yaml` if adding new hooks
1. Document changes in this file
1. Consider backward compatibility
### Best Practices

View File

@@ -57,13 +57,13 @@ f2b intelligently manages sudo requirements through a comprehensive privilege ch
### Privilege Escalation Process
1. **Pre-flight Check**: Determine user capabilities before command execution
2. **Context Creation**: Create context with timeout for the operation
3. **Command Classification**: Identify if the operation requires privileges
4. **Smart Escalation**: Only add sudo when necessary for specific commands
5. **Validation**: Ensure privilege escalation succeeded with timeout protection
6. **Execution**: Run command with appropriate privileges and context
7. **Timeout Handling**: Gracefully handle hanging operations with cancellation
8. **Audit**: Log privileged operations with context information
1. **Context Creation**: Create context with timeout for the operation
1. **Command Classification**: Identify if the operation requires privileges
1. **Smart Escalation**: Only add sudo when necessary for specific commands
1. **Validation**: Ensure privilege escalation succeeded with timeout protection
1. **Execution**: Run command with appropriate privileges and context
1. **Timeout Handling**: Gracefully handle hanging operations with cancellation
1. **Audit**: Log privileged operations with context information
### Error Handling
@@ -358,10 +358,10 @@ func setupSecureTestEnvironment(t *testing.T) {
- **Issue**: Insufficient path validation against sophisticated attacks
- **Impact**: Access to files outside intended directories
- **Fix**: Comprehensive path traversal protection with extensive test cases covering:
- Unicode normalization attacks (\u002e\u002e)
- Unicode normalization attacks (\\u002e\\u002e)
- Mixed case traversal (/var/LOG/../../../etc/passwd)
- Multiple slashes (/var/log////../../etc/passwd)
- Windows-style paths on Unix (/var/log\..\..\..\etc\passwd)
- Windows-style paths on Unix (`/var/log\..\..\..\etc\passwd`)
- URL encoding variants (%2e%2e%2f)
- Null byte injection attacks
@@ -382,15 +382,15 @@ func setupSecureTestEnvironment(t *testing.T) {
### Defense in Depth
1. **Input Validation**: First line of defense against malicious input with caching
2. **Advanced Path Traversal Protection**: Extensive sophisticated attack vector protection
3. **Privilege Validation**: Ensure user has necessary permissions with timeout protection
4. **Context-Aware Execution**: Use argument arrays with timeout and cancellation support
5. **Safe Execution**: Never use shell strings, always use context-aware operations
6. **Error Handling**: Fail safely without information leakage, include context information
7. **Audit Logging**: Track privileged operations with contextual information
8. **Test Isolation**: Prevent test-time security compromises with comprehensive mocks
9. **Performance Security**: Validation caching prevents DoS through repeated validation
10. **Timeout Protection**: Prevent resource exhaustion through hanging operations
1. **Advanced Path Traversal Protection**: Extensive sophisticated attack vector protection
1. **Privilege Validation**: Ensure user has necessary permissions with timeout protection
1. **Context-Aware Execution**: Use argument arrays with timeout and cancellation support
1. **Safe Execution**: Never use shell strings, always use context-aware operations
1. **Error Handling**: Fail safely without information leakage, include context information
1. **Audit Logging**: Track privileged operations with contextual information
1. **Test Isolation**: Prevent test-time security compromises with comprehensive mocks
1. **Performance Security**: Validation caching prevents DoS through repeated validation
1. **Timeout Protection**: Prevent resource exhaustion through hanging operations
### Security Boundaries
@@ -403,13 +403,13 @@ User Input → Context → Validation → Path Traversal → Privilege Check →
**Enhanced Security Flow:**
1. **Context Creation**: Establish timeout and cancellation context
2. **Input Sanitization**: Clean and validate all user input
3. **Cache Validation**: Check validation cache for performance and DoS protection
4. **Path Traversal Protection**: Block extensive sophisticated attack vectors
5. **Privilege Verification**: Confirm user permissions with timeout protection
6. **Context-Aware Execution**: Execute with timeout and cancellation support
7. **Timeout Handling**: Gracefully handle hanging operations
8. **Comprehensive Auditing**: Log all operations with context information
1. **Input Sanitization**: Clean and validate all user input
1. **Cache Validation**: Check validation cache for performance and DoS protection
1. **Path Traversal Protection**: Block extensive sophisticated attack vectors
1. **Privilege Verification**: Confirm user permissions with timeout protection
1. **Context-Aware Execution**: Execute with timeout and cancellation support
1. **Timeout Handling**: Gracefully handle hanging operations
1. **Comprehensive Auditing**: Log all operations with context information
## Incident Response
@@ -418,17 +418,17 @@ User Input → Context → Validation → Path Traversal → Privilege Check →
**For security vulnerabilities:**
1. **Do not** open public GitHub issues
2. Email: `ismo@ivuorinen.net` with subject "SECURITY: f2b vulnerability"
3. Include: Description, impact assessment, reproduction steps
4. Expect: Acknowledgment within 48 hours
1. Email: `ismo@ivuorinen.net` with subject "SECURITY: f2b vulnerability"
1. Include: Description, impact assessment, reproduction steps
1. Expect: Acknowledgment within 48 hours
### Security Update Process
1. **Assessment**: Evaluate impact and affected versions
2. **Development**: Create fix with security tests
3. **Testing**: Comprehensive security testing
4. **Release**: Coordinated disclosure with security advisory
5. **Communication**: Notify users via GitHub security advisories
1. **Development**: Create fix with security tests
1. **Testing**: Comprehensive security testing
1. **Release**: Coordinated disclosure with security advisory
1. **Communication**: Notify users via GitHub security advisories
## Security Best Practices

View File

@@ -604,11 +604,11 @@ go tool cover -func=coverage.out | grep total
### Avoid These Mistakes
1. **Real sudo execution in tests** - Always use MockSudoChecker
2. **Hardcoded file paths** - Use temporary files or mocks
3. **Network dependencies** - Mock all external calls
4. **Race conditions** - Use proper synchronization in concurrent tests
5. **Leaked goroutines** - Clean up background processes
6. **Platform dependencies** - Write portable tests
1. **Hardcoded file paths** - Use temporary files or mocks
1. **Network dependencies** - Mock all external calls
1. **Race conditions** - Use proper synchronization in concurrent tests
1. **Leaked goroutines** - Clean up background processes
1. **Platform dependencies** - Write portable tests
### Enhanced Security Testing Checklist

View File

@@ -74,6 +74,7 @@ func (r *OSRunner) CombinedOutputWithContext(ctx context.Context, name string, a
if err := validateCommandExecution(ctx, name, args); err != nil {
return nil, err
}
// #nosec G204 -- command validated by validateCommandExecution
return exec.CommandContext(ctx, name, args...).CombinedOutput()
}
@@ -92,7 +93,7 @@ func (r *OSRunner) CombinedOutputWithSudoContext(ctx context.Context, name strin
// If already root, no need for sudo
if checker.IsRoot() {
return exec.CommandContext(ctx, name, args...).CombinedOutput()
return exec.CommandContext(ctx, name, args...).CombinedOutput() // #nosec G204 -- command validated above
}
// If command requires sudo and user has privileges, use sudo
@@ -104,7 +105,7 @@ func (r *OSRunner) CombinedOutputWithSudoContext(ctx context.Context, name strin
}
// Otherwise run without sudo
return exec.CommandContext(ctx, name, args...).CombinedOutput()
return exec.CommandContext(ctx, name, args...).CombinedOutput() // #nosec G204 -- command validated above
}
// runnerManager provides thread-safe access to the global Runner.

View File

@@ -34,7 +34,12 @@ func main() {
// Check if this is a sudo privilege error
if strings.Contains(err.Error(), "fail2ban operations require sudo privileges") {
fmt.Fprintln(os.Stderr, "Hint: Try running with 'sudo' or ensure your user is in the sudo group")
fmt.Fprintln(os.Stderr, "Example: sudo", strings.Join(os.Args, " "))
// #nosec G705 -- stderr hint, not user-facing HTML
fmt.Fprintln(
os.Stderr,
"Example: sudo",
strings.Join(os.Args, " "),
)
}
os.Exit(1)
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"os"
"path/filepath"
"strconv"
"testing"
"time"
@@ -179,7 +180,7 @@ func BenchmarkGlobalStateAccess(b *testing.B) {
fail2ban.GetLogDir()
} else {
// 50% writes
fail2ban.SetLogDir("/tmp/test-" + string(rune(b.N)))
fail2ban.SetLogDir("/tmp/test-" + strconv.Itoa(b.N))
}
}
})

View File

@@ -4,7 +4,9 @@ import (
"context"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"testing"
"github.com/ivuorinen/f2b/fail2ban"
@@ -378,24 +380,28 @@ func TestSecurityAudit_ConcurrentSafety(t *testing.T) {
// Multiple goroutines modifying global state should not cause races
// This is tested by running with -race flag in CI
for i := 0; i < 10; i++ {
go func(id int) {
fail2ban.SetLogDir("/tmp/test-" + string(rune(id)))
var wg sync.WaitGroup
for i := range 10 {
wg.Go(func() {
fail2ban.SetLogDir("/tmp/test-" + strconv.Itoa(i))
fail2ban.GetLogDir()
}(i)
})
}
wg.Wait()
})
t.Run("CacheStatisticsSafety", func(_ *testing.T) {
processor := fail2ban.NewOptimizedLogProcessor()
// Multiple goroutines accessing cache statistics should be safe
for i := 0; i < 10; i++ {
go func() {
var wg sync.WaitGroup
for range 10 {
wg.Go(func() {
processor.GetCacheStats()
processor.ClearCaches()
}()
})
}
wg.Wait()
})
}

View File

@@ -499,4 +499,10 @@ const (
// MetricsFmtAverageLatencyTop is the format for average latency (top-level)
MetricsFmtAverageLatencyTop = " Average Latency: %.2f ms\n"
// MetricsFmtBanOperations is the format for ban operations with failures
MetricsFmtBanOperations = " Ban Operations: %d (failures: %d)\n"
// MetricsFmtUnbanOperations is the format for unban operations with failures
MetricsFmtUnbanOperations = " Unban Operations: %d (failures: %d)\n"
)