Development Standards
Development Standards
Section titled “Development Standards”Version: 1.0.13
Status: Active
Category: Development Standards
Table of Contents
Section titled “Table of Contents”- Overview
- Code Standards (CS:TS)
- Testing Standards (TS:JEST)
- Security Standards (SEC:API)
- NIST Compliance (NIST-IG)
- Container Standards (CN:DOCKER)
- Browser Automation Standards
- Quick Reference
Overview
Section titled “Overview”This document details the coding and security standards for the puppeteer-mcp project. All code must comply with these standards, which are based on William Zujkowski’s standards repository (https://github.com/williamzujkowski/standards).
Compliance Status
Section titled “Compliance Status”- Code Standards: ✅ ACHIEVED
- Testing Standards: ✅ IMPLEMENTED
- Security Standards: ✅ COMPREHENSIVE IMPLEMENTATION
- NIST Compliance: ✅ FULLY IMPLEMENTED
- Container Standards: ✅ BETA RELEASE
Code Standards (CS:TS)
Section titled “Code Standards (CS:TS)”TypeScript Configuration
Section titled “TypeScript Configuration”// tsconfig.json requirements{ "compilerOptions": { "target": "ES2020", "module": "ESNext", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true }}
File Organization
Section titled “File Organization”- Maximum Lines: 300 lines per file
- Single Responsibility: One primary export per file
- Barrel Exports: Use index.ts for public APIs
// GOOD: Focused file with single responsibility// auth-handler.ts (250 lines)export class AuthHandler { // Implementation}
// BAD: Multiple responsibilities in one file// everything.ts (500+ lines)export class AuthHandler {}export class SessionManager {}export class TokenValidator {}
Function Complexity
Section titled “Function Complexity”- Maximum Complexity: 10 (cyclomatic complexity)
- Extract Helper Functions: Break down complex logic
// GOOD: Extracted helper functionsfunction validateBrowserAction(action: BrowserAction): ValidationResult { if (!isValidActionType(action.type)) { return { valid: false, error: 'Invalid action type' }; }
if (!hasRequiredParams(action)) { return { valid: false, error: 'Missing required parameters' }; }
return validateActionSecurity(action);}
// Helper functions keep complexity lowfunction isValidActionType(type: string): boolean { return VALID_ACTION_TYPES.includes(type);}
function hasRequiredParams(action: BrowserAction): boolean { const schema = ACTION_SCHEMAS[action.type]; return schema ? schema.safeParse(action.params).success : false;}
Parameter Count
Section titled “Parameter Count”- Maximum Parameters: 4
- Use Interfaces: Group related parameters
// GOOD: Interface for grouped parametersinterface CreateSessionOptions { userId: string; permissions: Permission[]; metadata?: SessionMetadata; expiresIn?: number;}
function createSession(options: CreateSessionOptions): Session { // Implementation}
// BAD: Too many parametersfunction createSession( userId: string, permissions: Permission[], metadata: SessionMetadata, expiresIn: number, ipAddress: string, userAgent: string,): Session { // Hard to use and maintain}
Naming Conventions
Section titled “Naming Conventions”// Classes and Interfaces: PascalCaseclass BrowserPool {}interface SessionStore {}
// Functions and Variables: camelCasefunction executeAction() {}const maxRetries = 3;
// Constants: UPPER_SNAKE_CASEconst MAX_POOL_SIZE = 5;const DEFAULT_TIMEOUT = 30000;
// Private members: underscore prefixclass Service { private _internalState: State;}
// Type parameters: single letter or descriptivetype Result<T> = { data: T } | { error: Error };type KeyValue<TKey, TValue> = Map<TKey, TValue>;
Documentation
Section titled “Documentation”All public APIs must have JSDoc documentation:
/** * Executes a browser action within a security context * * @param sessionId - The session requesting the action * @param action - The browser action to execute * @returns Promise resolving to action result * @throws {SecurityError} If action validation fails * @throws {ResourceError} If browser pool is exhausted * * @example * ```typescript * const result = await executeAction('session-123', { * type: 'navigate', * params: { url: 'https://example.com' } * }); * ``` */export async function executeAction( sessionId: string, action: BrowserAction,): Promise<ActionResult> { // Implementation}
Architecture Principles
Section titled “Architecture Principles”-
SOLID Principles
- Single Responsibility
- Open/Closed
- Liskov Substitution
- Interface Segregation
- Dependency Inversion
-
Dependency Injection
// GOOD: Dependencies injectedclass SessionService {constructor(private store: SessionStore,private auth: AuthService,private logger: Logger,) {}} -
Clear Separation of Concerns
- Business logic separate from infrastructure
- Protocol handlers separate from core logic
- Security checks in dedicated middleware
Testing Standards (TS:JEST)
Section titled “Testing Standards (TS:JEST)”Coverage Requirements
Section titled “Coverage Requirements”{ "jest": { "coverageThreshold": { "global": { "branches": 80, "functions": 85, "lines": 85, "statements": 85 }, "./src/auth/**/*.ts": { "branches": 95, "functions": 95, "lines": 95, "statements": 95 }, "./src/utils/**/*.ts": { "branches": 100, "functions": 100, "lines": 100, "statements": 100 } } }}
Test Types
Section titled “Test Types”1. Hypothesis Tests
Section titled “1. Hypothesis Tests”Test specific behaviors and assumptions:
describe('SessionStore', () => { it('should create unique session IDs', async () => { const store = new SessionStore(); const ids = await Promise.all( Array(1000) .fill(0) .map(() => store.createSession()), ); const uniqueIds = new Set(ids); expect(uniqueIds.size).toBe(1000); });});
2. Regression Tests
Section titled “2. Regression Tests”Prevent bugs from reoccurring:
describe('PageManager', () => { it('should use consistent page- prefix for IDs (regression #123)', () => { const pageId = pageManager.generatePageId(); expect(pageId).toMatch(/^page-[a-z0-9]+$/); // Bug: Was returning 'browser-' prefix });});
3. Benchmark Tests
Section titled “3. Benchmark Tests”Ensure performance SLAs:
describe('Performance', () => { it('should acquire browser in <1s', async () => { const start = Date.now(); const browser = await pool.acquire(); const duration = Date.now() - start;
expect(duration).toBeLessThan(1000); await pool.release(browser); });});
4. Property-Based Tests
Section titled “4. Property-Based Tests”Test edge cases with generated data:
describe('Input Validation', () => { it.each([ ['empty string', ''], ['null', null], ['undefined', undefined], ['XSS attempt', '<script>alert(1)</script>'], ['SQL injection', "'; DROP TABLE users; --"], ])('should reject invalid input: %s', (_, input) => { expect(() => validateInput(input)).toThrow(); });});
Test-First Development
Section titled “Test-First Development”-
Write failing test first
it('should authenticate API key', async () => {const result = await authenticateApiKey('valid-key');expect(result.authenticated).toBe(true);}); -
Implement minimal code to pass
async function authenticateApiKey(key: string) {return { authenticated: true };} -
Refactor and enhance
async function authenticateApiKey(key: string) {const apiKey = await store.getApiKey(key);if (!apiKey || apiKey.revoked) {return { authenticated: false };}return { authenticated: true, userId: apiKey.userId };}
Security Standards (SEC:API)
Section titled “Security Standards (SEC:API)”Zero Trust Architecture
Section titled “Zero Trust Architecture”Never trust any input or caller:
// Every request must be authenticatedapp.use('/api/*', authenticate);
// Every action must be authorizedapp.use('/api/*', authorize);
// Every input must be validatedapp.use('/api/*', validateInput);
Authentication Implementation
Section titled “Authentication Implementation”// JWT with proper verificationimport jwt from 'jsonwebtoken';
export async function verifyToken(token: string): Promise<TokenPayload> { try { return jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'], maxAge: '24h', }) as TokenPayload; } catch (error) { throw new AuthenticationError('Invalid token'); }}
// API Key authenticationexport async function verifyApiKey(key: string): Promise<ApiKeyInfo> { const hash = crypto.createHash('sha256').update(key).digest('hex'); const apiKey = await store.getApiKeyByHash(hash);
if (!apiKey || apiKey.revoked || apiKey.expiresAt < new Date()) { throw new AuthenticationError('Invalid API key'); }
return apiKey;}
Required Security Headers
Section titled “Required Security Headers”import helmet from 'helmet';
app.use( helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "'unsafe-inline'"], styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", 'data:', 'https:'], }, }, hsts: { maxAge: 31536000, includeSubDomains: true, preload: true, }, noSniff: true, xssFilter: true, referrerPolicy: { policy: 'same-origin' }, permissionsPolicy: { features: { geolocation: ["'none'"], camera: ["'none'"], microphone: ["'none'"], }, }, }),);
Input Validation
Section titled “Input Validation”All inputs must be validated with Zod schemas:
import { z } from 'zod';
// Define schemas for all inputsconst CreateSessionSchema = z.object({ userId: z.string().uuid(), permissions: z.array(z.enum(['read', 'write', 'admin'])), metadata: z .object({ ipAddress: z.string().ip(), userAgent: z.string().max(500), }) .optional(),});
// Validation middlewareexport function validateBody(schema: z.ZodSchema) { return (req: Request, res: Response, next: NextFunction) => { const result = schema.safeParse(req.body); if (!result.success) { return res.status(400).json({ error: 'Validation failed', details: result.error.issues, }); } req.body = result.data; next(); };}
// Usageapp.post('/sessions', validateBody(CreateSessionSchema), async (req, res) => { // req.body is now typed and validated});
Rate Limiting
Section titled “Rate Limiting”Implement per-endpoint rate limits:
import rateLimit from 'express-rate-limit';
// Global rate limitapp.use( rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs message: 'Too many requests', }),);
// Strict limit for auth endpointsapp.use( '/auth/*', rateLimit({ windowMs: 15 * 60 * 1000, max: 5, skipSuccessfulRequests: true, }),);
// Lenient limit for read operationsapp.use( '/api/*/read', rateLimit({ windowMs: 1 * 60 * 1000, max: 60, }),);
NIST Compliance (NIST-IG)
Section titled “NIST Compliance (NIST-IG)”Required Control Tags
Section titled “Required Control Tags”All security-related functions must be tagged with NIST controls:
/** * Authenticates a user request * * @nist ia-2 "Identification and Authentication (Organizational Users)" * @nist ia-5 "Authenticator Management" * @nist ac-3 "Access Enforcement" * @evidence code - JWT validation implementation * @evidence test - auth.test.ts covers all paths */export async function authenticateRequest(req: Request): Promise<User> { const token = extractToken(req); const payload = await verifyToken(token); return getUserFromPayload(payload);}
/** * Logs security events * * @nist au-3 "Content of Audit Records" * @nist au-4 "Audit Storage Capacity" * @nist au-9 "Protection of Audit Information" * @evidence code - Structured logging with tamper protection * @evidence test - audit-log.test.ts */export function logSecurityEvent(event: SecurityEvent): void { logger.security({ timestamp: new Date().toISOString(), eventType: event.type, userId: event.userId, ipAddress: event.ipAddress, outcome: event.outcome, details: event.details, });}
Common NIST Controls
Section titled “Common NIST Controls”Control | Description | Implementation |
---|---|---|
AC-3 | Access Enforcement | Role-based access control |
AU-3 | Content of Audit Records | Structured security logging |
IA-2 | Identification and Authentication | Multi-factor authentication |
IA-5 | Authenticator Management | Password policies, key rotation |
SC-18 | Mobile Code | JavaScript execution controls |
SI-10 | Information Input Validation | Zod schemas, sanitization |
Container Standards (CN:DOCKER)
Section titled “Container Standards (CN:DOCKER)”Multi-Stage Builds
Section titled “Multi-Stage Builds”# Build stageFROM node:20-alpine AS builderWORKDIR /appCOPY package*.json ./RUN npm ci --only=production
# Security scan stageFROM builder AS securityRUN npm audit --productionRUN npm install -g snykRUN snyk test
# Production stageFROM node:20-alpine AS productionRUN apk add --no-cache dumb-initRUN addgroup -g 1001 -S nodejsRUN adduser -S nodejs -u 1001
WORKDIR /appCOPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modulesCOPY --chown=nodejs:nodejs . .
USER nodejsEXPOSE 3000ENTRYPOINT ["dumb-init", "--"]CMD ["node", "dist/index.js"]
Security Best Practices
Section titled “Security Best Practices”-
Non-root user execution
USER nodejs -
Health checks
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \CMD node healthcheck.js -
Graceful shutdown
process.on('SIGTERM', async () => {logger.info('SIGTERM received, shutting down gracefully');await server.close();await browserPool.shutdown();process.exit(0);}); -
Read-only root filesystem
# In docker-compose.ymlservices:app:read_only: truetmpfs:- /tmp- /app/logs
Browser Automation Standards
Section titled “Browser Automation Standards”Security Validation
Section titled “Security Validation”All browser actions must be validated:
/** * @nist si-10 "Information Input Validation" * @nist sc-18 "Mobile Code" */export function validateBrowserAction(action: BrowserAction): ValidationResult { // Check action type if (!ALLOWED_ACTIONS.includes(action.type)) { return { valid: false, error: 'Forbidden action type' }; }
// Validate parameters const schema = ACTION_SCHEMAS[action.type]; const result = schema.safeParse(action.params);
if (!result.success) { return { valid: false, error: 'Invalid parameters' }; }
// Security checks for evaluate action if (action.type === 'evaluate') { const script = action.params.script; if (containsDangerousCode(script)) { return { valid: false, error: 'Potentially dangerous code detected' }; } }
return { valid: true };}
function containsDangerousCode(script: string): boolean { const dangerous = [ 'eval', 'Function', 'setTimeout', 'setInterval', 'document.write', 'innerHTML', 'outerHTML', ]; return dangerous.some((keyword) => script.includes(keyword));}
Resource Management
Section titled “Resource Management”// Browser pool configurationexport const BROWSER_POOL_CONFIG = { maxSize: 5, // Maximum concurrent browsers minSize: 1, // Minimum ready browsers acquireTimeout: 30000, // Max wait for browser createTimeout: 30000, // Max time to launch browser idleTimeout: 300000, // Idle browser cleanup validateOnBorrow: true, // Health check before use};
// Automatic cleanupexport async function cleanupIdleBrowsers(): Promise<void> { const idleBrowsers = await pool.getIdleBrowsers();
for (const browser of idleBrowsers) { if (browser.idleTime > BROWSER_POOL_CONFIG.idleTimeout) { await browser.close(); pool.remove(browser); } }}
Performance Monitoring
Section titled “Performance Monitoring”// Track all browser actionsexport async function trackAction(action: BrowserAction, duration: number): Promise<void> { metrics.histogram('browser.action.duration', duration, { action: action.type, });
if (duration > ACTION_SLA[action.type]) { logger.warn('Browser action exceeded SLA', { action: action.type, duration, sla: ACTION_SLA[action.type], }); }}
Quick Reference
Section titled “Quick Reference”Pre-Commit Checklist
Section titled “Pre-Commit Checklist”Before committing code, ensure:
- TypeScript compiles without errors
- ESLint passes with no errors
- All tests pass
- Functions have complexity ≤10
- Files have ≤300 lines
- Public APIs have JSDoc comments
- Security functions have NIST tags
- Inputs are validated with Zod
- No secrets in code
- Dependencies audited
Common Commands
Section titled “Common Commands”# Check TypeScript compilationnpm run typecheck
# Run ESLintnpm run lint
# Run tests with coveragenpm run test:coverage
# Audit dependenciesnpm audit
# Check function complexitynpm run complexity
# Full pre-commit checknpm run precommit
Standards Compliance
Section titled “Standards Compliance”Standard | Status | Key Requirements |
---|---|---|
CS:TS | ✅ | TypeScript strict, ≤300 lines/file, ≤10 complexity |
TS:JEST | ✅ | 85%+ coverage, test-first development |
SEC:API | ✅ | Zero trust, Zod validation, rate limiting |
NIST-IG | ✅ | Control tags on security functions |
CN:DOCKER | ✅ | Multi-stage builds, non-root user |
For additional context and examples, see:
docs/lessons/implementation.md
- Real-world examplesdocs/development/workflow.md
- Development process- Project Standards Repository: https://github.com/williamzujkowski/standards