UNPKG

@lock-dev/core

Version:

Core security framework for lock.dev

341 lines (333 loc) 11.1 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { SecurityEventType: () => SecurityEventType, SecurityManager: () => SecurityManager, composeModules: () => composeModules, createContext: () => createContext, createModule: () => createModule, registerModule: () => registerModule, secure: () => secure, secureRoute: () => secureRoute }); module.exports = __toCommonJS(index_exports); // src/types.ts var SecurityEventType = /* @__PURE__ */ ((SecurityEventType2) => { SecurityEventType2["AUTH_SUCCESS"] = "auth_success"; SecurityEventType2["AUTH_FAILURE"] = "auth_failure"; SecurityEventType2["RATE_LIMIT_EXCEEDED"] = "rate_limit_exceeded"; SecurityEventType2["VALIDATION_FAILURE"] = "validation_failure"; SecurityEventType2["CSRF_VIOLATION"] = "csrf_violation"; SecurityEventType2["AUTHORIZATION_FAILURE"] = "authorization_failure"; SecurityEventType2["GEO_BLOCKED"] = "geo_blocked"; SecurityEventType2["INTERNAL_ERROR"] = "internal_error"; return SecurityEventType2; })(SecurityEventType || {}); // src/security-manager.ts var SecurityManager = class { constructor() { this.modules = []; } /** * Registers a security module. * * @param {SecurityModule} module - The security module to register. * @returns {this} The SecurityManager instance. */ registerModule(module2) { this.modules.push(module2); return this; } /** * Runs all registered security checks using the provided context. * * @param {SecurityContext} context - The initial security context. * @returns {Promise<boolean>} A promise that resolves to true if all checks pass; otherwise, false. */ async runSecurityChecks(context) { try { for (const module2 of this.modules) { try { const result = await module2.check(context); if (result.event) { context.events.push(result.event); } if (!result.passed) { context.denied = true; context.deniedReason = result.event?.message || `Security check failed: ${module2.name}`; if (module2.handleFailure && result.event) { await module2.handleFailure(context, result.event); } return false; } } catch (error) { const errorEvent = { type: "internal_error" /* INTERNAL_ERROR */, timestamp: Date.now(), message: `Error in module ${module2.name}: ${error instanceof Error ? error.message : String(error)}`, data: error, severity: "high", moduleName: module2.name }; context.events.push(errorEvent); context.denied = true; context.deniedReason = errorEvent.message; if (module2.handleFailure) { try { await module2.handleFailure(context, errorEvent); } catch (handlerError) { console.error("Error in failure handler:", handlerError); } } return false; } } context.endTime = Date.now(); return true; } catch (error) { console.error("Unexpected error in security manager:", error); context.denied = true; context.deniedReason = `Unexpected error: ${error instanceof Error ? error.message : String(error)}`; context.events.push({ type: "internal_error" /* INTERNAL_ERROR */, timestamp: Date.now(), message: context.deniedReason, data: error, severity: "critical", moduleName: "security-manager" }); return false; } } }; // src/module-factory.ts function createModule(definition) { return (config) => { const mergedConfig = { ...definition.defaultConfig, ...config }; return { name: definition.name, async check(context) { try { context.data.set(`${definition.name}:config`, mergedConfig); const result = await definition.check(context, mergedConfig); if (!result.passed) { const event = { type: result.reason || "internal_error" /* INTERNAL_ERROR */, timestamp: Date.now(), message: result.reason || "Security check failed", data: result.data, severity: result.severity || "medium", moduleName: definition.name }; return { passed: false, event, context }; } return { passed: true, context }; } catch (error) { const event = { type: "internal_error" /* INTERNAL_ERROR */, timestamp: Date.now(), message: `Error in ${definition.name}: ${error instanceof Error ? error.message : String(error)}`, data: error, severity: "high", moduleName: definition.name }; return { passed: false, event, context }; } }, async handleFailure(context, event) { if (definition.handleFailure) { await definition.handleFailure(context, event.message, event.data); } } }; }; } // src/composition.ts function composeModules(...modules) { return { name: "composed-modules", async check(context) { for (const module2 of modules) { const result = await module2.check(context); if (!result.passed) { return result; } } return { passed: true, context }; }, async handleFailure(context, event) { const module2 = modules.find((m) => m.name === event.moduleName); if (module2 && module2.handleFailure) { await module2.handleFailure(context, event); } } }; } // src/context.ts function createContext(request, response, config = {}) { return { request, response, denied: false, events: [], startTime: Date.now(), data: /* @__PURE__ */ new Map(), config }; } // src/secure-route.ts var moduleRegistry = {}; function registerModule(name, factory) { moduleRegistry[name] = factory; return factory; } function secureRoute(handler, options = {}) { const modules = []; for (const [key, config] of Object.entries(options)) { if (moduleRegistry[key]) { modules.push(moduleRegistry[key](config)); } else { console.warn( `Unknown security module: ${key}. Make sure it's registered with registerModule().` ); } } if (modules.length === 0) { return handler; } const composedModule = composeModules(...modules); return async function securedHandler(req, res, next) { const context = createContext(req, res, {}); try { const result = await composedModule.check(context); if (result.passed) { if (typeof next === "function") { return next(); } return handler(req, res); } else { if (!res.headersSent && !res.writableEnded) { const moduleInfo = result.event ? { module: result.event.moduleName, reason: result.event.message, type: result.event.type, timestamp: new Date(result.event.timestamp).toISOString(), severity: result.event.severity, details: result.event.data || {} } : { module: "unknown", reason: "Access denied by security policy" }; res.status(403).json({ error: "Access denied by security policy", blocked: true, meta: moduleInfo }); } } } catch (error) { console.error("Error in security middleware:", error); if (res && !context.response.headersSent) { if (typeof res.status === "function") { res.status(500).json({ error: "Internal security error" }); } else if (typeof res.statusCode === "number") { res.statusCode = 500; res.setHeader("Content-Type", "application/json"); res.end(JSON.stringify({ error: "Internal security error" })); } } } }; } function secure(handler) { return (...modules) => { const composedModule = composeModules(...modules); return async (req, res, next) => { const context = createContext(req, res, {}); try { const result = await composedModule.check(context); if (result.passed) { if (handler) { return handler(req, res); } else if (typeof next === "function") { return next(); } } else { if (!res.headersSent && !res.writableEnded) { const moduleInfo = result.event ? { module: result.event.moduleName, reason: result.event.message, type: result.event.type, timestamp: new Date(result.event.timestamp).toISOString(), severity: result.event.severity, details: result.event.data || {} } : { module: "unknown", reason: "Access denied by security policy" }; let statusCode = 403; let errorMessage = "Access denied by security policy"; if (result.event && result.event.moduleName) { const configKey = `${result.event.moduleName}:config`; const config = context.data.get(configKey); if (config) { statusCode = config.blockStatusCode || config.statusCode || statusCode; errorMessage = config.blockMessage || config.message || errorMessage; } } res.status(statusCode).json({ error: errorMessage, blocked: true, meta: moduleInfo }); } } } catch (error) { console.error("Security error:", error); if (!res.headersSent && !res.writableEnded) { res.status(500).json({ error: "Internal security error" }); } } }; }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { SecurityEventType, SecurityManager, composeModules, createContext, createModule, registerModule, secure, secureRoute });