Developer Security Checklist
Use this checklist when writing new code or modifying existing code to ensure security best practices.
Before Writing Code
- Understand the security implications of your changes
-
Identify affected security controls (check
governance/config/security-control-mapping.yml) - Review relevant security documentation
- Consider threat model for your changes
Input Validation
- Validate all user inputs at boundaries
- Sanitize inputs before processing
- Use type-safe APIs (Rust's type system)
- Reject invalid inputs early
- Validate data from external sources (network, files, databases)
Examples:
#![allow(unused)] fn main() { // ✅ Good: Validate input fn process_amount(amount: u64) -> Result<u64, Error> { if amount > MAX_AMOUNT { return Err(Error::AmountTooLarge); } Ok(amount) } // ❌ Bad: No validation fn process_amount(amount: u64) -> u64 { amount // Could overflow } }
Authentication & Authorization
- Implement proper authentication (if applicable)
- Check authorization before sensitive operations
- Use principle of least privilege
- Verify permissions at every boundary
- Don't trust client-side authorization checks
Examples:
#![allow(unused)] fn main() { // ✅ Good: Check authorization fn transfer_funds(from: Account, to: Account, amount: u64) -> Result<(), Error> { if !from.has_permission(Permission::Transfer) { return Err(Error::Unauthorized); } // ... transfer logic } // ❌ Bad: No authorization check fn transfer_funds(from: Account, to: Account, amount: u64) { // ... transfer logic without checking permissions } }
Cryptographic Operations
- Use well-tested cryptographic libraries (secp256k1, bitcoin_hashes)
- Never hardcode keys or secrets
- Use cryptographically secure random number generation
- Follow Bitcoin standards (BIP32, BIP39, BIP44)
- Verify signatures completely
- Use constant-time operations where needed (avoid timing attacks)
Examples:
#![allow(unused)] fn main() { // ✅ Good: Use secure random use rand::rngs::OsRng; let mut rng = OsRng; let key = secp256k1::SecretKey::new(&mut rng); // ❌ Bad: Insecure random let key = secp256k1::SecretKey::from_slice(&[1, 2, 3, ...])?; }
Consensus & Protocol
- Implement consensus rules exactly as specified
- Validate all protocol messages
- Handle network errors gracefully
- Prevent DoS attacks (rate limiting, resource limits)
- Don't bypass consensus validation
Examples:
#![allow(unused)] fn main() { // ✅ Good: Validate consensus rules fn validate_block(block: &Block) -> Result<(), ConsensusError> { if !block.verify_merkle_root() { return Err(ConsensusError::InvalidMerkleRoot); } // ... more validation } // ❌ Bad: Skip validation fn validate_block(block: &Block) -> Result<(), ConsensusError> { Ok(()) // No validation! } }
Memory Safety
- Prefer safe Rust code
-
Document and justify any
unsafecode - Ensure proper resource cleanup (Drop trait)
- Avoid memory leaks (use RAII patterns)
- Check bounds before array/vector access
Examples:
#![allow(unused)] fn main() { // ✅ Good: Safe Rust let value = vec.get(index).ok_or(Error::OutOfBounds)?; // ❌ Bad: Unsafe indexing let value = vec[index]; // Could panic }
Error Handling
- Don't leak sensitive information in errors
- Use specific error types
- Handle all error cases
- Fail securely (default deny)
- Log errors appropriately (no sensitive data)
Examples:
#![allow(unused)] fn main() { // ✅ Good: Generic error message return Err(Error::AuthenticationFailed); // Doesn't reveal why // ❌ Bad: Leaks information return Err(Error::InvalidPassword("user123")); // Reveals username }
Dependencies
- Use minimal dependencies
- Keep dependencies up-to-date
- Pin consensus-critical dependencies to exact versions
- Check for known vulnerabilities (cargo audit)
- Review dependency licenses
Examples:
# ✅ Good: Pin critical dependencies
[dependencies]
secp256k1 = "=0.28.0" # Exact version for consensus-critical
# ❌ Bad: Allow version ranges for critical code
[dependencies]
secp256k1 = "^0.28" # Could break consensus
Testing
- Write security-focused tests
- Test edge cases and boundary conditions
- Test error handling paths
- Include fuzzing for consensus/protocol code
- Test with malicious inputs
- Achieve adequate test coverage
Examples:
#![allow(unused)] fn main() { #[test] fn test_amount_overflow() { assert!(process_amount(u64::MAX).is_err()); } #[test] fn test_invalid_signature() { let invalid_sig = vec![0u8; 64]; assert!(verify_signature(&invalid_sig).is_err()); } }
Documentation
- Document security assumptions
- Document threat model considerations
- Document security implications of design decisions
- Update security documentation if adding new controls
- Document configuration security requirements
Code Review
- Request security review for security-sensitive code
- Address security review feedback
- Update security control mapping if needed
- Ensure appropriate governance tier is selected
Post-Implementation
- Verify security tests pass
- Check for new security advisories
- Update threat model if needed
- Document any security trade-offs
Security Control Categories
Category A: Consensus Integrity
- Genesis block implementation
- SegWit witness verification
- Taproot support
- Script execution limits
- UTXO set validation
Category B: Cryptographic
- Maintainer key management
- Emergency signature verification
- Multisig threshold enforcement
- Key derivation and storage
Category C: Governance
- Tier classification logic
- Database query implementation
- Cross-layer file verification
Category D: Data Integrity
- Audit log hash chain
- OTS timestamping
- State synchronization
Category E: Input Validation
- GitHub webhook signature verification
- Input sanitization
- SQL injection prevention
- API rate limiting
Resources
- Security Controls System
- Threat Models
- blvm-node SECURITY.md (reference node security practices)