Security Anti-Patterns And Safe Coding Practices#
Lead Section#
DBSurveyor enforces security-first development principles through explicit prohibition of dangerous patterns and mandatory safe coding practices. The codebase implements comprehensive security guarantees for credential protection, encryption, and database operations.
Key security principles:
- Zero credential exposure: Credentials are never stored, logged, or included in any output files
- Memory safety: No unsafe blocks permitted without explicit security review
- Offline operation: No external network calls beyond database connections, no telemetry, no auto-updates
Explicitly Prohibited Security Anti-Patterns#
Credential Exposure#
Never log or expose database credentials in any form. Prohibited patterns include:
❌ Logging connection strings:
// NEVER do this
log::info!("Connecting to {}", database_url);
❌ Including credentials in error messages:
// NEVER do this
#[error("Failed to connect to {url}")]
ConnectionError { url: String },
❌ Storing credentials in persistent structs
Connection strings are sanitized in all error messages and logs - credentials are automatically masked (e.g., postgres://user:secret123@localhost becomes postgres://user:****@localhost).
Unsafe unwrap() Usage#
All code must pass cargo clippy -- -D warnings, treating all clippy warnings as compilation errors.
❌ Never use unwrap() in production:
- Prohibited: Using
unwrap()orexpect()on Result or Option types - Required: Use proper error propagation with
?operator or explicit error handling
SQL String Concatenation#
All queries must use parameterized statements with no SQL injection risk.
❌ Never concatenate SQL strings:
// NEVER do this
format!("SELECT * FROM {}", table)
✅ Required: Use parameterized query interfaces provided by database drivers
Clippy Lint Suppression#
Zero warnings requirement means suppressing clippy lints is not acceptable.
❌ Never suppress clippy lints:
// NEVER do this
#[allow(clippy::all)]
✅ Required: Fix the underlying code issue instead of suppressing the lint
Required Safe Coding Practices#
Zeroize Trait for Sensitive Data#
Credentials must be automatically zeroed using the zeroize crate.
✅ Required pattern (actual implementation):
use zeroize::{Zeroize, Zeroizing};
#[derive(Debug, Clone, Zeroize)]
#[zeroize(drop)]
pub struct Credentials {
pub username: Zeroizing<String>,
pub password: Zeroizing<Option<String>>,
}
Purpose: Ensure credentials are automatically zeroed from memory when dropped.
Connection String Parsing#
Credentials must be parsed separately from connection configuration.
✅ Required pattern (actual implementation):
pub fn parse_connection_string(
connection_string: &str,
) -> crate::Result<(ConnectionInfo, Credentials)> {
let url = url::Url::parse(connection_string)?;
let username = url.username().to_string();
let password = url.password().map(|p| p.to_string());
let credentials = Credentials::new(username, password);
let config = ConnectionInfo {
scheme: url.scheme().to_string(),
host,
port,
database,
query_params: url.query_pairs().into_owned().collect(),
};
Ok((config, credentials))
}
The ConnectionInfo struct intentionally excludes credentials and provides a to_safe_string() method for logging.
Sanitized Logging#
Credentials must never appear in log output.
✅ Correct logging patterns:
log::info!("Connecting to database");
log::debug!("Connection timeout: {}", config.to_safe_string());
❌ Never log sensitive data:
log::info!("Connecting to {}", database_url);
Sanitization implementation (redact_database_url function):
pub fn redact_database_url(url: &str) -> String {
match url::Url::parse(url) {
Ok(mut parsed_url) => {
if parsed_url.password().is_some() {
let _ = parsed_url.set_password(Some("****"));
}
parsed_url.to_string()
}
Err(_) => "<redacted>".to_string(),
}
}
This function is used in connection pool error messages.
Parameterized Queries#
All queries must use parameterized statements.
✅ Implementation example (PostgreSQL sampling):
let rows = sqlx::query(pk_query)
.bind(table)
.bind(schema)
.fetch_all(pool)
.await
Column collection uses parameterized queries with placeholders:
WHERE c.table_name = $1
AND c.table_schema = $2
let column_rows = sqlx::query(columns_query)
.bind(table_name)
.bind(schema)
.fetch_all(&self.pool)
Database adapters: Use driver-provided parameterized query interfaces (sqlx for PostgreSQL/MySQL/SQLite, mongodb driver for MongoDB).
Read-Only Transaction Mode#
All database operations are SELECT/DESCRIBE only; no INSERT, UPDATE, DELETE, or DDL operations.
✅ Required enforcement (PostgreSQL adapter):
// Set read-only mode if requested (enforced by default for security)
if read_only {
conn.execute("SET default_transaction_read_only = on")
.await?;
}
The default ConnectionConfig enforces read-only mode:
impl Default for ConnectionConfig {
fn default() -> Self {
Self {
// ...
read_only: true, // Enforced by default
// ...
}
}
}
Additional protection: Recommend using read-only database users.
Statement Timeouts#
All connections and queries must have configurable timeouts.
✅ Required configuration (default timeouts):
ConnectionConfig {
connect_timeout: Duration::from_secs(30),
query_timeout: Duration::from_secs(30),
max_connections: 10,
read_only: true,
}
Enforcement (statement timeout via after_connect callback):
// Set query timeout to prevent resource exhaustion
conn.execute(
format!("SET statement_timeout = '{}s'", query_timeout_secs).as_str(),
)
.await?;
// Set lock timeout to prevent blocking operations
conn.execute("SET lock_timeout = '30s'").await?;
// Set idle timeout for session cleanup
conn.execute("SET idle_in_transaction_session_timeout = '60s'")
.await?;
Workspace-Level Security Policies#
No Unsafe Code#
The workspace enforces a strict no-unsafe-code policy.
Enforcement (Cargo.toml workspace lints):
[workspace.lints.rust]
unsafe_code = "deny" # No unsafe code allowed (security)
missing_docs = "warn" # Documentation required
unreachable_pub = "warn" # No dead public APIs
No unsafe blocks are permitted without explicit security review and documentation.
Zero Warnings Policy#
All contributions must pass cargo clippy -- -D warnings.
Enforcement (Cargo.toml clippy configuration):
[workspace.lints.clippy]
all = "deny" # Strict linting
pedantic = { level = "warn", priority = -1 }
# Security-specific lints
unwrap_used = "deny" # No unwrap in production code
expect_used = "deny" # Use proper error handling
panic = "deny" # No panic in production code
Justfile enforces -D warnings:
clippy:
cargo clippy --workspace --all-targets --all-features -- -D warnings
Error Sanitization Policy#
All error messages must be sanitized to prevent credential leakage.
Implementation: Error messages are sanitized throughout the adapter layer to prevent credential exposure, using generic messages like "Database connection failed" rather than including connection details.
Offline Operation Guarantee#
No external network calls permitted beyond database connections:
- NO NETWORK CALLS
- NO TELEMETRY
- NO AUTO-UPDATES
- AIRGAP COMPATIBLE
Validation and Testing#
Security validation commands (justfile):
just security-full # Complete security validation
just test-encryption # Test encryption capabilities
just test-offline # Verify offline operation
just test-credential-security # Check for credential leakage
just audit # Security audit
Security Test Suite#
Comprehensive credential security tests validate credential protection at the integration layer.
Security-sensitive contributions require comprehensive security tests.
Contribution Security Requirements#
Security-sensitive contributions require additional review:
- No credential exposure
- Offline operation only
- Encryption security (AES-GCM with random nonces)
- Memory safety (use zeroize)
- Test coverage (>80% with security-specific tests)
- Threat model review
- Documentation updates
Relevant Code Files#
| File Path | Purpose | Key Security Features |
|---|---|---|
.kiro/specs/database-schema-collection/design/security.md | Security architecture documentation | Comprehensive security guarantees, anti-patterns, implementation examples |
dbsurveyor-core/src/security/credentials.rs | Credential handling | Zeroize trait implementation, secure memory clearing |
dbsurveyor-core/src/security/connection.rs | Connection parsing | Separation of credentials from config, sanitized logging |
dbsurveyor-core/src/error.rs | Error handling | redact_database_url function for credential sanitization |
dbsurveyor-core/src/adapters/config/connection.rs | Connection configuration | Default read-only mode, timeout configuration |
dbsurveyor-core/src/adapters/postgres/connection.rs | PostgreSQL connection | Read-only mode enforcement, statement timeouts via after_connect |
dbsurveyor-core/src/adapters/postgres/schema_collection.rs | Schema collection | Parameterized queries for metadata collection |
dbsurveyor-core/src/adapters/postgres/sampling.rs | Data sampling | Parameterized queries for data sampling |
dbsurveyor-collect/tests/security_tests.rs | Security test suite | Credential leak detection, error sanitization validation |
Cargo.toml | Workspace configuration | Workspace-level lints, unsafe code denial, clippy enforcement |
justfile | Build automation | Security validation commands, clippy with -D warnings |
.github/workflows/ci.yml | CI/CD pipeline | Automated security checks in CI |
.github/workflows/security.yml | Security workflow | Dedicated security validation pipeline |
Related Topics#
- Credential Memory Security in Rust: Implementation of secure credential handling using Rust's Zeroize crate for automatic memory clearing
- Database Connection Security Architecture: Design patterns for separating credentials from configuration and enforcing read-only operations at the connection level
- Query Safety and SQL Injection Prevention: Use of parameterized queries with bind parameters across PostgreSQL operations to prevent SQL injection
- Rust Security Linting Enforcement: Workspace-level cargo configuration and CI/CD pipelines that enforce strict security-focused clippy lints with zero-warning policies
- Security Testing Infrastructure: Dedicated security test suite, justfile commands, and CI workflows that validate security guarantees