Contributing to Secure Proxy Manager

September 9, 2026 · View on GitHub

Thank you for your interest! We welcome contributions — bug fixes, features, documentation, and testing.

Quick Setup

git clone https://github.com/fabriziosalmi/secure-proxy-manager.git
cd secure-proxy-manager
cp .env.example .env          # edit credentials
docker compose up -d --build  # start all services
./tests/e2e.sh localhost admin your-password  # run tests

Tech Stack

ComponentTechnologyDirectory
BackendGo 1.24 (chi, zerolog, modernc/sqlite)backend-go/
WAF EngineGo (ICAP server, regex + heuristics)waf-go/
FrontendReact 19, Vite, TypeScript, Tailwind CSSui/
ProxySquid 5.xproxy/
DNSdnsmasqdns/
InfraDocker Compose, Nginxdocker-compose.yml, ui/

Development Workflow

Backend (Go)

cd backend-go
go build ./...              # compile
go test -race ./...         # unit tests — CI gates on these AND on 60% coverage
go vet ./...                # static analysis

WAF Engine (Go)

cd waf-go
go build ./...
go test -v -race ./...      # unit + fuzz tests

Frontend (React/TypeScript)

cd ui
npm ci                      # install deps
npm test                    # Vitest suites — CI gates on these
npm run lint                # ESLint — CI gates on this
npm run build               # build (includes tsc check)
npx tsc --noEmit            # type check only

Lint (both Go modules)

golangci-lint run           # from backend-go/ or waf-go/ — CI gates on this
shellcheck --severity=warning proxy/*.sh deploy/*.sh scripts/*.sh tests/*.sh

Full Stack (Docker)

docker compose build --no-cache web backend waf
docker compose up -d
docker compose logs -f backend  # watch Go backend logs

Coding Standards

Go

  • gofmt formatting (enforced by editor)
  • Error handling: always check and handle errors, no _ = err
  • Naming: camelCase for unexported, PascalCase for exported
  • No global mutable state — use dependency injection
  • SQL: parameterized queries only, never interpolate user input

TypeScript/React

  • Strict mode ("strict": true in tsconfig)
  • Typed interfaces in ui/src/types.ts — no any in business logic
  • Functional components with hooks
  • @tanstack/react-query for all API calls
  • Tailwind CSS for styling — no inline styles

Commit Messages

feat: short description (#issue)
fix: short description
docs: short description
chore: short description

Reproducing the CI gates locally

Everything below blocks a merge. Running them before opening a PR avoids a round trip:

CommandGate
cd ui && npm run lint && npm test && npm run buildUI lint, tests, build
cd backend-go && go build ./... && go vet ./... && golangci-lint runGo build, vet, lint
cd backend-go && go test -race ./...Backend tests + 60% coverage floor
cd waf-go && go test -race ./...WAF tests + 70% coverage floor
shellcheck --severity=warning proxy/*.sh deploy/*.sh scripts/*.sh tests/*.shShell lint
ruff check proxy/ mcp/Python lint
pytest tests/python -qPython tests (watchdog)
bash tests/shell/restore_test.shRestore drill
docker compose -f docker-compose.yml config -qCompose validation
cd backend-go && gosec -severity medium -confidence medium ./...Security scanning (gosec half)
bash tests/shell/generate_squid_conf_test.shSquid config generation
make adversarialAdversarial block-matrix (the suite README leads with)
bash scripts/check-version-sync.shVersion consistency
bash scripts/check-config-contract.sh/config filename contract (Go/Python/shell)

The remaining required checks run only in CI because they need to build and start the whole stack: the image build, the compose-up smoke test, the Playwright E2E suite, and Trivy. Their absence from this table is deliberate, not an omission — everything above is cheap enough to run before every push.

E2E Testing

Prerequisite: the stack must already be running and reachable — this drives a live deployment, it does not start one. docker compose up -d first.

# Run full suite (104 checks) against a running stack
./tests/e2e.sh <host> <user> <password>   # host defaults to localhost

# Example
./tests/e2e.sh localhost admin mypassword
./tests/e2e.sh 10.0.0.5 admin mypassword   # a remote deployment

The test suite covers:

  • Part A: Client-side (proxy connectivity, 17 WAF attack vectors, 7 false positives, protocol hardening, latency)
  • Part B: Admin-side (auth, 9 analytics endpoints, CRUD, settings, toggles, database)
  • Part C: Advanced (settings persistence, body validation, WAF evasion, concurrent stress, error handling)

Pull Request Process

  1. Fork the repo and create a feature branch
  2. Make your changes with clear commit messages
  3. Ensure go build ./... passes for Go changes
  4. Ensure npm run build passes for frontend changes
  5. Run E2E tests if possible
  6. Open a PR with description of what and why

What blocks a merge

main requires a pull request and 17 green checks. The list is not a convention — it is .github/branch-protection.json, applied to GitHub and checkable against it:

scripts/branch-protection.sh verify   # diff the live protection against the file
scripts/branch-protection.sh apply    # push the file's state to GitHub (needs admin:repo)

Everything in that list is deterministic and derived from the code, so a red check means the PR broke something. Two checks run but deliberately do not gate: Verify popular list URLs reaches third-party hosts, where an upstream outage would block every unrelated merge, and the CodeQL Analyze jobs are GitHub-managed, where a change to the analysed language set would leave a required check pending forever. Their findings still surface — in the job log and in the Security tab.

apply restores everything except one field: GitHub's branch-protection API accepts allow_force_pushes: false and silently leaves it enabled. verify compares it, so the gap shows up as drift; fix that one under Settings → Branches → main.

strict is on, so a PR must be up to date with main before it merges. If Dependabot churn makes that painful, that is the one setting to relax; the required-check list is not.

Reporting Issues

Community

License

MIT — see LICENSE