@pulzar/core
Version:
Next-generation Node.js framework for ultra-fast web applications with zero-reflection DI, GraphQL, WebSockets, events, and edge runtime support
173 lines • 5.74 kB
JavaScript
import { logger } from "../utils/logger";
export class SecurityManager {
app;
options;
csrfTokens = new Map();
rateLimitStore = new Map();
constructor(options = {}) {
this.options = {
csp: {
enabled: true,
directives: {
"default-src": ["'self'"],
"script-src": ["'self'", "'unsafe-inline'"],
},
},
hsts: {
enabled: true,
maxAge: 31536000,
},
xss: {
enabled: true,
mode: "block",
},
csrf: {
enabled: true,
ignoredMethods: ["GET", "HEAD", "OPTIONS"],
},
rateLimit: {
enabled: true,
windowMs: 15 * 60 * 1000,
max: 100,
},
...options,
};
}
async initialize(app) {
this.app = app;
try {
if (this.options.csp.enabled) {
await this.setupCSP();
}
if (this.options.hsts.enabled) {
await this.setupHSTS();
}
if (this.options.xss.enabled) {
await this.setupXSSProtection();
}
if (this.options.csrf.enabled) {
await this.setupCSRFProtection();
}
if (this.options.rateLimit.enabled) {
await this.setupRateLimit();
}
logger.info("Security manager initialized", {
csp: this.options.csp.enabled,
hsts: this.options.hsts.enabled,
xss: this.options.xss.enabled,
csrf: this.options.csrf.enabled,
rateLimit: this.options.rateLimit.enabled,
});
}
catch (error) {
logger.error("Failed to initialize security manager", { error });
throw error;
}
}
async setupCSP() {
if (!this.app)
return;
this.app.addHook("onSend", async (request, reply, payload) => {
const cspValue = this.buildCSPHeader();
reply.header("Content-Security-Policy", cspValue);
return payload;
});
logger.debug("CSP protection enabled");
}
buildCSPHeader() {
const directives = [];
for (const [directive, sources] of Object.entries(this.options.csp.directives)) {
if (Array.isArray(sources)) {
directives.push(`${directive} ${sources.join(" ")}`);
}
else {
directives.push(`${directive} ${sources}`);
}
}
return directives.join("; ");
}
async setupHSTS() {
if (!this.app)
return;
this.app.addHook("onSend", async (request, reply, payload) => {
reply.header("Strict-Transport-Security", `max-age=${this.options.hsts.maxAge}`);
return payload;
});
logger.debug("HSTS protection enabled");
}
async setupXSSProtection() {
if (!this.app)
return;
this.app.addHook("onSend", async (request, reply, payload) => {
reply.header("X-XSS-Protection", "1; mode=block");
reply.header("X-Content-Type-Options", "nosniff");
return payload;
});
logger.debug("XSS protection enabled");
}
async setupCSRFProtection() {
if (!this.app)
return;
this.app.get("/csrf-token", async (request, reply) => {
const token = this.generateCSRFToken(request);
return { csrfToken: token };
});
logger.debug("CSRF protection enabled");
}
generateCSRFToken(request) {
const sessionId = request.ip || "anonymous";
const token = Math.random().toString(36).substring(2);
const expires = Date.now() + 60 * 60 * 1000;
this.csrfTokens.set(sessionId, { token, expires });
return token;
}
async setupRateLimit() {
if (!this.app)
return;
this.app.addHook("preHandler", async (request, reply) => {
const key = request.ip || "unknown";
const now = Date.now();
const windowMs = this.options.rateLimit.windowMs;
const maxRequests = this.options.rateLimit.max;
let rateData = this.rateLimitStore.get(key);
if (!rateData || now > rateData.resetTime) {
rateData = {
count: 0,
resetTime: now + windowMs,
};
}
rateData.count++;
this.rateLimitStore.set(key, rateData);
if (rateData.count > maxRequests) {
reply.code(429).send({
error: "Too Many Requests",
statusCode: 429,
});
return;
}
});
logger.debug("Rate limiting enabled");
}
getStats() {
return {
csp: this.options.csp.enabled,
hsts: this.options.hsts.enabled,
xss: this.options.xss.enabled,
csrf: {
enabled: this.options.csrf.enabled,
activeSessions: this.csrfTokens.size,
},
rateLimit: {
enabled: this.options.rateLimit.enabled,
activeKeys: this.rateLimitStore.size,
},
};
}
async shutdown() {
this.csrfTokens.clear();
this.rateLimitStore.clear();
logger.info("Security manager shutdown");
}
}
export default SecurityManager;
//# sourceMappingURL=security-manager.js.map