nehonix-uri-processor
Version:
A powerful URI processor for encoding, decoding, and analyzing URI data securely.
143 lines • 5.57 kB
JavaScript
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