UNPKG

nehonix-uri-processor

Version:

A powerful URI processor for encoding, decoding, and analyzing URI data securely.

143 lines 5.57 kB
import { RuleType, ActionType } from "./types/ShieldTypes"; export class RuleEngine { constructor(rules = []) { this.rules = this.sortRulesByPriority(rules); } sortRulesByPriority(rules) { return [...rules].sort((a, b) => b.priority - a.priority); } loadRules(rules) { this.rules = this.sortRulesByPriority(rules); } getCSPDirectives(nonce) { const defaultDirectives = { 'default-src': ["'self'"], 'script-src': ["'self'", `'nonce-${nonce}'`], 'style-src': ["'self'"], 'img-src': ["'self'", 'data:'], 'font-src': ["'self'"], 'connect-src': ["'self'"], 'frame-ancestors': ["'none'"], 'form-action': ["'self'"], 'base-uri': ["'self'"], 'object-src': ["'none'"] }; const cspRules = this.rules.filter(rule => rule.type === RuleType.CSP); cspRules.forEach(rule => { if (rule.action.type === ActionType.MODIFY) { const directive = rule.action.value?.directive; const sources = rule.action.value?.sources; if (directive && Array.isArray(sources)) { if (defaultDirectives[directive]) { defaultDirectives[directive] = [...defaultDirectives[directive], ...sources]; } else { defaultDirectives[directive] = [...sources]; } } } }); return Object.entries(defaultDirectives) .map(([directive, sources]) => `${directive} ${Array.isArray(sources) ? sources.join(' ') : "'none'"}`) .join('; '); } shouldSkipCSRF(req) { if (["GET", "HEAD", "OPTIONS"].includes(req.method)) { return true; } const csrfRules = this.rules.filter(rule => rule.type === RuleType.CSRF); return csrfRules.some(rule => { if (rule.condition.type === "match_path") { return new RegExp(rule.condition.value).test(req.path) && rule.action.type === ActionType.ALLOW; } return false; }); } validateCSRFToken(token, storedToken) { return !!(token && storedToken && token === storedToken); } getSecureCookieOptions(options) { const secureOptions = { ...options, httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "strict" }; const cookieRules = this.rules.filter(rule => rule.type === RuleType.COOKIE); cookieRules.forEach(rule => { if (rule.action.type === ActionType.MODIFY) { Object.assign(secureOptions, rule.action.value); } }); return secureOptions; } async validateRequest(req) { const requestRules = this.rules.filter(rule => rule.type === RuleType.REQUEST); for (const rule of requestRules) { const matches = await this.evaluateCondition(rule.condition, req); if (matches && rule.action.type === ActionType.DENY) { return false; } } return true; } validateIP(ip) { const ipRules = this.rules.filter(rule => rule.type === RuleType.IP); for (const rule of ipRules) { if (rule.condition.type === "match_ip") { const matches = this.matchIP(ip, rule.condition.value); if (matches && rule.action.type === ActionType.DENY) { return false; } } } return true; } matchIP(ip, pattern) { // Support for CIDR notation and IP ranges if (pattern.includes('/') || pattern.includes('-')) { return this.isIPInRange(ip, pattern); } return ip === pattern; } isIPInRange(ip, range) { // Implementation would use ip-range-check or similar library return false; // Placeholder } getSecurityHeaders(req) { const headers = { 'Permissions-Policy': 'geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), accelerometer=(), gyroscope=()', 'Cross-Origin-Resource-Policy': 'same-origin', 'Cross-Origin-Opener-Policy': 'same-origin' }; if (req.path.includes('/logout')) { headers['Clear-Site-Data'] = '"cache", "cookies", "storage"'; } const headerRules = this.rules.filter(rule => rule.type === RuleType.HEADER); headerRules.forEach(rule => { if (rule.action.type === ActionType.ADD_HEADER) { headers[rule.action.value.name] = rule.action.value.value; } else if (rule.action.type === ActionType.REMOVE_HEADER) { delete headers[rule.action.value]; } }); return headers; } async evaluateCondition(condition, req) { switch (condition.type) { case "match_path": return new RegExp(condition.value).test(req.path); case "match_method": return condition.value === req.method; case "match_header": return req.headers[condition.value.name] === condition.value.value; case "custom": return await condition.value(req); default: return false; } } } //# sourceMappingURL=RuleEngine.js.map