Contributing
We welcome contributions to DBSurveyor! This guide will help you get started with contributing to the project.
Code of Conduct
DBSurveyor follows the Rust Code of Conduct. Please be respectful and inclusive in all interactions.
Getting Started
Prerequisites
- Rust 1.93.1+ (MSRV)
- Git
- Docker (for integration tests)
- Just task runner
Development Setup
# Clone the repository
git clone https://github.com/EvilBit-Labs/dbsurveyor.git
cd dbsurveyor
# Install development tools
just install
# Run initial checks
just dev
Project Structure
dbsurveyor/
├── dbsurveyor-core/ # Shared library
├── dbsurveyor-collect/ # Collection binary
├── dbsurveyor/ # Documentation binary
├── docs/ # Documentation source
│ └── solutions/ # Documented solutions to past problems
└── justfile # Development tasks
Development Workflow
Daily Development
# Format, lint, test, and check coverage
just dev
# Run specific test categories
just test-unit
just test-integration
just test-security
# Security validation
just security-full
# Pre-commit checks
just pre-commit
Code Quality Standards
DBSurveyor enforces strict quality standards:
- Zero Warnings:
cargo clippy -- -D warningsmust pass - Test Coverage: 55% minimum coverage required (target: 80%, to be raised incrementally)
- Security First: All code must pass security validation
- Documentation: All public APIs must have
///documentation
Testing Requirements
All contributions must include appropriate tests:
#![allow(unused)]
fn main() {
// Unit tests in source files
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_credential_sanitization() {
let config = ConnectionConfig::new("postgres://user:secret@host/db");
let safe_display = config.to_safe_string();
assert!(!safe_display.contains("secret"));
}
}
// Integration tests in tests/ directory
#[tokio::test]
async fn test_postgres_collection() {
let docker = testcontainers::clients::Cli::default();
let postgres = docker.run(testcontainers::images::postgres::Postgres::default());
// Test implementation
}
// Security tests are mandatory for security-sensitive code
#[tokio::test]
async fn test_no_credentials_in_output() {
let schema = collect_schema("postgres://user:secret@localhost/db").await?;
let json = serde_json::to_string(&schema)?;
assert!(!json.contains("secret"));
}
}
Contribution Types
Bug Reports
When reporting bugs, please include:
- System Information: OS, Rust version, DBSurveyor version
- Reproduction Steps: Minimal example that reproduces the issue
- Expected vs Actual Behavior: Clear description of the problem
- Debug Information: Output with
RUST_LOG=debug
Security Note: Never include actual database credentials in bug reports.
Feature Requests
For new features, please:
- Check Existing Issues: Avoid duplicates
- Describe Use Case: Why is this feature needed?
- Propose Implementation: High-level approach
- Consider Security: How does this maintain security guarantees?
Code Contributions
Pull Request Process
- Fork and Branch: Create a feature branch from
main - Implement Changes: Follow coding standards
- Add Tests: Comprehensive test coverage
- Update Documentation: Keep docs in sync
- Run Quality Checks:
just devmust pass - Submit PR: Clear description and context
Commit Standards
Follow Conventional Commits:
# Feature additions
feat(postgres): add connection pooling with timeout handling
feat(security): implement AES-GCM encryption with random nonces
# Bug fixes
fix(mysql): handle connection failures without exposing credentials
fix(core): ensure proper cleanup of sensitive data structures
# Security improvements
security(core): prevent credential leakage in error messages
security(encryption): add key derivation parameter validation
# Documentation
docs(readme): update installation instructions
docs(security): add encryption implementation details
Database Adapter Development
Adding New Database Support
To add support for a new database:
-
Create Adapter Module:
#![allow(unused)] fn main() { // dbsurveyor-core/src/adapters/newdb.rs pub struct NewDbAdapter { config: ConnectionConfig, } #[async_trait] impl DatabaseAdapter for NewDbAdapter { async fn test_connection(&self) -> Result<()> { ... } async fn collect_schema(&self) -> Result<DatabaseSchema> { ... } async fn sample_table(&self, table_ref: TableRef<'_>, config: &SamplingConfig) -> Result<TableSample> { ... } fn database_type(&self) -> DatabaseType { ... } fn supports_feature(&self, feature: AdapterFeature) -> bool { ... } fn connection_config(&self) -> ConnectionConfig { ... } } } -
Add Feature Flag:
# Cargo.toml [features] newdb = ["dep:newdb-driver"] -
Update Factory:
#![allow(unused)] fn main() { // dbsurveyor-core/src/adapters.rs match database_type { #[cfg(feature = "newdb")] DatabaseType::NewDb => { let adapter = NewDbAdapter::new(connection_string).await?; Ok(Box::new(adapter)) } // ... } } -
Add Tests:
#![allow(unused)] fn main() { // tests/integration/newdb_tests.rs #[tokio::test] async fn test_newdb_collection() { // Integration test with testcontainers } }
Database Adapter Requirements
All database adapters must:
- Implement
DatabaseAdaptertrait completelytest_connection(): Verify database connectivitycollect_schema(): Collect full database schema informationsample_table(): Sample data from a specific tabledatabase_type(): Return the database typesupports_feature(): Indicate supported featuresconnection_config(): Provide connection configuration
- Use read-only operations only (SELECT, DESCRIBE, SHOW)
- Handle connection timeouts (default: 30 seconds)
- Sanitize credentials in all error messages
- Support connection pooling where applicable
- Include comprehensive tests with testcontainers
- Document database-specific features and limitations
The sample_table() Method
The sample_table() method performs per-table sampling for a specific table:
#![allow(unused)]
fn main() {
async fn sample_table(
&self,
table_ref: TableRef<'_>,
config: &SamplingConfig,
) -> Result<TableSample>;
}
Parameters:
table_ref: ATableRefcontaining the table name and optional schema nameconfig: ASamplingConfigwith sampling parameters (sample size, throttle, etc.)
Returns: A TableSample that includes:
- Sampled rows as JSON values
- Sample metadata (size, total rows, strategy used)
- Collection timestamp and any warnings
sample_status: An optional field indicating the sampling outcome
Sample Status Values:
Some(SampleStatus::Complete): Sampling completed successfullySome(SampleStatus::PartialRetry { original_limit }): Sampling partially completed with a reduced limitSome(SampleStatus::Skipped { reason }): Sampling was skipped (e.g., not implemented)None: For backward compatibility with existing data
Implementation Notes:
- The
TableRefstruct wraps the table name and optional schema for the method signature - Implementations should populate
sample_statuswithSampleStatus::Completeon successful sampling - If sampling is not implemented, return
SampleStatus::Skippedwith an appropriate reason - The method supports optional schema qualification (e.g.,
public.usersvsusers)
Testing Database Adapters
# Test specific database adapter
just test-postgres
just test-mysql
just test-sqlite
# Test with real databases using testcontainers
cargo test --test postgres_integration -- --nocapture
# Security testing for new adapters
cargo test --test security_credential_protection
Security Contributions
Security-First Development
All contributions must maintain DBSurveyor’s security guarantees:
- No Credential Exposure: Never log or output credentials
- Offline Operation: No external network calls except to databases
- Encryption Security: Use AES-GCM with random nonces
- Memory Safety: Use
zeroizefor sensitive data
Security Review Process
Security-sensitive changes require additional review:
- Security Tests: Must include security-specific tests
- Threat Model: Consider impact on threat model
- Documentation: Update security documentation
- Review: Additional security-focused code review
Security Testing
#![allow(unused)]
fn main() {
// Example security test
#[tokio::test]
async fn test_new_feature_credential_security() {
// Test that new feature doesn't leak credentials
let result = new_feature("postgres://user:secret@localhost/db").await?;
let output = format!("{:?}", result);
assert!(!output.contains("secret"));
assert!(!output.contains("user:secret"));
}
}
Documentation Contributions
Documentation Standards
- User-Focused: Write for the end user
- Security-Aware: Highlight security implications
- Example-Rich: Include working code examples
- Up-to-Date: Keep in sync with code changes
Documentation Types
- API Documentation:
///comments in code - User Guide: Markdown files in
docs/src/ - README: Project overview and quick start
- Security Documentation: Security features and guarantees
Building Documentation
# Build API documentation
cargo doc --all-features --document-private-items --open
# Build user guide
just docs
# Check documentation
just docs-check
Release Process
Version Management
DBSurveyor uses semantic versioning:
- Major: Breaking changes
- Minor: New features (backward compatible)
- Patch: Bug fixes
Release Checklist
- Update Version: Bump version in
Cargo.toml - Update Changelog: Document all changes
- Run Full Tests:
just security-full - Update Documentation: Ensure docs are current
- Create Release: Tag and create GitHub release
- Verify Artifacts: Test release binaries
Community Guidelines
Communication
- GitHub Issues: Bug reports and feature requests
- Pull Requests: Code contributions and discussions
- Security Issues: Email security@evilbitlabs.io
Review Process
- Automated Checks: CI must pass
- Code Review: Maintainer review required
- Security Review: For security-sensitive changes
- Documentation Review: For user-facing changes
Recognition
Contributors are recognized in:
CONTRIBUTORS.mdfile- Release notes
- Git commit history
Development Environment
Recommended Tools
- IDE: VS Code with Rust Analyzer
- Git Hooks: Pre-commit hooks for quality checks
- Testing: Nextest for faster test execution
- Debugging:
RUST_LOG=debugfor detailed logging
Environment Variables
# Development environment
export RUST_LOG=debug
export DATABASE_URL="postgres://dev:dev@localhost/dev_db"
# Testing environment
export RUST_LOG=trace
export DBSURVEYOR_TEST_TIMEOUT=60
Docker Development
# Start test databases
docker-compose up -d postgres mysql mongodb
# Run integration tests
just test-integration
# Clean up
docker-compose down
Troubleshooting Development Issues
Common Issues
Build failures:
# Clean and rebuild
cargo clean
cargo build --all-features
# Update toolchain
rustup update
Test failures:
# Run specific test with output
cargo test test_name -- --nocapture
# Run with debug logging
RUST_LOG=debug cargo test test_name
Clippy warnings:
# Fix automatically where possible
cargo clippy --fix --allow-dirty
# Check specific warnings
cargo clippy --workspace --all-targets --all-features -- -D warnings
Getting Help
- Documentation: Check existing docs first
- Issues: Search existing GitHub issues
- Code: Look at similar implementations
- Community: Ask questions in GitHub discussions
License and Legal
License
DBSurveyor is licensed under the Apache License 2.0. By contributing, you agree to license your contributions under the same license.
Copyright
All contributions must include appropriate copyright headers:
#![allow(unused)]
fn main() {
// Copyright 2024 EvilBit Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
}
Contributor License Agreement
By submitting a pull request, you represent that:
- You have the right to license your contribution
- You agree to license it under the Apache License 2.0
- Your contribution is your original work
Thank you for contributing to DBSurveyor! Your contributions help make database documentation more secure and accessible for everyone.