UNPKG

@ordojs/security

Version:

Security package for OrdoJS with XSS, CSRF, and injection protection

170 lines 5.29 kB
/** * Content Security Policy (CSP) manager for XSS protection */ /** * Default secure CSP policy */ const DEFAULT_CSP_POLICY = { 'default-src': ["'self'"], 'script-src': ["'self'"], 'style-src': ["'self'", "'unsafe-inline'"], // Allow inline styles for component styling 'img-src': ["'self'", 'data:', 'https:'], 'font-src': ["'self'", 'https:'], 'connect-src': ["'self'"], 'media-src': ["'self'"], 'object-src': ["'none'"], 'frame-src': ["'none'"], 'base-uri': ["'self'"], 'form-action': ["'self'"], 'frame-ancestors': ["'none'"], 'upgrade-insecure-requests': [], }; /** * Content Security Policy manager for XSS protection */ export class CspManager { policy; options; nonces = new Set(); /** * Create a new CSP manager * @param policy Initial CSP policy * @param options CSP manager options */ constructor(policy = {}, options = {}) { this.policy = { ...DEFAULT_CSP_POLICY }; // Merge user policy, ensuring all values are arrays Object.entries(policy).forEach(([key, value]) => { if (value) { this.policy[key] = value; } }); this.options = { reportViolations: false, reportOnly: false, ...options, }; } /** * Generate a cryptographically secure nonce for inline scripts/styles * @returns Base64 encoded nonce */ generateNonce() { const array = new Uint8Array(16); crypto.getRandomValues(array); const nonce = btoa(String.fromCharCode(...array)); this.nonces.add(nonce); return nonce; } /** * Add a nonce to the script-src directive * @param nonce The nonce to add */ addScriptNonce(nonce) { this.addSourceToDirective('script-src', `'nonce-${nonce}'`); } /** * Add a nonce to the style-src directive * @param nonce The nonce to add */ addStyleNonce(nonce) { this.addSourceToDirective('style-src', `'nonce-${nonce}'`); } /** * Add a hash to a directive for inline content * @param directive The CSP directive * @param hash The SHA hash (e.g., 'sha256-abc123...') */ addHash(directive, hash) { this.addSourceToDirective(directive, `'${hash}'`); } /** * Add a source to a CSP directive * @param directive The CSP directive * @param source The source to add */ addSourceToDirective(directive, source) { if (!this.policy[directive]) { this.policy[directive] = []; } if (!this.policy[directive].includes(source)) { this.policy[directive].push(source); } } /** * Remove a source from a CSP directive * @param directive The CSP directive * @param source The source to remove */ removeSourceFromDirective(directive, source) { if (this.policy[directive]) { this.policy[directive] = this.policy[directive].filter(s => s !== source); } } /** * Generate the CSP header value * @returns CSP header value string */ generateHeader() { const directives = Object.entries(this.policy) .filter(([_, sources]) => sources.length > 0) .map(([directive, sources]) => { if (sources.length === 0) { return directive; } return `${directive} ${sources.join(' ')}`; }); if (this.options.reportUri) { directives.push(`report-uri ${this.options.reportUri}`); } return directives.join('; '); } /** * Get the appropriate CSP header name * @returns CSP header name */ getHeaderName() { return this.options.reportOnly ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy'; } /** * Create a strict CSP policy for maximum security * @returns New CspManager with strict policy */ static createStrict() { return new CspManager({ 'default-src': ["'none'"], 'script-src': ["'self'"], 'style-src': ["'self'"], 'img-src': ["'self'"], 'font-src': ["'self'"], 'connect-src': ["'self'"], 'media-src': ["'none'"], 'object-src': ["'none'"], 'frame-src': ["'none'"], 'base-uri': ["'none'"], 'form-action': ["'self'"], 'frame-ancestors': ["'none'"], }); } /** * Create a development-friendly CSP policy * @returns New CspManager with development policy */ static createDevelopment() { return new CspManager({ 'default-src': ["'self'"], 'script-src': ["'self'", "'unsafe-eval'"], // Allow eval for dev tools 'style-src': ["'self'", "'unsafe-inline'"], 'img-src': ["'self'", 'data:', 'https:', 'http:'], 'font-src': ["'self'", 'https:', 'data:'], 'connect-src': ["'self'", 'ws:', 'wss:'], // Allow WebSocket for HMR }); } } /** * Default CSP manager instance */ export const defaultCspManager = new CspManager(); //# sourceMappingURL=csp-manager.js.map