Initial project scaffold: macOS package security audit tool
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>
This commit is contained in:
+329
@@ -0,0 +1,329 @@
|
||||
# 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%
|
||||
Reference in New Issue
Block a user