This commit establishes the foundation for a comprehensive security audit tool for macOS developer machines. It audits all installed software, detects vulnerabilities via OSV.dev, and provides Claude-powered mitigation recommendations. ## Architecture - System inventory collection (Homebrew, pip, npm, Go, Rust, apps) - CVE scanning via OSV.dev API (no auth, unlimited rate limits) - Claude integration (CLI or SDK) for mitigation analysis - JSON + HTML report generation - Full test suite (34+ tests, 7 benchmarks) - Production-grade linting (11 linters via golangci-lint) - Vulnerability scanning (govulncheck) - GitHub Actions CI/CD pipeline ## Key Components - cmd/audit/: CLI entry point - internal/inventory/: System inventory parsing - internal/security/: OSV.dev querying, Claude integration, audit logic - internal/report/: JSON and HTML report generation - scripts/collect-inventory.sh: Bash script for system enumeration ## MVP Features - Homebrew package scanning - Python/Node/Go/Rust ecosystem scanning - CVSS-based severity filtering - Claude-powered recommendations (update/replace/protect/monitor) - Makefile automation (test, lint, vulnerability checks) - Pre-commit hooks configuration ## Testing & Quality - 34+ unit tests with table-driven patterns - 7 benchmark tests for performance - 11 configured linters (staticcheck, gosec, revive, etc.) - Code coverage reporting - GitHub Actions CI (tests on Go 1.22 & 1.23) - Pre-commit hook framework ## Documentation - README.md: Full feature and usage documentation - QUICKSTART.md: 3-minute setup guide - TESTING.md: Testing and code quality guide - QA_SETUP.md: Analysis tooling reference - DATA_SOURCES_SPEC.md: Vulnerability data source documentation ## Data Sources - OSV.dev: Primary CVE database (1.8 day latency, no auth needed) - GitHub Advisories: Supplement for maintainer-created advisories - OpenSSF Scorecard: Trust signals (repo health/practices) - Homebrew Formulae API: Package metadata - CISA KEV: Active exploitation tracking ## Next Steps Phase 1 (MVP): Complete and tested ✓ Phase 2 (planned): Third-party app scanning, VirusTotal integration Phase 3 (planned): Historical monitoring, scheduled audits, webhooks Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
330 lines
6.3 KiB
Markdown
330 lines
6.3 KiB
Markdown
# Testing & Code Quality Guide
|
|
|
|
This project uses comprehensive testing and static analysis to ensure code quality and reliability.
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Install analysis tools
|
|
make install-tools
|
|
|
|
# Run all checks (format, lint, test, vulnerability scan)
|
|
make ci
|
|
|
|
# View coverage report
|
|
make coverage && open coverage.html
|
|
```
|
|
|
|
## Testing
|
|
|
|
### Run All Tests
|
|
|
|
```bash
|
|
make test
|
|
```
|
|
|
|
### Run with Coverage
|
|
|
|
```bash
|
|
make coverage
|
|
# Opens coverage.html in browser
|
|
```
|
|
|
|
### Run Specific Tests
|
|
|
|
```bash
|
|
# Test a single package
|
|
go test -v github.com/user/package-review/internal/inventory
|
|
|
|
# Test a specific test function
|
|
go test -v -run TestLoadInventory ./internal/inventory
|
|
|
|
# Run with race detector (detects data races)
|
|
go test -race ./...
|
|
|
|
# Run benchmarks
|
|
go test -bench=. -benchmem ./...
|
|
```
|
|
|
|
### Test Organization
|
|
|
|
Tests follow Go conventions:
|
|
- `*_test.go` files in the same package
|
|
- Table-driven tests for better coverage
|
|
- Benchmarks for performance-critical code
|
|
- Example tests where applicable
|
|
|
|
**Coverage Targets:**
|
|
- Overall: >80%
|
|
- Core packages (security, inventory): >90%
|
|
- Report generation: >85%
|
|
|
|
## Static Analysis
|
|
|
|
### Linting
|
|
|
|
The project uses **golangci-lint** which runs multiple linters:
|
|
|
|
```bash
|
|
make lint
|
|
```
|
|
|
|
**Configured Linters:**
|
|
- `staticcheck` — Go vet on steroids
|
|
- `gosec` — Security analysis
|
|
- `revive` — Code style linting
|
|
- `errcheck` — Unchecked error returns
|
|
- `ineffassign` — Ineffectual assignments
|
|
- `unused` — Unused code detection
|
|
- `gocritic` — Advanced analysis
|
|
- `cyclop` — Cyclomatic complexity
|
|
- `dupl` — Code duplication detection
|
|
|
|
**Configuration:** `.golangci.yml`
|
|
|
|
### Format Checking
|
|
|
|
```bash
|
|
# Check formatting
|
|
make fmt
|
|
|
|
# This runs:
|
|
go fmt ./... # Format code
|
|
go vet ./... # Basic type checking
|
|
```
|
|
|
|
### Vulnerability Scanning
|
|
|
|
```bash
|
|
make vulnerability
|
|
```
|
|
|
|
This runs **govulncheck** which checks against the official Go vulnerability database.
|
|
|
|
## CI/CD Pipeline
|
|
|
|
Automated checks run on:
|
|
- Every push to master/main/develop
|
|
- Every pull request
|
|
|
|
**GitHub Actions Workflow:** `.github/workflows/ci.yml`
|
|
|
|
### Checks
|
|
|
|
1. **Tests** — Multiple Go versions (1.22, 1.23)
|
|
2. **Linting** — golangci-lint with all configured rules
|
|
3. **Vulnerabilities** — govulncheck
|
|
4. **Formatting** — go fmt + go vet
|
|
5. **Coverage** — Uploaded to Codecov
|
|
|
|
## Pre-commit Hooks
|
|
|
|
Set up automatic checks before committing:
|
|
|
|
```bash
|
|
pip install pre-commit
|
|
pre-commit install
|
|
```
|
|
|
|
This will run:
|
|
- golangci-lint
|
|
- File checks (trailing whitespace, JSON validity, etc.)
|
|
- Go security checks (gosec)
|
|
- Spell checking
|
|
|
|
**Configuration:** `.pre-commit-config.yaml`
|
|
|
|
## Code Quality Standards
|
|
|
|
### Errors
|
|
|
|
All errors must be handled:
|
|
|
|
```go
|
|
// ✓ Good
|
|
if err != nil {
|
|
return fmt.Errorf("operation failed: %w", err)
|
|
}
|
|
|
|
// ✗ Bad
|
|
_ = doSomething() // ignoring error
|
|
```
|
|
|
|
### Naming
|
|
|
|
- Package: lowercase, concise (`inventory`, `security`)
|
|
- Functions: CamelCase, descriptive (`LoadInventory`, `FilterByMinSeverity`)
|
|
- Constants: UPPER_SNAKE_CASE (`MAX_RETRIES`)
|
|
- Unexported: lowercase (`internal`, `helper`)
|
|
|
|
### Complexity
|
|
|
|
- Max cyclomatic complexity: 10
|
|
- Max package average: 5
|
|
- Avoid deep nesting (max 3 levels)
|
|
|
|
### Comments
|
|
|
|
- Exported functions should have a comment
|
|
- Non-obvious logic should be commented
|
|
- Keep comments up-to-date with code
|
|
|
|
```go
|
|
// LoadInventory reads and parses the inventory JSON file.
|
|
func LoadInventory(path string) (*Inventory, error) {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
## Adding Tests
|
|
|
|
When adding features, add corresponding tests:
|
|
|
|
```go
|
|
// internal/security/feature_test.go
|
|
package security
|
|
|
|
import "testing"
|
|
|
|
func TestNewFeature(t *testing.T) {
|
|
// Arrange
|
|
input := "test data"
|
|
|
|
// Act
|
|
result := NewFeature(input)
|
|
|
|
// Assert
|
|
if result != "expected" {
|
|
t.Errorf("expected 'expected', got '%s'", result)
|
|
}
|
|
}
|
|
|
|
// Table-driven test
|
|
func TestFeatureVariations(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
want string
|
|
}{
|
|
{"case1", "input1", "output1"},
|
|
{"case2", "input2", "output2"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := NewFeature(tt.input)
|
|
if got != tt.want {
|
|
t.Errorf("got %s, want %s", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Benchmark
|
|
func BenchmarkFeature(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
NewFeature("test")
|
|
}
|
|
}
|
|
```
|
|
|
|
## Performance Testing
|
|
|
|
```bash
|
|
# Run benchmarks
|
|
go test -bench=. -benchmem ./...
|
|
|
|
# Compare benchmarks
|
|
go test -bench=. -benchmem ./... > new.txt
|
|
benchstat old.txt new.txt
|
|
```
|
|
|
|
## Coverage Gaps
|
|
|
|
Check for untested code:
|
|
|
|
```bash
|
|
# Generate HTML coverage report
|
|
go test -coverprofile=coverage.out ./...
|
|
go tool cover -html=coverage.out
|
|
|
|
# Show coverage per function
|
|
go tool cover -func=coverage.out
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Linting Failures
|
|
|
|
```bash
|
|
# See what golangci-lint found
|
|
golangci-lint run ./...
|
|
|
|
# Disable a specific check for a line (use sparingly)
|
|
//nolint:gosec // This is safe because...
|
|
func risky() {}
|
|
```
|
|
|
|
### Test Failures
|
|
|
|
```bash
|
|
# Run with verbose output
|
|
go test -v -run TestName ./...
|
|
|
|
# Run with debug output
|
|
go test -v -run TestName -timeout 30s ./... 2>&1 | head -100
|
|
```
|
|
|
|
### Race Detector
|
|
|
|
The race detector catches concurrent access issues:
|
|
|
|
```bash
|
|
# Run with race detector (slower, catches data races)
|
|
go test -race ./...
|
|
```
|
|
|
|
## Documentation
|
|
|
|
- **Code Comments:** Explain the "why", not the "what"
|
|
- **Examples:** Add example tests (`ExampleFunction`)
|
|
- **Godoc:** Auto-generated from comments at https://pkg.go.dev
|
|
|
|
```bash
|
|
# View godoc locally
|
|
go doc -http=:6060
|
|
# Visit http://localhost:6060
|
|
```
|
|
|
|
## Commit Guidelines
|
|
|
|
Good commits help with testing and history:
|
|
|
|
1. **Write meaningful messages:**
|
|
- Good: "Fix CVE filtering by CVSS score"
|
|
- Bad: "Fix bug"
|
|
|
|
2. **Keep commits small:**
|
|
- One feature or fix per commit
|
|
- Easier to bisect regressions
|
|
|
|
3. **Pass CI locally:**
|
|
```bash
|
|
make ci
|
|
```
|
|
|
|
## Resources
|
|
|
|
- **Go Testing:** https://golang.org/pkg/testing/
|
|
- **Golangci-lint:** https://golangci-lint.run/
|
|
- **Govulncheck:** https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck
|
|
- **Table-driven tests:** https://github.com/golang/go/wiki/TableDrivenTests
|
|
|
|
---
|
|
|
|
**Target Metrics:**
|
|
- Test coverage: >80%
|
|
- Linting: 0 warnings
|
|
- Vulnerabilities: 0 known issues
|
|
- Code duplications: <3%
|