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}] [{Makefile,go.mod,go.sum}]
indent_style = tab indent_style = tab
[*.md]
indent_size = unset
[.github/renovate.json] [.github/renovate.json]
max_line_length = off max_line_length = off

View File

@@ -4,7 +4,6 @@ about: Create a report to help us improve
title: '' title: ''
labels: bug labels: bug
assignees: ivuorinen assignees: ivuorinen
--- ---
**Describe the bug** **Describe the bug**
@@ -14,9 +13,9 @@ A clear and concise description of what the bug is.
Steps to reproduce the behavior: Steps to reproduce the behavior:
1. Run command: `f2b [command]` 1. Run command: `f2b [command]`
2. With arguments: `[arguments]` 1. With arguments: `[arguments]`
3. Expected behavior: `[what should happen]` 1. Expected behavior: `[what should happen]`
4. Actual result: `[what actually happened]` 1. Actual result: `[what actually happened]`
**Expected behavior** **Expected behavior**
A clear and concise description of what you expected to happen. 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: '' title: ''
labels: enhancement labels: enhancement
assignees: ivuorinen assignees: ivuorinen
--- ---
**Is your feature request related to a problem? Please describe.** **Is your feature request related to a problem? Please describe.**

View File

@@ -1,12 +1,12 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$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": [ "customManagers": [
{ {
"customType": "regex", "customType": "regex",
"fileMatch": ["^Makefile$", "\\.mk$"], "managerFilePatterns": ["^Makefile$", ".*\\.mk$"],
"matchStrings": [ "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" "versioningTemplate": "semver"
} }
@@ -15,7 +15,7 @@
"packageRules": [ "packageRules": [
{ {
"matchManagers": ["custom.regex"], "matchManagers": ["custom.regex"],
"matchFileNames": ["Makefile", "*.mk"], "matchFileNames": ["Makefile", "**/*.mk"],
"groupName": "development tools", "groupName": "development tools",
"schedule": ["before 6am on monday"] "schedule": ["before 6am on monday"]
} }

View File

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

View File

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

View File

@@ -35,7 +35,7 @@ jobs:
node-version: 24.x node-version: 24.x
- name: Set up Go - name: Set up Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with: with:
go-version-file: go.mod go-version-file: go.mod
cache: true cache: true
@@ -46,6 +46,9 @@ jobs:
with: with:
python-version: "3.x" python-version: "3.x"
- name: Install Python dependencies
run: pip install pyyaml
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with: with:
@@ -58,7 +61,7 @@ jobs:
go test -race -covermode=atomic -coverprofile=coverage.out ./... go test -race -covermode=atomic -coverprofile=coverage.out ./...
- name: Upload coverage report - name: Upload coverage report
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with: with:
name: coverage-report name: coverage-report
path: coverage.out path: coverage.out
@@ -66,4 +69,4 @@ jobs:
- name: Run PR Lint - name: Run PR Lint
# Custom PR linting action that performs additional PR-specific checks # Custom PR linting action that performs additional PR-specific checks
# https://github.com/ivuorinen/actions # 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 fetch-depth: 0 # Required for changelog generation
- name: Set up Go - name: Set up Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with: with:
go-version-file: go.mod go-version-file: go.mod
cache: true cache: true
- name: Install GoReleaser - name: Install GoReleaser
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
with: with:
install-only: true install-only: true
version: "~> v2" version: "~> v2"
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Run GoReleaser - name: Run GoReleaser
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
with: with:
version: "~> v2" version: "~> v2"
args: release --clean args: release --clean
@@ -52,7 +52,7 @@ jobs:
# HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} # HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
- name: Upload Release Assets - name: Upload Release Assets
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with: with:
name: release-artifacts name: release-artifacts
path: dist/ path: dist/

View File

@@ -23,4 +23,4 @@ jobs:
issues: write issues: write
pull-requests: write pull-requests: write
steps: 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 contents: read
issues: write issues: write
steps: 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 !dist/.gitkeep
# Anonymous test data from real fail2ban logs # Anonymous test data from real fail2ban logs
!fail2ban/testdata/* !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 SARIF_REPORTER: true # Generate SARIF report
SHOW_SKIPPED_LINTERS: false # Show skipped linters in MegaLinter log SHOW_SKIPPED_LINTERS: false # Show skipped linters in MegaLinter log
FILTER_REGEX_EXCLUDE: '(\.serena/)'
DISABLE_LINTERS: DISABLE_LINTERS:
- REPOSITORY_DEVSKIM - REPOSITORY_DEVSKIM
- GO_REVIVE # run as part of golangci-lint - GO_REVIVE # run as part of golangci-lint

View File

@@ -25,13 +25,13 @@ repos:
hooks: hooks:
- id: golangci-lint - id: golangci-lint
name: 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 language: system
types: [go] types: [go]
pass_filenames: false pass_filenames: false
- repo: https://github.com/google/yamlfmt - repo: https://github.com/google/yamlfmt
rev: v0.20.0 rev: v0.21.0
hooks: hooks:
- id: yamlfmt - id: yamlfmt
@@ -48,7 +48,7 @@ repos:
args: [-q, -c, .markdown-link-check.json] args: [-q, -c, .markdown-link-check.json]
- repo: https://github.com/rhysd/actionlint - repo: https://github.com/rhysd/actionlint
rev: v1.7.9 rev: v1.7.11
hooks: hooks:
- id: actionlint - id: actionlint
args: ["-shellcheck="] args: ["-shellcheck="]
@@ -59,26 +59,34 @@ repos:
- id: shfmt - id: shfmt
- repo: https://github.com/checkmake/checkmake - repo: https://github.com/checkmake/checkmake
rev: 0.2.2 rev: v0.3.2
hooks: hooks:
- id: checkmake - id: checkmake
name: Makefile Linter name: Makefile Linter
files: ^Makefile$ files: ^Makefile$
- repo: https://github.com/bridgecrewio/checkov.git - repo: https://github.com/bridgecrewio/checkov.git
rev: "3.2.495" rev: "3.2.506"
hooks: hooks:
- id: checkov - id: checkov
args: args:
- "--quiet" - "--quiet"
- repo: https://github.com/python-jsonschema/check-jsonschema - repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.36.0 rev: 0.37.0
hooks: hooks:
- id: check-github-workflows - id: check-github-workflows
args: ["--verbose"] args: ["--verbose"]
- repo: https://github.com/editorconfig-checker/editorconfig-checker - repo: https://github.com/editorconfig-checker/editorconfig-checker
rev: v3.6.0 rev: v3.6.1
hooks: hooks:
- id: editorconfig-checker - 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
--- ______________________________________________________________________
## [Unreleased] ## [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. - Various minor bug fixes and improved test coverage.
- **Test safety**: Eliminated potential for real sudo execution during testing - **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. **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.** **📋 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 Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity). 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 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/faq>. Translations are available at
<https://www.contributor-covenant.org/translations>. <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, 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. whether you are fixing bugs, adding features, improving documentation, or helping others.
--- ______________________________________________________________________
## How to Contribute ## How to Contribute
@@ -48,7 +48,7 @@ go test ./...
- Describe your changes, reference related issues, and explain any design decisions. - Describe your changes, reference related issues, and explain any design decisions.
- Be ready to discuss and revise your code based on feedback. - Be ready to discuss and revise your code based on feedback.
--- ______________________________________________________________________
## Code Style ## Code Style
@@ -61,7 +61,7 @@ go test ./...
- Handle sudo privileges securely - validate before escalation, use mocks in tests. - Handle sudo privileges securely - validate before escalation, use mocks in tests.
- Use argument arrays for command execution, never shell string concatenation. - Use argument arrays for command execution, never shell string concatenation.
--- ______________________________________________________________________
## Security & Testing Guidelines ## 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 - [docs/testing.md](docs/testing.md) - Testing strategies and mock patterns
- [AGENTS.md](AGENTS.md) - AI/LLM contributor guidelines - [AGENTS.md](AGENTS.md) - AI/LLM contributor guidelines
--- ______________________________________________________________________
## Communication ## Communication
@@ -86,7 +86,7 @@ For comprehensive security guidelines, testing patterns, and examples, see:
- Review the [Code of Conduct](CODE_OF_CONDUCT.md). - 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. - For large or breaking changes, open an issue to discuss your approach before submitting a PR.
--- ______________________________________________________________________
## Additional Notes ## 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. - 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. - By contributing, you agree that your contributions will be licensed under the MIT License.
--- ______________________________________________________________________
Thank you for helping make **f2b** better! Thank you for helping make **f2b** better!
[effective_go]: https://golang.org/doc/effective_go.html
[contributing](CONTRIBUTING.md) [contributing](CONTRIBUTING.md)
[effective_go]: https://golang.org/doc/effective_go.html

200
Makefile
View File

@@ -1,10 +1,36 @@
# f2b Makefile # f2b Makefile
.PHONY: help all build test lint fmt clean install dev-deps ci \ .PHONY: help all build test lint fmt clean install
check-deps test-verbose test-coverage update-deps \ .PHONY: ci ci-coverage test-verbose test-coverage update-deps fmt-md
lint-go lint-md lint-yaml lint-actions lint-make \ .PHONY: lint-go lint-md lint-yaml lint-actions lint-make
ci ci-coverage security dev-setup pre-commit-setup \ .PHONY: security dev-setup pre-commit-setup
release-dry-run release release-snapshot release-check _check-tag .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 # Default target
help: ## Show this help message 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) @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
all: ci ## Run all CI checks (same as ci target) all: ci ## Run all CI checks (same as ci target)
@echo "All checks completed" @echo "All checks completed"
# Build targets # Build targets
build: ## Build the f2b binary build: ## Build the f2b binary
@@ -23,107 +49,6 @@ build: ## Build the f2b binary
install: ## Install f2b globally install: ## Install f2b globally
go install github.com/ivuorinen/f2b@latest 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 # Testing targets
test: ## Run all tests test: ## Run all tests
go test ./... go test ./...
@@ -134,42 +59,38 @@ test-verbose: ## Run tests with verbose output
test-coverage: ## Run tests with coverage report test-coverage: ## Run tests with coverage report
go test -coverprofile=coverage.out ./... go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html 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 update-deps: ## Update Go dependencies to latest patch versions
@echo "Updating Go dependencies (patch versions only)..."
go get -u=patch ./... go get -u=patch ./...
go mod tidy go mod tidy
go mod verify go mod verify
@echo "Dependencies updated ✓"
@echo "Updated dependencies:"
@go list -u -m all | grep '\[' || true @go list -u -m all | grep '\[' || true
# Code quality targets # Code quality targets
fmt: ## Format Go code fmt: ## Format Go code
gofmt -w . 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) lint: ## Run all linters using pre-commit (preferred method)
@echo "Running pre-commit linters..."
@pre-commit run --all-files @pre-commit run --all-files
@echo "All linting completed ✓"
lint-go: ## Run only Go linters lint-go: ## Run only Go linters
go vet ./... 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 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 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 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 lint-make: ## Run only Makefile linter
checkmake Makefile go run github.com/checkmake/checkmake/cmd/checkmake@$(CHECKMAKE_VERSION) Makefile
# CI targets # CI targets
ci: fmt lint test ## Run all CI checks (format, lint, test) 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 targets
security: ## Run security checks security: ## Run security checks
gosec ./... go run github.com/securego/gosec/v2/cmd/gosec@$(GOSEC_VERSION) ./...
# Cleanup targets # Cleanup targets
clean: ## Clean build artifacts clean: ## Clean build artifacts
rm -f f2b rm -f f2b coverage.out coverage.html
rm -f coverage.out
rm -f coverage.html
go clean go clean
# Development targets # Development targets
dev-setup: dev-deps ## Set up development environment dev-setup: pre-commit-setup ## 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 ✓"
pre-commit-setup: ## Install and configure pre-commit hooks pre-commit-setup: ## Install and configure pre-commit hooks
@echo "Installing pre-commit..." @command -v pre-commit >/dev/null 2>&1 || pip install pre-commit
@command -v pre-commit >/dev/null 2>&1 || { \
echo "Installing pre-commit..."; \
pip install pre-commit; \
}
@pre-commit install @pre-commit install
@echo "Pre-commit hooks installed ✓"
# Release targets # Release targets
release-dry-run: ## Test release process without creating artifacts 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"); \ @VERSION=$$(git describe --tags --exact-match 2>/dev/null || echo "v0.0.0-dev"); \
echo "Building version: $$VERSION"; \ echo "Building version: $$VERSION"; \
go build -ldflags "-X github.com/ivuorinen/f2b/cmd.version=$$VERSION" -o f2b-test . go build -ldflags "-X github.com/ivuorinen/f2b/cmd.version=$$VERSION" -o f2b-test . && rm -f f2b-test
@rm -f f2b-test
@echo "Release dry-run complete ✓"
release: ## Create a new release using GoReleaser release: _check-tag ## Create a new release using GoReleaser
@echo "Creating release with GoReleaser..." go run github.com/goreleaser/goreleaser/v2@$(GORELEASER_VERSION) release --clean
@$(MAKE) _check-tag
@goreleaser release --clean
_check-tag: ## Internal: Check if a git tag exists _check-tag: ## Internal: Check if a git tag exists
@if [ -z "$$(git describe --exact-match 2>/dev/null)" ]; then \ @if [ -z "$$(git describe --exact-match 2>/dev/null)" ]; then \
@@ -228,10 +129,7 @@ _check-tag: ## Internal: Check if a git tag exists
fi fi
release-snapshot: ## Create a snapshot release (no tag required) release-snapshot: ## Create a snapshot release (no tag required)
@echo "Creating snapshot release with GoReleaser..." go run github.com/goreleaser/goreleaser/v2@$(GORELEASER_VERSION) release --snapshot --clean
goreleaser release --snapshot --clean
release-check: ## Check if GoReleaser configuration is valid release-check: ## Check if GoReleaser configuration is valid
@echo "Checking GoReleaser configuration..." go run github.com/goreleaser/goreleaser/v2@$(GORELEASER_VERSION) check
goreleaser check
@echo "GoReleaser configuration is valid ✓"

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/) [![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) [![Build Status](https://img.shields.io/badge/tests-passing-brightgreen.svg)](https://github.com/ivuorinen/f2b/actions)
--- ______________________________________________________________________
## 🚀 Quick Start ## 🚀 Quick Start
@@ -79,7 +79,7 @@ make build
go build -ldflags "-X github.com/ivuorinen/f2b/cmd.version=1.2.3" -o f2b . go build -ldflags "-X github.com/ivuorinen/f2b/cmd.version=1.2.3" -o f2b .
``` ```
--- ______________________________________________________________________
## ✨ Key Features ## ✨ 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 - **Thread Safety**: Extensive race condition testing and protection
- **Security Audit Trail**: Comprehensive logging of all privileged operations - **Security Audit Trail**: Comprehensive logging of all privileged operations
--- ______________________________________________________________________
## 📋 Usage Examples ## 📋 Usage Examples
@@ -190,7 +190,7 @@ f2b completion fish > ~/.config/fish/completions/f2b.fish
f2b completion powershell | Out-String | Invoke-Expression f2b completion powershell | Out-String | Invoke-Expression
``` ```
--- ______________________________________________________________________
## ⚙️ Configuration ## ⚙️ 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 f2b --log-level=debug --log-file=/tmp/f2b-debug.log ban 192.168.1.100
``` ```
--- ______________________________________________________________________
## 🔐 Security & Privileges ## 🔐 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 For detailed security practices, threat model, and contribution security guidelines, see
[docs/security.md](docs/security.md). [docs/security.md](docs/security.md).
--- ______________________________________________________________________
## 📖 Complete Command Reference ## 📖 Complete Command Reference
@@ -322,7 +322,7 @@ For convenience, most commands have short aliases:
- `ban``banip`, `b` - `ban``banip`, `b`
- `unban``unbanip`, `ub` - `unban``unbanip`, `ub`
--- ______________________________________________________________________
## 🏗️ Architecture ## 🏗️ Architecture
@@ -338,8 +338,8 @@ f2b is built as an **enterprise-grade** Go application following modern architec
### 📊 **Quality Metrics** ### 📊 **Quality Metrics**
- **Test Coverage**: 76.8% (cmd/), 59.3% (fail2ban/) - Above industry standards - **Test Coverage**: Comprehensive coverage across all packages - above industry standards
- **Modern Testing**: Fluent testing framework reducing code duplication by 60-70% - **Modern Testing**: Fluent testing framework with significant reduction in test duplication
- **Security Testing**: 13 comprehensive attack vector test cases implemented - **Security Testing**: 13 comprehensive attack vector test cases implemented
- **Performance**: Context-aware operations with configurable timeouts and resource management - **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, For detailed architecture information, implementation patterns, and extension guidelines,
see [docs/architecture.md](docs/architecture.md). see [docs/architecture.md](docs/architecture.md).
--- ______________________________________________________________________
## 🧪 Development & Testing ## 🧪 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: Install the development dependencies and hooks:
```bash ```bash
make dev-deps make dev-setup
make pre-commit-setup
``` ```
Run all linters: Run all linters:
@@ -428,7 +427,7 @@ f2b logs-watch all --limit 20 | while read line; do
done done
``` ```
--- ______________________________________________________________________
## 🚀 Releases ## 🚀 Releases
@@ -438,19 +437,19 @@ Releases are automated using [GoReleaser](https://goreleaser.com/). To create a
1. **Tag the release:** 1. **Tag the release:**
```bash ```bash
git tag -a v1.2.3 -m "Release v1.2.3" git tag -a v1.2.3 -m "Release v1.2.3"
git push origin v1.2.3 git push origin v1.2.3
``` ```
2. **GitHub Actions will automatically:** 2. **GitHub Actions will automatically:**
- Build binaries for multiple platforms (Linux, macOS, Windows, BSD) - Build binaries for multiple platforms (Linux, macOS, Windows, BSD)
- Create a GitHub release with changelog - Create a GitHub release with changelog
- Upload release artifacts - Upload release artifacts
- Build and push Docker images - Build and push Docker images
- Update Homebrew tap (if configured) - Update Homebrew tap (if configured)
- Generate .deb, .rpm, and .apk packages - Generate .deb, .rpm, and .apk packages
### Manual Release (Development) ### Manual Release (Development)
@@ -476,7 +475,7 @@ Each release includes:
- Docker images at `ghcr.io/ivuorinen/f2b` with architecture-specific tags - Docker images at `ghcr.io/ivuorinen/f2b` with architecture-specific tags
- Linux packages (.deb, .rpm, .apk) for multiple architectures - Linux packages (.deb, .rpm, .apk) for multiple architectures
--- ______________________________________________________________________
## 🤝 Contributing ## 🤝 Contributing
@@ -500,25 +499,25 @@ Please see:
- [docs/security.md](docs/security.md) - Security practices and guidelines - [docs/security.md](docs/security.md) - Security practices and guidelines
- [docs/testing.md](docs/testing.md) - Testing strategies and patterns - [docs/testing.md](docs/testing.md) - Testing strategies and patterns
--- ______________________________________________________________________
## 📄 License ## 📄 License
[MIT License](LICENSE.md). [MIT License](LICENSE.md).
--- ______________________________________________________________________
## 👨‍💻 Author ## 👨‍💻 Author
**Ismo Vuorinen** ([@ivuorinen](https://github.com/ivuorinen)) **Ismo Vuorinen** ([@ivuorinen](https://github.com/ivuorinen))
--- ______________________________________________________________________
## 🆘 Support ## 🆘 Support
- 📝 [Open an issue](https://github.com/ivuorinen/f2b/issues) - 📝 [Open an issue](https://github.com/ivuorinen/f2b/issues)
- 📖 [Read the FAQ](docs/faq.md) - 📖 [Read the FAQ](docs/faq.md)
--- ______________________________________________________________________
_Built with ❤️ and Go. Securing systems one ban at a time._ _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) t.Fatalf("failed to create temp file: %v", err)
} }
defer func() { 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) 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 // System metrics
sb.WriteString("System:\n") sb.WriteString("System:\n")
sb.WriteString(fmt.Sprintf(" Uptime: %ds\n", snapshot.UptimeSeconds)) fmt.Fprintf(&sb, " Uptime: %ds\n", snapshot.UptimeSeconds)
sb.WriteString(fmt.Sprintf(" Max Memory: %.2f MB\n", float64(snapshot.MaxMemoryUsage)/(1024*1024))) fmt.Fprintf(&sb, " Max Memory: %.2f MB\n", float64(snapshot.MaxMemoryUsage)/(1024*1024))
sb.WriteString(fmt.Sprintf(" Goroutines: %d\n\n", snapshot.GoroutineCount)) fmt.Fprintf(&sb, " Goroutines: %d\n\n", snapshot.GoroutineCount)
// Command metrics // Command metrics
sb.WriteString("Commands:\n") sb.WriteString("Commands:\n")
sb.WriteString(fmt.Sprintf(shared.MetricsFmtTotalExecutions, snapshot.CommandExecutions)) fmt.Fprintf(&sb, shared.MetricsFmtTotalExecutions, snapshot.CommandExecutions)
sb.WriteString(fmt.Sprintf(shared.MetricsFmtTotalFailures, snapshot.CommandFailures)) fmt.Fprintf(&sb, shared.MetricsFmtTotalFailures, snapshot.CommandFailures)
if snapshot.CommandExecutions > 0 { if snapshot.CommandExecutions > 0 {
avgLatency := float64(snapshot.CommandTotalDuration) / float64(snapshot.CommandExecutions) avgLatency := float64(snapshot.CommandTotalDuration) / float64(snapshot.CommandExecutions)
sb.WriteString(fmt.Sprintf(shared.MetricsFmtAverageLatencyTop, avgLatency)) fmt.Fprintf(&sb, shared.MetricsFmtAverageLatencyTop, avgLatency)
} }
sb.WriteString("\n") sb.WriteString("\n")
// Ban/Unban metrics // Ban/Unban metrics
sb.WriteString("Ban Operations:\n") sb.WriteString("Ban Operations:\n")
sb.WriteString(fmt.Sprintf(" Ban Operations: %d (failures: %d)\n", snapshot.BanOperations, snapshot.BanFailures)) fmt.Fprintf(&sb, shared.MetricsFmtBanOperations, snapshot.BanOperations, snapshot.BanFailures)
sb.WriteString( fmt.Fprintf(&sb, shared.MetricsFmtUnbanOperations, snapshot.UnbanOperations, snapshot.UnbanFailures)
fmt.Sprintf(" Unban Operations: %d (failures: %d)\n", snapshot.UnbanOperations, snapshot.UnbanFailures),
)
sb.WriteString("\n") sb.WriteString("\n")
// Client metrics // Client metrics
sb.WriteString("Client Operations:\n") sb.WriteString("Client Operations:\n")
sb.WriteString(fmt.Sprintf(shared.MetricsFmtTotalOperations, snapshot.ClientOperations)) fmt.Fprintf(&sb, shared.MetricsFmtTotalOperations, snapshot.ClientOperations)
sb.WriteString(fmt.Sprintf(shared.MetricsFmtTotalFailures, snapshot.ClientFailures)) fmt.Fprintf(&sb, shared.MetricsFmtTotalFailures, snapshot.ClientFailures)
if snapshot.ClientOperations > 0 { if snapshot.ClientOperations > 0 {
avgLatency := float64(snapshot.ClientTotalDuration) / float64(snapshot.ClientOperations) avgLatency := float64(snapshot.ClientTotalDuration) / float64(snapshot.ClientOperations)
sb.WriteString(fmt.Sprintf(shared.MetricsFmtAverageLatencyTop, avgLatency)) fmt.Fprintf(&sb, shared.MetricsFmtAverageLatencyTop, avgLatency)
} }
sb.WriteString("\n") sb.WriteString("\n")
// Validation metrics // Validation metrics
sb.WriteString("Validation:\n") sb.WriteString("Validation:\n")
sb.WriteString(fmt.Sprintf(" Cache Hits: %d\n", snapshot.ValidationCacheHits)) fmt.Fprintf(&sb, " Cache Hits: %d\n", snapshot.ValidationCacheHits)
sb.WriteString(fmt.Sprintf(" Cache Misses: %d\n", snapshot.ValidationCacheMiss)) fmt.Fprintf(&sb, " Cache Misses: %d\n", snapshot.ValidationCacheMiss)
sb.WriteString(fmt.Sprintf(" Failures: %d\n", snapshot.ValidationFailures)) fmt.Fprintf(&sb, " Failures: %d\n", snapshot.ValidationFailures)
if total := snapshot.ValidationCacheHits + snapshot.ValidationCacheMiss; total > 0 { if total := snapshot.ValidationCacheHits + snapshot.ValidationCacheMiss; total > 0 {
hitRate := float64(snapshot.ValidationCacheHits) / float64(total) * 100 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") sb.WriteString("\n")

View File

@@ -240,7 +240,7 @@ const (
The configuration system supports the following environment variables: The configuration system supports the following environment variables:
| Variable | Description | Default | | Variable | Description | Default |
| -------- | ----------- | ------- | | ---------------------- | -------------------------- | ------------------------ |
| `F2B_LOG_DIR` | Log directory path | `/var/log` | | `F2B_LOG_DIR` | Log directory path | `/var/log` |
| `F2B_FILTER_DIR` | Filter directory path | `/etc/fail2ban/filter.d` | | `F2B_FILTER_DIR` | Filter directory path | `/etc/fail2ban/filter.d` |
| `F2B_LOG_LEVEL` | Log level | `info` | | `F2B_LOG_LEVEL` | Log level | `info` |
@@ -550,30 +550,30 @@ func (h *HTTPHandler) writeError(w http.ResponseWriter, code int, err error) {
### Error Handling Best Practices ### Error Handling Best Practices
1. Always use contextual errors for user-facing messages 1. Always use contextual errors for user-facing messages
2. Provide remediation hints where possible 1. Provide remediation hints where possible
3. Log errors with appropriate context 1. Log errors with appropriate context
4. Use error categories for systematic handling 1. Use error categories for systematic handling
### Context Usage ### Context Usage
1. Always use context for operations that can timeout 1. Always use context for operations that can timeout
2. Propagate context through the call chain 1. Propagate context through the call chain
3. Add relevant context values for logging 1. Add relevant context values for logging
4. Use context cancellation for cleanup 1. Use context cancellation for cleanup
### Testing ### Testing
1. Use the fluent testing framework for command tests 1. Use the fluent testing framework for command tests
2. Always use mock environments for integration tests 1. Always use mock environments for integration tests
3. Test both success and failure scenarios 1. Test both success and failure scenarios
4. Include timeout testing for long-running operations 1. Include timeout testing for long-running operations
### Performance ### Performance
1. Use the metrics system to monitor performance 1. Use the metrics system to monitor performance
2. Implement proper caching where appropriate 1. Implement proper caching where appropriate
3. Use object pooling for frequently allocated objects 1. Use object pooling for frequently allocated objects
4. Profile and optimize hot paths 1. Profile and optimize hot paths
This documentation provides a comprehensive overview of the f2b internal APIs and patterns. 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. 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 ### fail2ban/ Package
- **Purpose**: Core business logic and system interaction - **Purpose**: Core business logic and system interaction
- **Key Interfaces**: - **Key Interfaces**:
- `Client`: Main interface for fail2ban operations with context support - `Client`: Main interface for fail2ban operations with context support
- `Runner`: Command execution interface - `Runner`: Command execution interface
- `SudoChecker`: Privilege validation interface - `SudoChecker`: Privilege validation interface
- **Implementations**: - **Implementations**:
- `RealClient`: Production fail2ban client with timeout handling - `RealClient`: Production fail2ban client with timeout handling
- `MockClient`: Comprehensive test double with thread-safe operations - `MockClient`: Comprehensive test double with thread-safe operations
- `NoOpClient`: Safe fallback implementation - `NoOpClient`: Safe fallback implementation
- **Advanced Features**: - **Advanced Features**:
- Context-aware operations with timeout and cancellation support - Context-aware operations with timeout and cancellation support
- Validation caching system with thread-safe operations - Validation caching system with thread-safe operations
- Optimized ban record parsing with object pooling - Optimized ban record parsing with object pooling
@@ -105,14 +109,14 @@ validation caching, and parallel processing capabilities for enterprise-grade re
### Command Execution Flow ### Command Execution Flow
1. **CLI Parsing**: Cobra processes command-line arguments 1. **CLI Parsing**: Cobra processes command-line arguments
2. **Context Creation**: Create context with timeout for operation 1. **Context Creation**: Create context with timeout for operation
3. **Validation**: Input validation with caching and sanitization 1. **Validation**: Input validation with caching and sanitization
4. **Privilege Check**: Determine if sudo is required 1. **Privilege Check**: Determine if sudo is required
5. **Metrics Start**: Begin performance metrics collection 1. **Metrics Start**: Begin performance metrics collection
6. **Business Logic**: Execute fail2ban operations via Client interface with context 1. **Business Logic**: Execute fail2ban operations via Client interface with context
7. **Parallel Processing**: Use parallel workers for multi-jail operations 1. **Parallel Processing**: Use parallel workers for multi-jail operations
8. **Metrics End**: Record operation timing and success/failure 1. **Metrics End**: Record operation timing and success/failure
9. **Output**: Format and display results (plain or JSON) 1. **Output**: Format and display results (plain or JSON)
### Dependency Flow ### Dependency Flow
@@ -170,25 +174,25 @@ fail2ban/client.go
### Adding New Commands ### Adding New Commands
1. Create new file in `cmd/` package 1. Create new file in `cmd/` package
2. Implement command using established patterns with context support 1. Implement command using established patterns with context support
3. Use dependency injection for testability 1. Use dependency injection for testability
4. Add performance metrics collection 1. Add performance metrics collection
5. Implement fluent testing framework patterns 1. Implement fluent testing framework patterns
6. Add comprehensive tests with mocks and context-aware operations 1. Add comprehensive tests with mocks and context-aware operations
### Adding New Backends ### Adding New Backends
1. Implement the `Client` interface 1. Implement the `Client` interface
2. Add any new required interfaces (Runner, etc.) 1. Add any new required interfaces (Runner, etc.)
3. Update main.go to support new backend 1. Update main.go to support new backend
4. Add configuration options 1. Add configuration options
### Adding New Output Formats ### Adding New Output Formats
1. Extend output formatting helpers 1. Extend output formatting helpers
2. Update command implementations 1. Update command implementations
3. Add format validation 1. Add format validation
4. Test with existing commands 1. Test with existing commands
## Testing Architecture ## Testing Architecture

View File

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

View File

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

View File

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

View File

@@ -604,11 +604,11 @@ go tool cover -func=coverage.out | grep total
### Avoid These Mistakes ### Avoid These Mistakes
1. **Real sudo execution in tests** - Always use MockSudoChecker 1. **Real sudo execution in tests** - Always use MockSudoChecker
2. **Hardcoded file paths** - Use temporary files or mocks 1. **Hardcoded file paths** - Use temporary files or mocks
3. **Network dependencies** - Mock all external calls 1. **Network dependencies** - Mock all external calls
4. **Race conditions** - Use proper synchronization in concurrent tests 1. **Race conditions** - Use proper synchronization in concurrent tests
5. **Leaked goroutines** - Clean up background processes 1. **Leaked goroutines** - Clean up background processes
6. **Platform dependencies** - Write portable tests 1. **Platform dependencies** - Write portable tests
### Enhanced Security Testing Checklist ### 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 { if err := validateCommandExecution(ctx, name, args); err != nil {
return nil, err return nil, err
} }
// #nosec G204 -- command validated by validateCommandExecution
return exec.CommandContext(ctx, name, args...).CombinedOutput() 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 already root, no need for sudo
if checker.IsRoot() { 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 // 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 // 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. // 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 // Check if this is a sudo privilege error
if strings.Contains(err.Error(), "fail2ban operations require sudo privileges") { 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, "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) os.Exit(1)
} }

View File

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

View File

@@ -499,4 +499,10 @@ const (
// MetricsFmtAverageLatencyTop is the format for average latency (top-level) // MetricsFmtAverageLatencyTop is the format for average latency (top-level)
MetricsFmtAverageLatencyTop = " Average Latency: %.2f ms\n" 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"
) )