@lock-dev/core
Version:
Core security framework for lock.dev
341 lines (333 loc) • 11.1 kB
JavaScript
;
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
});