Skip to content

Latest commit

 

History

History
96 lines (74 loc) · 2.62 KB

File metadata and controls

96 lines (74 loc) · 2.62 KB

Security Guideline

Input Validation

use validator::{Validate, ValidationError};

#[derive(Deserialize, Validate)]
pub struct CreateUserRequest {
    #[validate(length(min = 1, max = 100))]
    pub name: String,

    #[validate(email)]
    pub email: String,

    #[validate(custom = "validate_password")]
    pub password: String,
}

fn validate_password(password: &str) -> Result<(), ValidationError> {
    if password.len() < 8 {
        return Err(ValidationError::new("Password too short"));
    }
    Ok(())
}

Secrets Management

  • Never commit secrets to version control
  • Use environment variables for configuration
  • Rotate secrets regularly
  • Use secure random generation for tokens
// Bad: hardcoded secret
const API_KEY: &str = "sk-1234567890abcdef";

// Good: environment variable
let api_key = std::env::var("API_KEY")
    .context("API_KEY environment variable not set")?;

Secure ORM

Gears provide a secure-by-default ORM layer that enforces access control at compile time using the typestate pattern. This prevents unscoped database queries from executing and ensures tenant isolation.

For secure ORM usage, see docs/toolkit_unified_system/06_authn_authz_secure_orm.md.

Key Features

  1. Compile-time enforcement: Cannot bypass scoping via type system
  2. Deny-by-default: Empty scopes explicitly denied with WHERE 1=0
  3. Tenant isolation: Automatic filtering when tenant_ids provided
  4. Request-scoped: Security context tied to request lifecycle
  5. No SQL injection: Uses SeaORM's parameterized queries
  6. Audit trail: Raw access logs security warnings

Quick Example

use toolkit_db::secure::{SecurityContext, SecureConn};
use toolkit_db_macros::Scopable;

// Define a scopable entity
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Scopable)]
#[sea_orm(table_name = "users")]
#[secure(tenant_col = "tenant_id")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: Uuid,
    pub tenant_id: Uuid,
    pub email: String,
}

// Use in API handler
pub async fn list_users_handler(
    Extension(auth): Extension<AuthContext>,
    Extension(db): Extension<DbHandle>,
) -> Result<Json<Vec<User>>, Problem> {
    // Create security context from request
    let ctx = SecurityContext::for_tenants(vec![auth.tenant_id], auth.user_id);

    // Get secure connection
    let secure_conn = db.sea_secure();

    // Query with automatic tenant isolation
    let scope = toolkit_db::secure::AccessScope::tenant(ctx.tenant_id());
    let users = secure_conn
        .find::<user::Entity>(&scope)
        .all(&secure_conn)
        .await?;

    Ok(Json(users))
}