ADR-0031: AST-Based Test Enforcement for Route Guard Functions
- Status: accepted
- Date: 2024-11-27
- Tags: Security, Testing, Authorization, Code Quality
Context and Problem Statement
SPIKE Nexus and SPIKE Keeper route handlers must perform authorization checks
before processing requests. The established pattern uses net.ReadParseAndGuard
which accepts a guard function parameter that is executed internally. However,
there is no compile-time or automated mechanism to ensure new route handlers
follow this pattern.
A contributor adding a new route could forget to include guard function invocation, creating an authorization bypass vulnerability. Code review catches most issues, but human oversight is fallible.
Decision Drivers
- Security: Every route must have authorization checks
- Developer experience: Should not add excessive boilerplate
- Maintainability: Solution should be self-documenting
- CI integration: Violations should be caught before merge
- Flexibility: Must support both standard and custom guard patterns
Considered Options
- Mandatory guard parameter on route registration - Pass guard function to all routes, but this does not guarantee the guard is actually called
- Wrapper function approach -
SecureRoute(pattern, guard, handler)that always calls guard before handler - Interface-based handlers - Require
SecureHandlerinterface withGuard()andHandle()methods - AST-based test - Scan route handler code and verify guard invocation
- Convention + code review only - Document the pattern and rely on review
Decision
Implement an AST-based test that scans all route handler files and verifies
each Route* function contains a guard invocation.
The test recognizes multiple valid patterns:
net.ReadParseAndGuardcalls (standard JSON route pattern)- Functions starting with
guard(e.g.,guardPolicyDeleteRequest) - Known guarded helper functions (e.g.,
handleJSONDecryptfor cipher routes that support streaming)
Rationale
- No code changes required - existing pattern works as-is
- CI enforcement - test fails if any route lacks guard invocation
- Self-documenting - test code documents the convention
- Zero runtime overhead - purely a test-time check
- Flexible - supports existing cipher routes that use custom guard patterns due to streaming support
The existing net.ReadParseAndGuard pattern already guarantees guard execution
for routes that use it. The AST test ensures all routes use either this pattern
or an equivalent guard invocation.
Consequences
Positive
- Authorization bypass vulnerabilities are caught automatically in CI
- New contributors learn the pattern from test failure messages
- No changes to production code or runtime behavior
- Test serves as living documentation of the guard convention
Negative
- Test must be updated if new guarded helper functions are added
- AST parsing adds test complexity
- False positives possible if function naming conventions change
Implementation Notes
SPIKE Nexus
The test is located at app/nexus/internal/route/base/guard_test.go and:
- Scans subdirectories:
acl/policy,bootstrap,cipher,operator,secret - Finds functions starting with
Route - Verifies each calls a guard (via
ReadParseAndGuard,guard*functions, or known guarded helpers) - Reports all violations with file paths and function names
Utility files (errors.go, guard.go, handle.go, etc.) are skipped as they
do not contain route handlers.
SPIKE Keeper
A similar test is located at app/keeper/internal/route/base/guard_test.go and:
- Scans the
storesubdirectory - Finds functions starting with
Route - Verifies each calls a guard (via
ReadParseAndGuardorguard*functions) - Reports all violations with file paths and function names
SPIKE Keeper has fewer routes (shard contribution and retrieval) but they are equally critical since they handle root key shards.
- ADR-0032: Standard 12-Byte Nonce Size for AES-GCM
- ADR-0031: AST-Based Test Enforcement for Route Guard Functions
- ADR-0030: Minimal Error Messages in API Responses
- ADR-0029: Restrict Recovery and Restoration Operations to SPIKE Pilot
- ADR-0028: Use Human-Readable Error Messages in CLI Tools
- ADR-0027: Separate Audit Logs from Operational Logs
- ADR-0026: Configurable Data Directory for SPIKE Components
- ADR-0025: Path Patterns as Key Namespaces with Regular Expression Matching
- ADR-0024: Transition from In-Memory Cache to Direct Backend Storage for High Availability
- ADR-0023: Decision Against Implementing Lock/Unlock Mechanism in SPIKE Nexus
- ADR-0022: Continuous Polling of SPIKE Keepers Despite 404 Response
- ADR-0021: SPIKE Keeper as a Stateless Shard Holder
- ADR-0020: Switch to Zola for Documentation System
- ADR-0019: Plugin-Based Storage Backend Architecture
- ADR-0018: Administrative Access to SPIKE
- ADR-0017: Synchronous Persistence for SPIKE Secrets Store
- ADR-0016: Memory-First Secrets Store
- ADR-0015: Use Singular Form for File and Package Naming
- ADR-0014: Maintaining SQLite as SPIKE’s Primary Storage Backend
- ADR-0013: S3-Compatible Storage as SPIKE’s Backing Store
- ADR-0012: HTTP Methods for SPIKE API
- ADR-0011: PostgreSQL as SPIKE’s Backing Store
- ADR-0010: Session Token Storage Strategy for SPIKE Nexus
- ADR-0009: Multi-Administrator Support System
- ADR-0008: Administrative Access Control System
- ADR-0007: Root Key Lifecycle and Management Strategy
- ADR-0006: Trust Boundary Definition and Security Assumptions
- ADR-0005: Use SPIFFE mTLS for Inter-Component Authentication and Communication
- ADR-0004: SPIKE Keeper Minimalist Design Approach
- ADR-0003: Root Key Management and Storage Strategy
- ADR-0002: Use Docsify for Documentation System
- ADR-0001: Display Secrets in Plain Text in SPIKE Pilot Admin CLI