javascript
exercises
exercises.js⚡javascript
/**
* 21.2 Secure Coding Practices - Exercises
*
* Practice implementing secure coding patterns
*/
/**
* Exercise 1: Advanced Input Validator
*
* Build a comprehensive input validation system that supports:
* - Nested object validation
* - Array item validation
* - Cross-field validation
* - Custom sanitization
* - Internationalization support
*
* Requirements:
* 1. Validate nested objects recursively
* 2. Support conditional required fields
* 3. Support cross-field validation rules
* 4. Provide detailed error paths
* 5. Support async validators
*/
class AdvancedValidator {
constructor() {
// TODO: Initialize validator state
this.schemas = new Map();
}
// Register a schema
registerSchema(name, schema) {
// TODO: Store schema for reuse
}
// Validate data against schema
async validate(data, schema) {
// TODO: Implement validation logic
// Return { valid, errors, sanitized }
}
// Validate a single field
async validateField(value, rules, path, context) {
// TODO: Validate individual field
// Handle all rule types
}
// Cross-field validation
validateCrossFields(data, crossRules) {
// TODO: Validate relationships between fields
}
}
// Test your implementation
function testAdvancedValidator() {
const validator = new AdvancedValidator();
const addressSchema = {
street: { type: 'string', required: true },
city: { type: 'string', required: true },
zip: { type: 'string', pattern: /^\d{5}(-\d{4})?$/ },
};
const orderSchema = {
customer: {
type: 'object',
required: true,
properties: {
name: { type: 'string', required: true },
email: { type: 'email', required: true },
},
},
items: {
type: 'array',
required: true,
minItems: 1,
items: {
productId: { type: 'string', required: true },
quantity: { type: 'number', required: true, min: 1 },
},
},
shippingAddress: {
type: 'object',
required: true,
schema: addressSchema,
},
billingAddress: {
type: 'object',
requiredIf: (data) => !data.sameAsShipping,
},
sameAsShipping: { type: 'boolean', default: false },
};
// Test cases
console.log('Testing Advanced Validator...');
// Add your tests here
}
// Uncomment to test
// testAdvancedValidator();
/**
* Exercise 2: Secure Token Manager
*
* Implement a secure token management system for:
* - JWT generation and validation
* - Refresh token rotation
* - Token revocation
* - Rate limiting by token
*
* Requirements:
* 1. Generate cryptographically secure tokens
* 2. Implement token expiration
* 3. Support token refresh with rotation
* 4. Maintain a revocation list
* 5. Implement sliding expiration
*/
class SecureTokenManager {
constructor(options = {}) {
// TODO: Initialize with secret and options
this.secret = options.secret;
this.accessTokenTTL = options.accessTokenTTL || 15 * 60 * 1000; // 15 minutes
this.refreshTokenTTL = options.refreshTokenTTL || 7 * 24 * 60 * 60 * 1000; // 7 days
this.revokedTokens = new Set();
this.refreshTokens = new Map();
}
// Generate access token
generateAccessToken(payload) {
// TODO: Create JWT-like token structure
// Include: sub, iat, exp, jti (token ID)
}
// Generate refresh token
generateRefreshToken(userId) {
// TODO: Create secure refresh token
// Store mapping to user
}
// Verify and decode token
verifyAccessToken(token) {
// TODO: Verify signature and expiration
// Check revocation list
}
// Refresh access token
refreshAccessToken(refreshToken) {
// TODO: Validate refresh token
// Rotate refresh token (issue new, revoke old)
// Return new access + refresh tokens
}
// Revoke token
revokeToken(token) {
// TODO: Add to revocation list
}
// Revoke all tokens for user
revokeAllUserTokens(userId) {
// TODO: Revoke all refresh tokens for user
}
// Create HMAC signature
sign(data) {
// TODO: Create HMAC-SHA256 signature
}
// Verify HMAC signature
verifySignature(data, signature) {
// TODO: Timing-safe signature verification
}
}
// Test your implementation
function testSecureTokenManager() {
const tokenManager = new SecureTokenManager({
secret: 'test-secret-key-32-bytes-long!!',
});
console.log('Testing Secure Token Manager...');
// Generate tokens
const accessToken = tokenManager.generateAccessToken({
sub: 'user123',
role: 'admin',
});
console.log('Access token:', accessToken);
const refreshToken = tokenManager.generateRefreshToken('user123');
console.log('Refresh token:', refreshToken);
// Verify token
const verified = tokenManager.verifyAccessToken(accessToken);
console.log('Verified:', verified);
// Refresh token
const newTokens = tokenManager.refreshAccessToken(refreshToken);
console.log('New tokens:', newTokens);
}
// Uncomment to test
// testSecureTokenManager();
/**
* Exercise 3: Secure Form Handler
*
* Create a comprehensive form security handler that:
* - Validates CSRF tokens
* - Sanitizes all inputs
* - Prevents double submission
* - Implements honeypot fields
* - Logs security events
*
* Requirements:
* 1. Generate and validate CSRF tokens
* 2. Detect and block bot submissions
* 3. Implement submission rate limiting
* 4. Track and prevent replay attacks
*/
class SecureFormHandler {
constructor(options = {}) {
// TODO: Initialize form handler
this.csrfTokens = new Map();
this.submissionLog = new Map();
this.honeypotField = options.honeypotField || '_hp_field';
}
// Generate form with CSRF token
generateForm(formId, userId) {
// TODO: Create CSRF token
// Return token for inclusion in form
}
// Validate form submission
validateSubmission(formId, data, userId, meta = {}) {
// TODO: Validate CSRF token
// Check honeypot
// Check for double submission
// Check rate limiting
// Return validation result
}
// Check honeypot field
checkHoneypot(data) {
// TODO: Verify honeypot is empty
// Filled honeypot = bot
}
// Check submission timing
checkSubmissionTiming(meta) {
// TODO: Check if form was submitted too quickly
// Very fast submission = likely bot
}
// Rate limit submissions
checkRateLimit(userId, formId) {
// TODO: Limit submissions per user per time window
}
// Log security event
logSecurityEvent(event, details) {
// TODO: Log for security monitoring
}
}
// Test your implementation
function testSecureFormHandler() {
const formHandler = new SecureFormHandler();
console.log('Testing Secure Form Handler...');
// Generate form token
const token = formHandler.generateForm('contact-form', 'user123');
console.log('Form token:', token);
// Valid submission
const validResult = formHandler.validateSubmission(
'contact-form',
{ name: 'John', email: 'john@example.com', csrfToken: token },
'user123',
{ submitTime: Date.now() - 5000, loadTime: Date.now() - 10000 }
);
console.log('Valid submission:', validResult);
// Bot submission (honeypot filled)
const botResult = formHandler.validateSubmission(
'contact-form',
{
name: 'Bot',
email: 'bot@spam.com',
_hp_field: 'spam content',
csrfToken: token,
},
'bot-user',
{ submitTime: Date.now() - 100 }
);
console.log('Bot submission:', botResult);
}
// Uncomment to test
// testSecureFormHandler();
/**
* Exercise 4: Secure Data Store
*
* Implement a secure in-memory data store that:
* - Encrypts sensitive data at rest
* - Implements access control
* - Audit logs all access
* - Supports data expiration
* - Prevents information leakage
*
* Requirements:
* 1. Field-level encryption for sensitive data
* 2. Role-based access control
* 3. Complete audit trail
* 4. Secure data deletion
*/
class SecureDataStore {
constructor(options = {}) {
// TODO: Initialize secure store
this.data = new Map();
this.encryptionKey = options.encryptionKey;
this.accessControl = new Map();
this.auditLog = [];
}
// Define access rules
defineAccess(resource, rules) {
// TODO: Set up access control rules
// { read: ['admin', 'user'], write: ['admin'] }
}
// Store data securely
set(key, value, options = {}) {
// TODO: Encrypt sensitive fields
// Set TTL if provided
// Log the operation
}
// Retrieve data
get(key, accessor) {
// TODO: Check access permissions
// Decrypt if needed
// Log the access
}
// Delete data securely
delete(key, accessor) {
// TODO: Check permissions
// Securely delete (overwrite before removal)
// Log the deletion
}
// Encrypt value
encrypt(value) {
// TODO: Encrypt using AES-256-GCM or similar
}
// Decrypt value
decrypt(encrypted) {
// TODO: Decrypt and verify integrity
}
// Check access permission
checkAccess(key, accessor, operation) {
// TODO: Verify accessor has permission
}
// Get audit log
getAuditLog(filter = {}) {
// TODO: Return filtered audit log
}
}
// Test your implementation
function testSecureDataStore() {
const store = new SecureDataStore({
encryptionKey: 'test-encryption-key-32-bytes!!',
});
console.log('Testing Secure Data Store...');
// Define access rules
store.defineAccess('user-data', {
read: ['admin', 'owner'],
write: ['owner'],
});
// Store sensitive data
store.set(
'user:123',
{
name: 'John Doe',
ssn: '123-45-6789',
creditCard: '4111111111111111',
},
{
sensitiveFields: ['ssn', 'creditCard'],
owner: 'user:123',
ttl: 3600000,
}
);
// Access as admin
const adminView = store.get('user:123', { role: 'admin', id: 'admin:1' });
console.log('Admin view:', adminView);
// Access as different user (should fail)
const otherView = store.get('user:123', { role: 'user', id: 'user:456' });
console.log('Other user view:', otherView);
}
// Uncomment to test
// testSecureDataStore();
/**
* Exercise 5: Security Middleware Pipeline
*
* Build a modular security middleware system that:
* - Applies multiple security checks in order
* - Allows custom middleware addition
* - Handles errors gracefully
* - Provides detailed security reports
*
* Requirements:
* 1. Composable middleware architecture
* 2. Built-in common security middleware
* 3. Async middleware support
* 4. Early termination on security failures
*/
class SecurityPipeline {
constructor() {
// TODO: Initialize middleware stack
this.middleware = [];
}
// Add middleware to pipeline
use(name, handler, options = {}) {
// TODO: Add middleware with priority support
}
// Execute pipeline
async execute(request, response) {
// TODO: Run all middleware in order
// Stop on first failure
// Return combined result
}
// Built-in: Rate Limiter middleware
static rateLimiter(options = {}) {
// TODO: Return rate limiting middleware
}
// Built-in: Input Sanitizer middleware
static inputSanitizer(options = {}) {
// TODO: Return input sanitization middleware
}
// Built-in: Authentication checker
static authChecker(options = {}) {
// TODO: Return auth checking middleware
}
// Built-in: CORS validator
static corsValidator(options = {}) {
// TODO: Return CORS validation middleware
}
// Built-in: Content Security Policy setter
static cspSetter(options = {}) {
// TODO: Return CSP header setting middleware
}
// Generate security report
generateReport(results) {
// TODO: Create detailed security report
}
}
// Test your implementation
function testSecurityPipeline() {
const pipeline = new SecurityPipeline();
console.log('Testing Security Pipeline...');
// Add middleware
pipeline.use(
'rate-limiter',
SecurityPipeline.rateLimiter({ maxRequests: 100 })
);
pipeline.use('sanitizer', SecurityPipeline.inputSanitizer());
pipeline.use('auth', SecurityPipeline.authChecker({ required: true }));
pipeline.use(
'cors',
SecurityPipeline.corsValidator({ origins: ['http://localhost:3000'] })
);
pipeline.use('csp', SecurityPipeline.cspSetter());
// Custom middleware
pipeline.use('custom-check', async (req, res, next) => {
if (req.headers['x-custom-token'] !== 'valid') {
return { passed: false, reason: 'Invalid custom token' };
}
return next();
});
// Test request
const request = {
ip: '192.168.1.1',
headers: {
authorization: 'Bearer valid-token',
origin: 'http://localhost:3000',
'x-custom-token': 'valid',
},
body: { name: '<script>alert("xss")</script>' },
};
const response = { headers: {} };
pipeline.execute(request, response).then((result) => {
console.log('Pipeline result:', result);
console.log('Response headers:', response.headers);
});
}
// Uncomment to test
// testSecurityPipeline();
// ============================================================
// SOLUTIONS (Uncomment to reveal)
// ============================================================
/*
// SOLUTION 1: Advanced Input Validator
class AdvancedValidatorSolution {
constructor() {
this.schemas = new Map();
this.customValidators = new Map();
}
registerSchema(name, schema) {
this.schemas.set(name, schema);
}
registerValidator(name, validator) {
this.customValidators.set(name, validator);
}
async validate(data, schema) {
const errors = [];
const sanitized = {};
for (const [field, rules] of Object.entries(schema)) {
const result = await this.validateField(
data[field],
rules,
field,
data
);
if (result.errors.length > 0) {
errors.push(...result.errors);
} else {
sanitized[field] = result.value;
}
}
// Cross-field validation
if (schema._crossValidation) {
const crossErrors = this.validateCrossFields(sanitized, schema._crossValidation);
errors.push(...crossErrors);
}
return { valid: errors.length === 0, errors, sanitized };
}
async validateField(value, rules, path, context) {
const errors = [];
let processedValue = value;
// Check required
const isRequired = typeof rules.required === 'function'
? rules.required(context)
: rules.required;
if (isRequired && (value === undefined || value === null || value === '')) {
return { errors: [{ path, message: `${path} is required` }], value };
}
if (value === undefined || value === null) {
return { errors: [], value: rules.default };
}
// Type validation
if (rules.type) {
const typeValid = this.checkType(value, rules.type);
if (!typeValid) {
errors.push({ path, message: `${path} must be of type ${rules.type}` });
return { errors, value };
}
}
// Nested object validation
if (rules.type === 'object' && rules.properties) {
const nestedResult = await this.validate(value, rules.properties);
if (!nestedResult.valid) {
for (const error of nestedResult.errors) {
errors.push({ path: `${path}.${error.path}`, message: error.message });
}
}
processedValue = nestedResult.sanitized;
}
// Array validation
if (rules.type === 'array') {
if (rules.minItems && value.length < rules.minItems) {
errors.push({ path, message: `${path} must have at least ${rules.minItems} items` });
}
if (rules.maxItems && value.length > rules.maxItems) {
errors.push({ path, message: `${path} must have at most ${rules.maxItems} items` });
}
if (rules.items) {
const validatedItems = [];
for (let i = 0; i < value.length; i++) {
const itemResult = await this.validate(value[i], rules.items);
if (!itemResult.valid) {
for (const error of itemResult.errors) {
errors.push({ path: `${path}[${i}].${error.path}`, message: error.message });
}
}
validatedItems.push(itemResult.sanitized);
}
processedValue = validatedItems;
}
}
// String validations
if (rules.type === 'string') {
if (rules.minLength && value.length < rules.minLength) {
errors.push({ path, message: `${path} must be at least ${rules.minLength} characters` });
}
if (rules.maxLength && value.length > rules.maxLength) {
errors.push({ path, message: `${path} must be at most ${rules.maxLength} characters` });
}
if (rules.pattern && !rules.pattern.test(value)) {
errors.push({ path, message: `${path} format is invalid` });
}
}
// Custom async validator
if (rules.validate) {
const customResult = await rules.validate(value, context);
if (customResult !== true) {
errors.push({ path, message: customResult || `${path} is invalid` });
}
}
// Sanitization
if (rules.sanitize && errors.length === 0) {
processedValue = rules.sanitize(processedValue);
}
return { errors, value: processedValue };
}
checkType(value, type) {
switch (type) {
case 'string': return typeof value === 'string';
case 'number': return typeof value === 'number' && !isNaN(value);
case 'boolean': return typeof value === 'boolean';
case 'array': return Array.isArray(value);
case 'object': return typeof value === 'object' && value !== null && !Array.isArray(value);
case 'email': return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
default: return true;
}
}
validateCrossFields(data, rules) {
const errors = [];
for (const rule of rules) {
const result = rule.validate(data);
if (result !== true) {
errors.push({ path: rule.fields.join(', '), message: result });
}
}
return errors;
}
}
// SOLUTION 2: Secure Token Manager
class SecureTokenManagerSolution {
constructor(options = {}) {
this.secret = options.secret || this.generateSecret();
this.accessTokenTTL = options.accessTokenTTL || 15 * 60 * 1000;
this.refreshTokenTTL = options.refreshTokenTTL || 7 * 24 * 60 * 60 * 1000;
this.revokedTokens = new Set();
this.refreshTokens = new Map();
}
generateSecret() {
return Array.from({ length: 32 }, () =>
Math.floor(Math.random() * 256).toString(16).padStart(2, '0')
).join('');
}
generateTokenId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
generateAccessToken(payload) {
const header = { alg: 'HS256', typ: 'JWT' };
const now = Date.now();
const tokenPayload = {
...payload,
iat: now,
exp: now + this.accessTokenTTL,
jti: this.generateTokenId()
};
const headerB64 = this.base64UrlEncode(JSON.stringify(header));
const payloadB64 = this.base64UrlEncode(JSON.stringify(tokenPayload));
const signature = this.sign(`${headerB64}.${payloadB64}`);
return `${headerB64}.${payloadB64}.${signature}`;
}
generateRefreshToken(userId) {
const tokenId = this.generateTokenId();
const token = {
id: tokenId,
userId,
createdAt: Date.now(),
expiresAt: Date.now() + this.refreshTokenTTL
};
this.refreshTokens.set(tokenId, token);
const tokenData = this.base64UrlEncode(JSON.stringify({ id: tokenId, userId }));
const signature = this.sign(tokenData);
return `${tokenData}.${signature}`;
}
verifyAccessToken(token) {
try {
const [headerB64, payloadB64, signature] = token.split('.');
// Verify signature
if (!this.verifySignature(`${headerB64}.${payloadB64}`, signature)) {
return { valid: false, error: 'Invalid signature' };
}
const payload = JSON.parse(this.base64UrlDecode(payloadB64));
// Check expiration
if (payload.exp < Date.now()) {
return { valid: false, error: 'Token expired' };
}
// Check revocation
if (this.revokedTokens.has(payload.jti)) {
return { valid: false, error: 'Token revoked' };
}
return { valid: true, payload };
} catch (error) {
return { valid: false, error: 'Invalid token format' };
}
}
refreshAccessToken(refreshToken) {
try {
const [tokenData, signature] = refreshToken.split('.');
if (!this.verifySignature(tokenData, signature)) {
return { success: false, error: 'Invalid refresh token' };
}
const { id, userId } = JSON.parse(this.base64UrlDecode(tokenData));
const storedToken = this.refreshTokens.get(id);
if (!storedToken) {
return { success: false, error: 'Refresh token not found' };
}
if (storedToken.expiresAt < Date.now()) {
this.refreshTokens.delete(id);
return { success: false, error: 'Refresh token expired' };
}
// Rotate: delete old, create new
this.refreshTokens.delete(id);
const newAccessToken = this.generateAccessToken({ sub: userId });
const newRefreshToken = this.generateRefreshToken(userId);
return {
success: true,
accessToken: newAccessToken,
refreshToken: newRefreshToken
};
} catch (error) {
return { success: false, error: 'Invalid refresh token format' };
}
}
revokeToken(token) {
const verified = this.verifyAccessToken(token);
if (verified.valid) {
this.revokedTokens.add(verified.payload.jti);
}
}
revokeAllUserTokens(userId) {
for (const [id, token] of this.refreshTokens.entries()) {
if (token.userId === userId) {
this.refreshTokens.delete(id);
}
}
}
sign(data) {
// Simple HMAC simulation (use crypto in production)
let hash = 0;
const combined = data + this.secret;
for (let i = 0; i < combined.length; i++) {
hash = ((hash << 5) - hash) + combined.charCodeAt(i);
hash = hash & hash;
}
return Math.abs(hash).toString(36);
}
verifySignature(data, signature) {
return this.sign(data) === signature;
}
base64UrlEncode(str) {
return Buffer.from(str).toString('base64')
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
base64UrlDecode(str) {
str = str.replace(/-/g, '+').replace(/_/g, '/');
while (str.length % 4) str += '=';
return Buffer.from(str, 'base64').toString();
}
}
// SOLUTION 3: Secure Form Handler
class SecureFormHandlerSolution {
constructor(options = {}) {
this.csrfTokens = new Map();
this.submissionLog = new Map();
this.honeypotField = options.honeypotField || '_hp_field';
this.minSubmitTime = options.minSubmitTime || 2000; // 2 seconds minimum
this.maxSubmissionsPerWindow = options.maxSubmissions || 5;
this.windowMs = options.windowMs || 60000;
this.tokenTTL = options.tokenTTL || 3600000; // 1 hour
this.securityLog = [];
}
generateForm(formId, userId) {
const token = this.generateToken();
const key = `${formId}:${userId}`;
this.csrfTokens.set(key, {
token,
createdAt: Date.now(),
expiresAt: Date.now() + this.tokenTTL,
used: false
});
return token;
}
generateToken() {
return Array.from({ length: 32 }, () =>
Math.floor(Math.random() * 16).toString(16)
).join('');
}
validateSubmission(formId, data, userId, meta = {}) {
const errors = [];
const key = `${formId}:${userId}`;
// 1. CSRF Token validation
const csrfResult = this.validateCsrfToken(key, data.csrfToken);
if (!csrfResult.valid) {
this.logSecurityEvent('CSRF_FAILURE', { formId, userId, reason: csrfResult.error });
errors.push({ type: 'csrf', message: csrfResult.error });
}
// 2. Honeypot check
if (!this.checkHoneypot(data)) {
this.logSecurityEvent('HONEYPOT_TRIGGERED', { formId, userId });
errors.push({ type: 'honeypot', message: 'Bot detected' });
}
// 3. Submission timing check
if (!this.checkSubmissionTiming(meta)) {
this.logSecurityEvent('SUSPICIOUS_TIMING', { formId, userId, meta });
errors.push({ type: 'timing', message: 'Submission too fast' });
}
// 4. Rate limiting
const rateLimitResult = this.checkRateLimit(userId, formId);
if (!rateLimitResult.allowed) {
this.logSecurityEvent('RATE_LIMIT_EXCEEDED', { formId, userId });
errors.push({ type: 'rateLimit', message: 'Too many submissions' });
}
if (errors.length === 0) {
// Mark token as used
const tokenData = this.csrfTokens.get(key);
if (tokenData) {
tokenData.used = true;
}
// Log successful submission
this.recordSubmission(userId, formId);
}
return {
valid: errors.length === 0,
errors,
rateLimitInfo: rateLimitResult
};
}
validateCsrfToken(key, token) {
const stored = this.csrfTokens.get(key);
if (!stored) {
return { valid: false, error: 'No CSRF token found' };
}
if (stored.used) {
return { valid: false, error: 'CSRF token already used' };
}
if (stored.expiresAt < Date.now()) {
this.csrfTokens.delete(key);
return { valid: false, error: 'CSRF token expired' };
}
if (stored.token !== token) {
return { valid: false, error: 'Invalid CSRF token' };
}
return { valid: true };
}
checkHoneypot(data) {
const honeypotValue = data[this.honeypotField];
return honeypotValue === undefined || honeypotValue === '' || honeypotValue === null;
}
checkSubmissionTiming(meta) {
const { submitTime, loadTime } = meta;
if (!submitTime || !loadTime) return true;
const timeTaken = submitTime - loadTime;
return timeTaken >= this.minSubmitTime;
}
checkRateLimit(userId, formId) {
const key = `rate:${userId}:${formId}`;
const now = Date.now();
let record = this.submissionLog.get(key);
if (!record || record.windowStart + this.windowMs < now) {
record = { windowStart: now, count: 0 };
}
record.count++;
this.submissionLog.set(key, record);
return {
allowed: record.count <= this.maxSubmissionsPerWindow,
remaining: Math.max(0, this.maxSubmissionsPerWindow - record.count),
resetAt: record.windowStart + this.windowMs
};
}
recordSubmission(userId, formId) {
const key = `rate:${userId}:${formId}`;
const record = this.submissionLog.get(key) || { windowStart: Date.now(), count: 0 };
record.count++;
this.submissionLog.set(key, record);
}
logSecurityEvent(event, details) {
this.securityLog.push({
timestamp: new Date().toISOString(),
event,
details
});
}
getSecurityLog() {
return this.securityLog;
}
}
// SOLUTION 4: Secure Data Store
class SecureDataStoreSolution {
constructor(options = {}) {
this.data = new Map();
this.encryptionKey = options.encryptionKey || this.generateKey();
this.accessControl = new Map();
this.auditLog = [];
this.ttlTimers = new Map();
}
generateKey() {
return Array.from({ length: 32 }, () =>
Math.floor(Math.random() * 256).toString(16).padStart(2, '0')
).join('');
}
defineAccess(resource, rules) {
this.accessControl.set(resource, rules);
}
set(key, value, options = {}) {
const { sensitiveFields = [], owner, ttl } = options;
// Deep clone to avoid reference issues
let storedValue = JSON.parse(JSON.stringify(value));
// Encrypt sensitive fields
for (const field of sensitiveFields) {
if (storedValue[field] !== undefined) {
storedValue[field] = this.encrypt(storedValue[field]);
}
}
const record = {
value: storedValue,
sensitiveFields,
owner,
createdAt: Date.now(),
updatedAt: Date.now()
};
// Set TTL
if (ttl) {
record.expiresAt = Date.now() + ttl;
this.setTTLTimer(key, ttl);
}
this.data.set(key, record);
this.log('SET', key, { owner }, 'success');
}
get(key, accessor) {
const record = this.data.get(key);
if (!record) {
this.log('GET', key, accessor, 'not_found');
return null;
}
// Check expiration
if (record.expiresAt && record.expiresAt < Date.now()) {
this.secureDelete(key);
this.log('GET', key, accessor, 'expired');
return null;
}
// Check access
if (!this.checkAccess(key, record, accessor, 'read')) {
this.log('GET', key, accessor, 'access_denied');
return { error: 'Access denied' };
}
// Decrypt sensitive fields
const result = JSON.parse(JSON.stringify(record.value));
for (const field of record.sensitiveFields) {
if (result[field]) {
result[field] = this.decrypt(result[field]);
}
}
this.log('GET', key, accessor, 'success');
return result;
}
delete(key, accessor) {
const record = this.data.get(key);
if (!record) {
return { success: false, error: 'Not found' };
}
if (!this.checkAccess(key, record, accessor, 'write')) {
this.log('DELETE', key, accessor, 'access_denied');
return { success: false, error: 'Access denied' };
}
this.secureDelete(key);
this.log('DELETE', key, accessor, 'success');
return { success: true };
}
secureDelete(key) {
const record = this.data.get(key);
if (record) {
// Overwrite sensitive data before deletion
for (const field in record.value) {
record.value[field] = null;
}
}
this.data.delete(key);
// Clear TTL timer
const timer = this.ttlTimers.get(key);
if (timer) {
clearTimeout(timer);
this.ttlTimers.delete(key);
}
}
setTTLTimer(key, ttl) {
const existing = this.ttlTimers.get(key);
if (existing) clearTimeout(existing);
const timer = setTimeout(() => {
this.secureDelete(key);
this.log('TTL_EXPIRE', key, { system: true }, 'expired');
}, ttl);
this.ttlTimers.set(key, timer);
}
encrypt(value) {
// Simple XOR encryption simulation (use real crypto in production)
const str = typeof value === 'string' ? value : JSON.stringify(value);
let encrypted = '';
for (let i = 0; i < str.length; i++) {
encrypted += String.fromCharCode(
str.charCodeAt(i) ^ this.encryptionKey.charCodeAt(i % this.encryptionKey.length)
);
}
return { encrypted: Buffer.from(encrypted).toString('base64'), isEncrypted: true };
}
decrypt(encrypted) {
if (!encrypted || !encrypted.isEncrypted) return encrypted;
const str = Buffer.from(encrypted.encrypted, 'base64').toString();
let decrypted = '';
for (let i = 0; i < str.length; i++) {
decrypted += String.fromCharCode(
str.charCodeAt(i) ^ this.encryptionKey.charCodeAt(i % this.encryptionKey.length)
);
}
try {
return JSON.parse(decrypted);
} catch {
return decrypted;
}
}
checkAccess(key, record, accessor, operation) {
// Owner always has access
if (accessor.id === record.owner) {
return true;
}
// Check role-based access
const resourceType = key.split(':')[0];
const rules = this.accessControl.get(resourceType);
if (!rules) return false;
const allowedRoles = rules[operation] || [];
return allowedRoles.includes(accessor.role);
}
log(operation, key, accessor, result) {
this.auditLog.push({
timestamp: new Date().toISOString(),
operation,
key,
accessor: accessor?.id || 'unknown',
accessorRole: accessor?.role || 'unknown',
result
});
}
getAuditLog(filter = {}) {
let logs = [...this.auditLog];
if (filter.key) {
logs = logs.filter(l => l.key === filter.key);
}
if (filter.operation) {
logs = logs.filter(l => l.operation === filter.operation);
}
if (filter.accessor) {
logs = logs.filter(l => l.accessor === filter.accessor);
}
if (filter.since) {
logs = logs.filter(l => new Date(l.timestamp) >= new Date(filter.since));
}
return logs;
}
}
// SOLUTION 5: Security Pipeline
class SecurityPipelineSolution {
constructor() {
this.middleware = [];
}
use(name, handler, options = {}) {
this.middleware.push({
name,
handler,
priority: options.priority || this.middleware.length,
enabled: options.enabled !== false
});
// Sort by priority
this.middleware.sort((a, b) => a.priority - b.priority);
}
async execute(request, response) {
const results = [];
const context = { startTime: Date.now() };
for (const mw of this.middleware) {
if (!mw.enabled) continue;
const startTime = Date.now();
try {
const result = await mw.handler(request, response, () => ({ passed: true }));
const duration = Date.now() - startTime;
results.push({
name: mw.name,
passed: result.passed,
duration,
details: result
});
if (!result.passed) {
return {
success: false,
failedAt: mw.name,
reason: result.reason,
results,
report: this.generateReport(results)
};
}
} catch (error) {
results.push({
name: mw.name,
passed: false,
error: error.message
});
return {
success: false,
failedAt: mw.name,
reason: error.message,
results,
report: this.generateReport(results)
};
}
}
return {
success: true,
results,
totalTime: Date.now() - context.startTime,
report: this.generateReport(results)
};
}
static rateLimiter(options = {}) {
const store = new Map();
const { maxRequests = 100, windowMs = 60000 } = options;
return async (req, res, next) => {
const key = req.ip || 'unknown';
const now = Date.now();
let record = store.get(key);
if (!record || record.resetTime < now) {
record = { count: 0, resetTime: now + windowMs };
}
record.count++;
store.set(key, record);
res.headers['X-RateLimit-Limit'] = maxRequests;
res.headers['X-RateLimit-Remaining'] = Math.max(0, maxRequests - record.count);
if (record.count > maxRequests) {
return { passed: false, reason: 'Rate limit exceeded' };
}
return next();
};
}
static inputSanitizer(options = {}) {
return async (req, res, next) => {
if (req.body) {
req.body = sanitizeObject(req.body);
}
return next();
function sanitizeObject(obj) {
if (typeof obj === 'string') {
return obj
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.trim();
}
if (typeof obj === 'object' && obj !== null) {
for (const key of Object.keys(obj)) {
obj[key] = sanitizeObject(obj[key]);
}
}
return obj;
}
};
}
static authChecker(options = {}) {
const { required = true, headerName = 'authorization' } = options;
return async (req, res, next) => {
const authHeader = req.headers[headerName];
if (!authHeader && required) {
return { passed: false, reason: 'Authentication required' };
}
if (authHeader) {
const [scheme, token] = authHeader.split(' ');
if (scheme?.toLowerCase() !== 'bearer' || !token) {
return { passed: false, reason: 'Invalid authentication format' };
}
req.authToken = token;
}
return next();
};
}
static corsValidator(options = {}) {
const { origins = [], methods = ['GET', 'POST', 'PUT', 'DELETE'] } = options;
return async (req, res, next) => {
const origin = req.headers.origin;
if (origin && !origins.includes(origin) && !origins.includes('*')) {
return { passed: false, reason: 'Origin not allowed' };
}
res.headers['Access-Control-Allow-Origin'] = origin || '*';
res.headers['Access-Control-Allow-Methods'] = methods.join(', ');
res.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization';
return next();
};
}
static cspSetter(options = {}) {
const defaultPolicy = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'";
const policy = options.policy || defaultPolicy;
return async (req, res, next) => {
res.headers['Content-Security-Policy'] = policy;
res.headers['X-Content-Type-Options'] = 'nosniff';
res.headers['X-Frame-Options'] = 'DENY';
res.headers['X-XSS-Protection'] = '1; mode=block';
return next();
};
}
generateReport(results) {
const passed = results.filter(r => r.passed).length;
const failed = results.filter(r => !r.passed).length;
const totalDuration = results.reduce((sum, r) => sum + (r.duration || 0), 0);
return {
summary: {
total: results.length,
passed,
failed,
passRate: `${((passed / results.length) * 100).toFixed(1)}%`,
totalDuration: `${totalDuration}ms`
},
details: results.map(r => ({
name: r.name,
status: r.passed ? 'PASS' : 'FAIL',
duration: r.duration ? `${r.duration}ms` : 'N/A',
reason: r.details?.reason || r.error || null
}))
};
}
}
console.log('Solutions loaded. Uncomment test functions to verify implementations.');
*/
module.exports = {
AdvancedValidator,
SecureTokenManager,
SecureFormHandler,
SecureDataStore,
SecurityPipeline,
};