Contributing to Pipelock#
Thanks for your interest in making AI agents more secure.
Prerequisites#
- Go 1.25+ (
go version) - golangci-lint v2
- gofumpt (
go install mvdan.cc/gofumpt@latest)
Quick Start#
git clone https://github.com/luckyPipewrench/pipelock.git
cd pipelock
make build
make test
make lint
Development Workflow#
- Fork the repository on GitHub
- Clone your fork and create a feature branch
- Make changes with tests
- Run the pre-commit checklist (below)
- Open a PR against
main
Branch naming:
feat/for new featuresfix/for bug fixeschore/for maintenancedocs/for documentation
Pre-Commit Checklist#
These match exactly what CI checks. Both must pass with zero issues.
golangci-lint run ./... # Full lint (19 linters, see .golangci.yml)
go test -race -count=1 ./... # All tests with race detector
Pull Requests#
- Fill in a clear description of what changed and why
- CI runs 6 required checks: test (Go 1.25 + 1.26 matrix), lint, build, govulncheck, CodeQL, pipelock (self-scan)
- Address reviewer feedback and bot comments (CodeRabbit reviews automatically)
- PRs are squash-merged
Testing#
Requirements#
- All tests run with
-race -count=1 - Target 95%+ coverage on new code (
make test-coverfor local report) - Table-driven tests where there are 3+ cases
Patterns#
Disable SSRF in unit tests to avoid DNS lookups:
cfg := config.Defaults()
cfg.Internal = nil // disables SSRF checks
CLI tests capture output via SetOut, never os.Pipe:
var buf strings.Builder
cmd.SetOut(&buf)
Build fake credentials at runtime to avoid gitleaks false positives:
key := "sk-ant-" + "api03-" + "XXXXXXXXXXXX"
Benchmarks#
make bench
See docs/benchmarks.md for methodology and results.
Code Style#
- gofumpt formatting, not just gofmt (CI enforces this)
- Error wrapping:
fmt.Errorf("context: %w", err) - No stutter:
proxy.Optionnotproxy.ProxyOption cmd.OutOrStdout()for CLI output,cmd.ErrOrStderr()for diagnostics- File permissions:
0o600not0600 - HTTP methods:
http.MethodGetnot"GET" - See .golangci.yml for all 19 enabled linters
Building#
make build # Build with version metadata
make test # Run tests
make lint # Lint
make docker # Build Docker image
Project Structure#
cmd/pipelock/ CLI entry point
internal/
cli/ 20+ Cobra commands (run, check, report, tls, mcp, audit, generate, ...)
config/ YAML config loading, validation, defaults, hot-reload (fsnotify)
scanner/ 11-layer URL scanning pipeline + response injection detection
audit/ Structured JSON audit logging (zerolog) + event emission dispatch
proxy/ HTTP proxy: fetch, forward (CONNECT), WebSocket, TLS interception
certgen/ ECDSA P-256 CA + leaf certificate generation, cache
mcp/ MCP proxy + bidirectional scanning + tool poisoning + chains
report/ HTML/JSON audit report generation from JSONL event logs
killswitch/ Emergency deny-all (4 sources) + port-isolated API
emit/ Event emission (webhook + syslog sinks)
metrics/ Prometheus metrics + JSON stats endpoint
normalize/ Unicode normalization (NFKC, confusables, combining marks)
hitl/ Human-in-the-loop terminal approval
integrity/ File integrity monitoring (SHA256 manifests)
signing/ Ed25519 key management, file signing, signature verification
gitprotect/ Git diff scanning for secrets
projectscan/ Project directory scanner for audit command
receipt/ Action receipt signing + hash-chained evidence
addressprotect/ Blockchain address validation and poisoning detection
seedprotect/ BIP-39 seed phrase detection (dictionary, checksum)
shield/ Airlock, browser shield, posture capsule
rules/ Community rule bundle loading, verification, and CLI
enterprise/ Multi-agent features (ELv2, see enterprise/LICENSE)
configs/ 7 preset config files (balanced, strict, audit, claude-code, cursor, generic-agent, hostile-model)
docs/ Guides, OWASP mapping, comparison
Architecture#
See CLAUDE.md for the full architecture guide, including:
- Scanner pipeline (11 layers)
- MCP proxy design
- Config system and hot-reload
- Package structure and conventions
Adding Features#
New CLI command#
- Create
internal/cli/<command>.gowith a<command>Cmd()function - Register in
rootCmd()ininternal/cli/root.go - Add tests in
internal/cli/<command>_test.go
New scanner layer#
- Add the check function in
internal/scanner/ - Wire into
Scanner.Scan()pipeline - Add metrics counter in
internal/metrics/ - Add audit event in
internal/audit/ - Add benchmarks in
internal/scanner/scanner_bench_test.go
New DLP pattern#
- Add regex to
config.Defaults()ininternal/config/config.go - Add test cases in
internal/scanner/scanner_test.go - Update preset configs in
configs/
Dependencies#
Pipelock keeps its direct dependency set intentionally small. Any new dependency must be justified in the PR description. We prefer the standard library.
Security#
- Vulnerabilities: Report via GitHub Security Advisories, NOT public issues
- Don't weaken capability separation: the proxy must never access agent secrets
- Don't bypass fail-closed defaults: if in doubt, block
- See SECURITY.md for the full policy
Reporting Issues#
- Security issues: See SECURITY.md
- Bugs: Open a GitHub issue with steps to reproduce
- Features: Open a GitHub issue describing the use case
- Scanner bypasses: Use the security bypass issue template
Contributor License Agreement#
We use a Contributor License Agreement (CLA) for all contributions. When you open your first PR, the CLA Assistant bot will ask you to sign electronically. This is a one-time process that takes about 30 seconds.
The CLA is based on the Apache Individual Contributor License Agreement. It grants the project the right to use your contribution under the project's license terms while you retain ownership of your work.
License#
Pipelock uses two licenses:
- Apache License 2.0 for the core (files without the
enterprisebuild tag) - Elastic License 2.0 (ELv2) for paid multi-agent features (files with the
//go:build enterprisebuild tag, primarily inenterprise/with integration code incmd/andinternal/)
By contributing to the core, you agree that your contributions will be licensed under the Apache License 2.0. Contributions to enterprise-licensed code are licensed under the Elastic License 2.0.
Each file's license is indicated by its header. When in doubt, check the file's build tag and license notice.