원클릭으로
test-multitenant-isolation
Validates complete data isolation between tenants, tests cross-tenant access, ensures proper query scoping
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Validates complete data isolation between tenants, tests cross-tenant access, ensures proper query scoping
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | test-multitenant-isolation |
| description | Validates complete data isolation between tenants, tests cross-tenant access, ensures proper query scoping |
| user-invocable | true |
Validates complete data isolation between tenants to prevent catastrophic data leaks. Tests cross-tenant access attempts and ensures all database queries are properly scoped.
Run this skill:
# Run full multi-tenant isolation test suite
cargo test --test mcp_multitenant_complete_test --features testing -- --nocapture
# Run specific isolation tests
cargo test multitenant -- --nocapture
# Test cross-tenant access attempts
cargo test test_cross_tenant -- --nocapture
# Test tenant context middleware
cargo test test_tenant_middleware -- --nocapture
# Search for queries without tenant_id filtering
echo "🔍 Checking for unscoped queries..."
rg "SELECT.*FROM.*WHERE" src/ --type rust -A 3 | rg -v "tenant_id" | head -20
# Verify TenantContext usage in routes
echo "🔍 Checking route handler tenant context..."
rg "Extension.*TenantContext" src/routes/ --type rust -n | wc -l
# Check for hardcoded tenant IDs (security issue)
rg -i "tenant.*=.*\"[a-f0-9-]{36}\"" src/ --type rust -n || echo "✓ No hardcoded tenant IDs"
// Tenant A creates activity
// Tenant B attempts to read it
// Expected: 403 Forbidden or empty result
// Middleware extracts tenant_id from JWT
// All subsequent queries filtered by tenant_id
// Expected: Only tenant's own data visible
// Tenant A's API key used
// Attempt to access Tenant B's data
// Expected: Empty results (not 403 - security through obscurity)
// OAuth tokens stored per tenant
// Tenant A cannot access Tenant B's tokens
// Expected: Null/NotFound
# All queries MUST include tenant_id filter
# Examples of CORRECT patterns:
# ✅ SELECT with tenant_id
SELECT * FROM activities WHERE tenant_id = $1 AND user_id = $2
# ✅ INSERT with tenant_id
INSERT INTO activities (tenant_id, user_id, ...) VALUES ($1, $2, ...)
# ✅ UPDATE with tenant_id
UPDATE activities SET ... WHERE tenant_id = $1 AND id = $2
# ✅ DELETE with tenant_id
DELETE FROM activities WHERE tenant_id = $1 AND id = $2
// All route handlers must use TenantContext
// ✅ Correct
pub async fn get_activities(
Extension(tenant): Extension<TenantContext>,
Json(params): Json<GetActivitiesParams>,
) -> Result<Json<Activities>, AppError> {
// tenant.tenant_id automatically scopes queries
}
// ❌ Incorrect (missing TenantContext)
pub async fn get_activities(
Json(params): Json<GetActivitiesParams>,
) -> Result<Json<Activities>, AppError> {
// No tenant scoping!
}
test test_tenant_isolation ... ok
test test_cross_tenant_activity_access ... ok (should fail access)
test test_cross_tenant_user_access ... ok (should fail access)
test test_tenant_oauth_isolation ... ok
test test_tenant_api_key_isolation ... ok
test result: ok. 12 passed; 0 failed
# ❌ BAD: Cross-tenant access succeeded
test test_cross_tenant_activity_access ... FAILED
Expected: Forbidden or empty
Actual: Returned data from other tenant
# ❌ BAD: Query without tenant_id
SELECT * FROM activities WHERE user_id = $1
(Missing tenant_id filter!)
# ❌ BAD: Tenant ID from request body instead of JWT
let tenant_id = params.tenant_id; // User can forge!
# Verify OAuth tokens are stored per-tenant (not global)
echo "🔐 Checking OAuth credential isolation..."
rg "oauth_token|refresh_token|access_token" src/database/ --type rust -A 3 | \
rg "tenant_id" | wc -l
echo "OAuth token queries with tenant_id scoping"
# Check that provider credentials are tenant-scoped
rg "provider.*credential|strava.*token|garmin.*token" src/ --type rust -A 5 | \
rg -v "tenant_id" | rg "SELECT|INSERT|UPDATE" && \
echo "⚠️ Provider credential query without tenant_id!" || \
echo "✓ Provider credentials properly tenant-scoped"
# Verify config mutations check tenant membership
echo "🔐 Checking config write/delete tenant scoping..."
rg "fn.*config.*(create|update|delete|write|remove)" src/ --type rust -A 10 | \
rg "tenant_id" | wc -l
echo "Config mutation functions with tenant_id check"
# Check admin tools verify target belongs to caller's tenant
rg "fn.*(assign|remove|update).*coach|fn.*(assign|remove|update).*user" src/ --type rust -A 10 | \
rg "tenant_id" | wc -l
echo "Admin tool functions with tenant_id verification"
# Verify LLM/AI settings are per-tenant
echo "🔐 Checking LLM API key isolation..."
rg "llm.*key|ai.*key|gemini.*key|groq.*key|ollama.*url" src/ --type rust -A 5 | \
rg "tenant_id" | wc -l
echo "LLM key storage/retrieval with tenant_id scoping"
tests/mcp_multitenant_complete_test.rs - Main test suitesrc/tenant/mod.rs - TenantContext definitionsrc/middleware/tenant_middleware.rs - Tenant extractionsrc/database/mod.rs - Scoped database queriescheck-no-secrets - Secret detectionvalidate-architecture - Architectural validationUse when designing prompts for LLMs, optimizing model performance, building evaluation frameworks, or implementing advanced prompting techniques like chain-of-thought, few-shot learning, or structured outputs.
How to deploy Dravr infrastructure and apply Cloud Run config changes. Use when editing infra/ terraform, when a merged code change is live but a Cloud Run setting (cpu, memory, min/max instances, env var, scaling) hasn't taken effect, or when asked to plan/apply infra. Explains the two-pipeline model (app binary auto-deploys on push; terraform infra config is a separate manual apply) plus the cpu/cpu_idle guardrails.
Enforces zero-tolerance code quality policy using Clippy with strict lints, all warnings treated as errors
Write well-formatted notes to the dravr-vault Obsidian knowledge base. Use this skill whenever creating or updating an ADR, runbook, plan, API doc, guide, session output, or any structured document that should land in the vault — even when the user doesn't say "Obsidian" explicitly. Delegates to obsidian:obsidian-cli to write to the live vault and applies Dravr frontmatter and formatting standards.
Bootstrap Pierre server with database, admin user, coaches, and test users for development and testing
Validates coach markdown files for required frontmatter fields, sections, and naming conventions