Website security auditing and crawling library for Go. Crawls sites concurrently, runs checks and declarative rules, generates findings with severity and CWE references.
- Library — importable Go library + embeddable MCP server (no CLI binary)
- No LLM dependency — pure static analysis on crawled pages
- Extensible — custom checks (Go code) + declarative rules (no code required)
go test ./... # Run all tests
go test -race ./... # Race detector
go test -coverprofile=c.out ./... # Coverage
go vet ./... # Static analysis
gofumpt -w . # Formatcrawler.go— Concurrent website crawler with depth controlcheck.go— Check interface and built-in security checksrule.go— Declarative rule engine (YAML-based)finding.go— Findings with severity, CWE, and evidencereport.go— Report generation (JSON, SARIF, HTML)
- Go 1.26+, pure Go, no CGO
- Table-driven tests
- Conventional Commits:
feat:,fix:,docs:,refactor:,test: - No
Co-authored-by:trailers (auto-stripped by githook) gofumptformatting enforced in CI- CWE references required for all security findings
- Crawler tests need HTTP test servers — use
httptest.NewServer - Rule YAML must be validated before execution
- Session cookie matching uses substring, not exact match
- Types are domain nouns:
Finding,Report,Stats,Page,PageLink,Checker,RuleCheck - Option functions use
Withprefix:WithChecks(),WithDepth(),WithConcurrency(),WithAllowPrivateIPs() - Preset options are bare vars:
Quick,Standard,Deep,SecurityOnly,CI— exportedvar Optionvalues - Severity is a type alias:
type Severity = types.Severityfromhawk/shared/types— shared across hawk-eco - Internal adapters use
Adaptersuffix:ruleCheckAdapter,customCheckAdapter— bridge public to internal interfaces - Check names are lowercase strings:
"security","links","forms","a11y","performance"— used inWithChecks() - Error handling:
Scan()returns(*Report, error)— validation errors for empty URL, nil errors for success
- Functional options pattern: same as sight —
Optioninterface withoptFuncadapter,buildConfig()merge - One-shot + reusable:
Scan(ctx, target, opts...)creates aScannerinternally;NewScanner(opts...)for reuse - Checker interface for extensibility:
Name() string+Run(ctx, pages) []Finding— register viaRegisterCheck() - RuleCheck for declarative rules:
HeaderMatch,HeaderMissing,BodyMatch,BodyMissing,URLMatchpatterns - Global + per-scanner custom checks:
RegisterCheck()/RegisterRule()for global; pass slices toScannerfor scoped - Report.Failed(): checks if any finding meets
FailOnseverity threshold — same pattern as sight - ReDoS protection: all user-supplied regex patterns go through
compileWithTimeout()andmatchWithTimeout()with 1s/100ms limits - Regex complexity check:
checkRegexComplexity()rejects nested quantifiers and deep group nesting before compilation
- External test package:
package inspect_test— tests importinspectas a consumer would - httptest.NewServer for all tests: each test spins up a mock HTTP server with specific HTML/headers/responses
- Test patterns by concern:
TestScan_BasicSite(links),TestScan_SecurityHeaders,TestScan_FormCSRF,TestScan_Accessibility - Always pass
WithAllowPrivateIPs(): tests run against127.0.0.1— without this flag, localhost is blocked - Always pass
WithDepth(1): keeps tests fast by limiting crawl depth - Finding assertions: iterate
report.Findingsand check specificCheck,Severity,Messagefields - Preset smoke test:
TestScan_Presetsruns all presets against a simple server — catches config panics - ClearCustomChecks() in tests: call before registering test-specific checks to avoid global state leaks
- Report method tests:
TestReport_Failed,TestReport_MaxSeverity— test on struct literals, no HTTP needed
- Safe to refactor:
checkRegexComplexity(),compileWithTimeout(),matchWithTimeout()— internal helpers - Safe to refactor:
truncateEvidence(),intIn()— pure utility functions - Safe to refactor:
parseInspectTOML(),parseInspectKeyValue(),applyFileConfig()— config parsing internals - Do not touch:
Checkerinterface (Name(),Run()) — breaking change for all custom check implementations - Do not touch:
RuleCheckstruct field names — used by consumers to define declarative rules - Do not touch:
Finding,Report,Statsstruct field names/tags — JSON serialization contract - Safe to extend: add new
Optionfunctions, new presets, new built-in checks inchecks/package - When adding checks: create a new file in
checks/, implementCheckerinterface, register ininit()
| What | Where |
|---|---|
| Public API entry point | inspect.go (types, Scan(), Finding, Report, Stats) |
| Check interface & adapters | check.go (Checker, RuleCheck, RegisterCheck(), RegisterRule(), ReDoS protection) |
| Scanner implementation | scanner.go (crawler orchestration, check execution) |
| Configuration & presets | options.go (config struct, With* functions, presets) |
| Config file loading | config.go (.inspect.toml parsing, LoadConfig()) |
| Severity type alias | severity.go (re-exports from hawk/shared/types) |
| SARIF output | sarif.go |
| CI output formatting | ci_output.go |
| Built-in checks | checks/ directory |
| Internal crawler | internal/crawler/ |
| Internal check runner | internal/check/ |
| Browser-based crawling | browser.go, browser/ |
| LLM scanner integration | llm_scanner.go |
| API security checks | api_security.go |
| Dependency checking | dependency_check.go |
| SBOM generation | sbom.go |
| Main test file | inspect_test.go (httptest servers, per-concern scenarios) |
| Linter config | .golangci.yml (errcheck, govet, staticcheck, gocritic, bodyclose, noctx) |