Code Examples
This page shows real patterns extracted from the ThreatWeaver codebase. Use these as templates when building new features.
Example 1: Express Route Handlerβ
This pattern is taken from backend/src/routes/admin.routes.ts. It demonstrates a typical authenticated, permission-gated route:
// backend/src/routes/admin.routes.ts (actual pattern)
import { Router, Request, Response } from 'express'
import {
authenticate,
requirePermission,
AuthenticatedRequest,
} from '../middleware/auth.js'
import { PERMISSIONS } from '../entities/index.js'
import { settingsService } from '../services/settings.service.js'
const router = Router()
// Apply authentication to ALL routes in this file
router.use(authenticate)
// GET /api/admin/severity-debug
// Requires MANAGE_SETTINGS permission (admin-only)
router.get(
'/severity-debug',
requirePermission(PERMISSIONS.MANAGE_SETTINGS),
async (req: Request, res: Response) => {
try {
// Disable caching for dynamic data
res.setHeader('Cache-Control', 'no-store')
// Call the service layer (never put business logic in routes)
const summary = await settingsService.getSeveritySummary()
res.json(summary)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
}
)
export default router
Key patterns:
router.use(authenticate)at the top applies to all routes in the filerequirePermission()is added as route-level middleware for admin gates- Business logic lives in a service, not in the route handler
Cache-Control: no-storefor data that should not be cached- Try/catch wrapping every async handler
- Type
AuthenticatedRequestgives youreq.userwithuserId,role,tenantId, etc.
Example 2: TypeORM Entity with Relationshipsβ
This pattern is taken from backend/src/entities/User.entity.ts. It demonstrates a full entity with columns, relationships, lifecycle hooks, and instance methods:
// backend/src/entities/User.entity.ts (actual code)
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
BeforeInsert,
BeforeUpdate,
ManyToOne,
} from 'typeorm'
import bcrypt from 'bcryptjs'
import type { Role } from './Role.entity'
export type UserStatus = 'active' | 'inactive' | 'locked' | 'pending_invite' | 'deactivated'
@Entity('users')
export class User {
@PrimaryGeneratedColumn('uuid')
id!: string
@Column({ type: 'varchar', length: 255, unique: true })
email!: string
// select: false -- this column is excluded from default queries
// Must explicitly select it: .addSelect('user.passwordHash')
@Column({ type: 'varchar', length: 255, select: false, nullable: true })
passwordHash!: string | null
@Column({ type: 'varchar', length: 255 })
name!: string
@Column({ type: 'varchar', length: 50, default: 'viewer', nullable: true })
role!: string
// Relationship to Role entity (RBAC v2)
@ManyToOne('Role', (role: Role) => role.users, { nullable: true })
roleDefinition!: Role
@Column({ type: 'varchar', length: 50, default: 'active' })
status!: UserStatus
@Column({ type: 'int', default: 0 })
failedLoginAttempts!: number
@Column({ type: 'timestamp', nullable: true })
lockedUntil!: Date | null
@Column({ type: 'timestamp', nullable: true })
lastLoginAt!: Date | null
@Column({ type: 'boolean', default: false })
twoFactorEnabled!: boolean
@Column({ type: 'varchar', nullable: true, select: false })
twoFactorSecret!: string | null
// SSO fields
@Column({ type: 'varchar', length: 50, nullable: true })
ssoProvider!: string | null // 'microsoft-entra-id' | null
@Column({ type: 'varchar', length: 255, nullable: true })
ssoSubjectId!: string | null // Azure AD Object ID
@Column({ type: 'varchar', length: 50, default: 'credentials' })
loginMethod!: string // 'credentials' | 'sso'
@CreateDateColumn()
createdAt!: Date
@UpdateDateColumn()
updatedAt!: Date
// Lifecycle hook: hash password before insert/update
@BeforeInsert()
@BeforeUpdate()
async hashPassword(): Promise<void> {
if (this.passwordHash && !this.passwordHash.startsWith('$2')) {
this.passwordHash = await bcrypt.hash(this.passwordHash, 12)
}
}
// Instance method: verify password
async verifyPassword(password: string): Promise<boolean> {
if (!this.passwordHash) return false
return bcrypt.compare(password, this.passwordHash)
}
// Instance method: check account lockout
isLocked(): boolean {
if (!this.lockedUntil) return false
return new Date() < this.lockedUntil
}
// Instance method: lock after failed attempts
incrementFailedAttempts(): void {
this.failedLoginAttempts++
if (this.failedLoginAttempts >= 5) {
this.lockedUntil = new Date(Date.now() + 15 * 60 * 1000)
this.status = 'locked'
}
}
// Instance method: reset on successful login
resetFailedAttempts(): void {
this.failedLoginAttempts = 0
this.lockedUntil = null
if (this.status === 'locked') {
this.status = 'active'
}
}
// Instance method: record login metadata
recordLogin(ip: string): void {
this.lastLoginAt = new Date()
this.lastLoginIp = ip
this.resetFailedAttempts()
}
}
Key patterns:
PrimaryGeneratedColumn('uuid')-- UUIDs for all primary keysselect: falseon sensitive fields (password hashes, secrets)@BeforeInsert()/@BeforeUpdate()for automatic password hashing (bcrypt cost 12)- Instance methods for domain logic (lockout, password verification)
!definite assignment assertions on all fields (required by TypeORM + strict TypeScript)- Nullable columns use
Type | nullunion types
Example 3: Service with Tenant-Aware Repositoryβ
This pattern shows how services access the database in a multi-tenant-safe way:
// backend/src/services/myFeature.service.ts (pattern from codebase)
import { getTenantAwareRepository } from '../multi-tenant/tenant-local-storage.js'
import { MyEntity } from '../entities/index.js'
import { ILike } from 'typeorm'
class MyFeatureService {
/**
* Get all items for the current tenant.
* getTenantAwareRepository automatically resolves the correct schema.
*/
async getAll(filters?: { search?: string; status?: string }): Promise<MyEntity[]> {
const repo = getTenantAwareRepository(MyEntity)
const where: any = {}
if (filters?.search) {
where.name = ILike(`%${filters.search}%`)
}
if (filters?.status) {
where.status = filters.status
}
return repo.find({
where,
order: { createdAt: 'DESC' },
})
}
/**
* Get a single item by ID.
*/
async getById(id: string): Promise<MyEntity | null> {
const repo = getTenantAwareRepository(MyEntity)
return repo.findOne({ where: { id } })
}
/**
* Create a new item.
*/
async create(data: Partial<MyEntity>): Promise<MyEntity> {
const repo = getTenantAwareRepository(MyEntity)
const entity = repo.create(data)
return repo.save(entity)
}
/**
* Update an existing item.
* Returns null if not found.
*/
async update(id: string, data: Partial<MyEntity>): Promise<MyEntity | null> {
const repo = getTenantAwareRepository(MyEntity)
const existing = await repo.findOne({ where: { id } })
if (!existing) return null
Object.assign(existing, data)
return repo.save(existing)
}
/**
* Delete an item by ID.
*/
async delete(id: string): Promise<boolean> {
const repo = getTenantAwareRepository(MyEntity)
const result = await repo.delete(id)
return (result.affected ?? 0) > 0
}
}
// Singleton export
export const myFeatureService = new MyFeatureService()
Key patterns:
- Always call
getTenantAwareRepository()inside the method, not as a class-level field. The tenant context is request-scoped (viaAsyncLocalStorage), so the repository must be resolved per-call. - Singleton export at the bottom (
export const myFeatureService = new MyFeatureService()) - Services never import
RequestorResponse-- they are framework-agnostic - Use TypeORM operators like
ILikefor search queries
Example 4: Route with Zod Validationβ
This pattern is taken from backend/src/routes/dashboard.routes.ts, showing request body validation:
// backend/src/routes/dashboard.routes.ts (actual pattern)
import { z } from 'zod'
const ALLOWED_DATA_SOURCES = [
'vulnerabilities', 'assets', 'risk_scores', 'compliance',
'remediation', 'sla', 'trends', 'appsec',
] as const
const ALLOWED_CHART_TYPES = [
'kpi', 'bar', 'line', 'doughnut', 'gauge', 'table',
'matrix', 'stacked_bar', 'area', 'pie', 'number',
] as const
const widgetDataSchema = z.object({
dataSource: z.enum(ALLOWED_DATA_SOURCES),
chartType: z.enum(ALLOWED_CHART_TYPES).optional(),
groupBy: z.string().max(50).optional(),
groupBySecondary: z.string().max(50).optional(),
filters: z.record(z.string(), z.unknown()).optional(),
sort: z.object({
field: z.string().max(50),
order: z.enum(['ASC', 'DESC']),
}).optional(),
limit: z.number().int().min(1).max(1000).optional(),
metric: z.string().max(50).optional(),
days: z.number().int().min(1).max(730).optional(),
}).passthrough()
// Usage in a route handler:
router.post('/widget-data', async (req: AuthenticatedRequest, res: Response) => {
try {
const parsed = widgetDataSchema.parse(req.body)
// Use parsed.dataSource, parsed.chartType, etc.
// ...
} catch (err: any) {
if (err instanceof z.ZodError) {
return res.status(400).json({
error: 'Validation failed',
details: err.errors,
})
}
res.status(500).json({ error: err.message })
}
})
Key patterns:
- Define allowed values as
as constarrays for type-safe enums - Use
z.enum()for whitelisting -- prevents injection through unvalidated input .max(50)on string fields to prevent oversized inputs.passthrough()to allow additional fields without failing validation- Catch
ZodErrorspecifically and return structured error details
Example 5: Rate Limiter Configurationβ
This pattern is from backend/src/routes/scan.routes.ts, showing per-route rate limiting:
// backend/src/routes/scan.routes.ts (actual code)
import rateLimit from 'express-rate-limit'
// Per-user rate limiter for scan launches
const scanLaunchLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 10, // 10 launches per minute per user
keyGenerator: (req: any) => req.user?.userId || 'anon',
message: { error: 'Too many scan launches. Maximum 10 per minute.' },
standardHeaders: true,
legacyHeaders: false,
validate: { xForwardedForHeader: false, default: true },
})
const bulkOperationLimiter = rateLimit({
windowMs: 60 * 1000,
max: 5, // 5 bulk operations per minute per user
keyGenerator: (req: any) => req.user?.userId || 'anon',
message: { error: 'Too many bulk operations. Maximum 5 per minute.' },
standardHeaders: true,
legacyHeaders: false,
validate: { xForwardedForHeader: false, default: true },
})
// Apply to specific routes
router.post('/launch', scanLaunchLimiter, async (req, res) => { ... })
router.post('/bulk-action', bulkOperationLimiter, async (req, res) => { ... })
Key patterns:
keyGeneratorusesreq.user?.userIdfor per-user limiting (not per-IP)validate: { xForwardedForHeader: false }to avoid warnings behind reverse proxies- Different limits for different operation types (reads vs. writes vs. bulk)
- JSON error messages for API consumers
Example 6: Scanner Agent (Attack Agent Pattern)β
This pattern is from the actual agent architecture in backend/src/services/appsec/agents/:
// backend/src/services/appsec/agents/myAgent.agent.ts (pattern)
import {
BaseAttackAgent,
AgentFinding,
} from './baseAttackAgent.js'
import type { SharedBlackboard } from '../sharedBlackboard.service.js'
import type { PentestAiAdapter } from '../pentestAiAdapter.service.js'
import type { ScannerPolicy } from '../scannerConstants.js'
class MyVulnAgent extends BaseAttackAgent {
constructor() {
super()
this.agentName = 'my_vuln_agent'
}
async run(
blackboard: SharedBlackboard,
aiAdapter: PentestAiAdapter,
policy: ScannerPolicy,
): Promise<AgentFinding[]> {
const findings: AgentFinding[] = []
const endpoints = blackboard.getEndpoints()
for (const ep of endpoints) {
// Budget check -- stop if we've used our allocation
if (this.isOverBudget()) break
// Skip endpoints outside scope
if (!this.isInScope(ep.url, policy)) continue
// Send a probe request
const response = await this.probe({
url: ep.url,
method: ep.method,
headers: this.getDefaultHeaders(),
})
// Classify the response (WAF blocked? Error? Success?)
const classification = this.classifyResponse(response)
if (classification.isWafBlocked) continue
if (classification.isRateLimited) {
await this.backoff(classification.retryAfter)
continue
}
// Analyze for vulnerability indicators
// ... your detection logic ...
// Record finding if vulnerability confirmed
findings.push({
type: 'my_vuln_type',
severity: 'high',
title: 'Vulnerability Detected',
description: 'Description of the vulnerability...',
endpoint: ep.url,
method: ep.method,
parameter: 'paramName',
payload: 'test-payload',
evidence: {
request: response.requestDetails,
response: response.body.substring(0, 500),
proofType: 'deterministic',
},
confidence: 0.85,
cweId: 'CWE-79',
owaspCategory: 'A03:2021',
remediation: 'How to fix this vulnerability...',
})
}
return findings
}
}
export const myVulnAgent = new MyVulnAgent()
Key patterns:
- Extend
BaseAttackAgentand implementrun() - Use
this.isOverBudget()to respect resource limits - Use
this.classifyResponse()to detect WAF blocks and rate limiting - Return
AgentFinding[]with structured evidence - Singleton export matches the coordinator's import pattern
Example 7: Multi-Tenant Aware Queryβ
This pattern shows how to use raw SQL queries in a tenant-aware context:
// Using getTenantAwareQuery for raw SQL
import {
getTenantAwareRepository,
getTenantAwareQuery,
} from '../multi-tenant/tenant-local-storage.js'
// Repository approach (preferred for CRUD)
async function getUserCount(): Promise<number> {
const repo = getTenantAwareRepository(User)
return repo.count()
}
// Raw query approach (for complex aggregations)
async function getVulnStats(): Promise<any> {
const query = getTenantAwareQuery()
const results = await query(
`SELECT severity, COUNT(*) as count
FROM vulnerabilities
WHERE status = $1
GROUP BY severity`,
['open']
)
return results
}
Key patterns:
getTenantAwareRepository()for standard TypeORM operationsgetTenantAwareQuery()for raw SQL -- the returned function automatically uses the tenant-scoped EntityManager (which hassearch_pathset to the correct schema)- Both fall back to
AppDataSourcewhen called outside a tenant request context (background jobs, startup)
Project File Structure Referenceβ
backend/
src/
config/ # env.ts, database.ts, redis.ts
entities/ # TypeORM entities (100+ files)
middleware/ # auth.ts, validation.ts, moduleGate.ts
multi-tenant/ # schema-manager.ts, tenant-local-storage.ts
routes/ # Express route files (40+ files)
services/ # Business logic services
appsec/ # AppSec scanner module
agents/ # 56+ attack agents
scripts/ # Migration scripts
utils/ # Utility functions
types/ # TypeScript type definitions
index.ts # Application entry point
frontend/
src/
components/ # Reusable UI components
pages/ # Page-level components
services/ # API client functions
stores/ # Zustand state stores
hooks/ # Custom React hooks
App.tsx # Route definitions