aimless-sdk
Version:
Enhanced Runtime Application Self-Protection (RASP) and API Fuzzing Engine with advanced threat detection, behavioral analysis, and intelligent response scoring for Node.js applications
308 lines • 12.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RASP = void 0;
const injection_detector_1 = require("./injection-detector");
const xss_detector_1 = require("./xss-detector");
const csrf_detector_1 = require("./csrf-detector");
const anomaly_detector_1 = require("./anomaly-detector");
const advanced_detector_1 = require("./advanced-detector");
class RASP {
constructor(config = {}, logger) {
this.config = {
enabled: true,
injectionProtection: true,
xssProtection: true,
csrfProtection: false, // Disabled by default to avoid false positives
anomalyDetection: false, // Disabled by default to avoid false positives
blockMode: false, // Detection mode by default - safer for production
accessControl: {
mode: 'monitor', // Default to monitor mode
defaultAction: 'allow',
...config.accessControl
},
trustedOrigins: [],
maxRequestSize: 10 * 1024 * 1024, // 10MB default
rateLimiting: {
enabled: false,
maxRequests: 100,
windowMs: 60000
},
customBlockMessage: undefined,
loadingScreen: {
enabled: false,
message: 'Checking security...',
minDuration: 500
},
...config
};
this.logger = logger;
this.injectionDetector = new injection_detector_1.InjectionDetector();
this.xssDetector = new xss_detector_1.XSSDetector();
this.csrfDetector = new csrf_detector_1.CSRFDetector(this.config.trustedOrigins);
this.anomalyDetector = new anomaly_detector_1.AnomalyDetector();
this.advancedDetector = new advanced_detector_1.AdvancedThreatDetector();
}
analyze(request) {
if (!this.config.enabled)
return [];
const threats = [];
try {
// Injection detection - only if data exists
if (this.config.injectionProtection) {
if (request.query && typeof request.query === 'object') {
const queryThreats = this.injectionDetector.detect(request.query, 'query');
threats.push(...queryThreats);
}
if (request.body && typeof request.body === 'object') {
const bodyThreats = this.injectionDetector.detect(request.body, 'body');
threats.push(...bodyThreats);
}
}
// XSS detection - only if data exists
if (this.config.xssProtection) {
if (request.query && typeof request.query === 'object') {
const queryXSS = this.xssDetector.detect(request.query, 'query');
threats.push(...queryXSS);
}
if (request.body && typeof request.body === 'object') {
const bodyXSS = this.xssDetector.detect(request.body, 'body');
threats.push(...bodyXSS);
}
}
// Advanced threat detection (LDAP, Template Injection, etc.) - with safety check
if (request.body) {
const advancedThreats = this.advancedDetector.detectAll(request.body, request.path?.includes('graphql') ? 'graphql' : undefined);
threats.push(...advancedThreats);
}
}
catch (detectionError) {
// Log error but continue - don't let detection errors break the request
this.logger.error('Error during threat detection:', detectionError);
}
try {
// CSRF detection
if (this.config.csrfProtection && request.headers) {
const origin = this.getHeader(request.headers, 'origin');
const referer = this.getHeader(request.headers, 'referer');
const csrfToken = this.getHeader(request.headers, 'x-csrf-token');
const sessionId = this.getHeader(request.headers, 'cookie')?.split('sessionId=')[1]?.split(';')[0];
const csrfThreat = this.csrfDetector.detect(request.method, origin, referer, csrfToken, sessionId);
if (csrfThreat)
threats.push(csrfThreat);
}
}
catch (csrfError) {
this.logger.error('Error during CSRF detection:', csrfError);
}
try {
// Anomaly detection
if (this.config.anomalyDetection && request.ip) {
const userAgent = this.getHeader(request.headers || {}, 'user-agent');
let bodySize = 0;
try {
bodySize = request.body ? JSON.stringify(request.body).length : 0;
}
catch {
bodySize = 0; // Circular reference or other JSON error
}
const anomalies = this.anomalyDetector.detect(request.ip, request.method, request.path, userAgent, bodySize);
threats.push(...anomalies);
}
}
catch (anomalyError) {
this.logger.error('Error during anomaly detection:', anomalyError);
}
// Log threats safely
try {
threats.forEach(threat => this.logger.threat(threat));
}
catch (logError) {
// Even logging shouldn't break the flow
console.error('Failed to log threats:', logError);
}
return threats;
}
shouldBlock(threats) {
if (!this.config.blockMode)
return false;
return threats.some(t => t.blocked && ['high', 'critical'].includes(t.severity));
}
/**
* Check if endpoint is allowed based on access control rules
*/
checkEndpointAccess(request) {
const ac = this.config.accessControl;
if (!ac || ac.mode === 'monitor') {
return { allowed: true }; // Monitor mode - always allow, just log
}
const { method, path, headers } = request;
// Check if endpoint is explicitly blocked
if (ac.blockedEndpoints?.length) {
const blocked = ac.blockedEndpoints.some(pattern => this.matchesPattern(path, pattern));
if (blocked) {
return { allowed: false, reason: 'Endpoint is blocked' };
}
}
// Check authentication requirement
if (ac.requireAuthHeader && headers) {
const authHeader = headers[ac.requireAuthHeader.toLowerCase()];
if (!authHeader) {
return { allowed: false, reason: `Missing required header: ${ac.requireAuthHeader}` };
}
}
// ALLOWLIST mode - only explicitly allowed endpoints are permitted
if (ac.mode === 'allowlist') {
if (!ac.allowedEndpoints?.length) {
// No allowed endpoints defined, use defaultAction
const allowed = ac.defaultAction === 'allow';
return {
allowed,
reason: allowed ? undefined : 'Endpoint not in allowlist'
};
}
const matchedRule = ac.allowedEndpoints.find(rule => {
const pathMatches = this.matchesPattern(path, rule.path);
const methodMatches = !rule.methods || rule.methods.includes(method);
return pathMatches && methodMatches;
});
if (!matchedRule) {
return { allowed: false, reason: 'Endpoint not in allowlist', matchedRule };
}
// Check method-specific rules
if (matchedRule.methods && !matchedRule.methods.includes(method)) {
return {
allowed: false,
reason: `Method ${method} not allowed for this endpoint`,
matchedRule
};
}
// Check auth requirement for this specific endpoint
if (matchedRule.requireAuth && headers) {
const authHeader = headers['authorization'] || headers['x-api-key'];
if (!authHeader) {
return {
allowed: false,
reason: 'Authentication required for this endpoint',
matchedRule
};
}
}
return { allowed: true, matchedRule };
}
// BLOCKLIST mode - all endpoints allowed except explicitly blocked
if (ac.mode === 'blocklist') {
return { allowed: true }; // Already checked blockedEndpoints above
}
// Default action for unmatched
const allowed = ac.defaultAction === 'allow';
return {
allowed,
reason: allowed ? undefined : 'No matching rule, default is block'
};
}
/**
* Check if endpoint has extra protection rules
*/
getProtectionRules(request) {
const ac = this.config.accessControl;
if (!ac?.protectedEndpoints?.length)
return null;
return ac.protectedEndpoints.find(rule => {
const pathMatches = this.matchesPattern(request.path, rule.path);
const methodMatches = !rule.methods || rule.methods.includes(request.method);
return pathMatches && methodMatches;
});
}
/**
* Match path against string or regex pattern
*/
matchesPattern(path, pattern) {
if (typeof pattern === 'string') {
// Support wildcards: /api/* matches /api/users, /api/posts, etc.
if (pattern.includes('*')) {
const regexPattern = pattern.replace(/\*/g, '.*').replace(/\//g, '\\/');
return new RegExp(`^${regexPattern}$`).test(path);
}
return path === pattern;
}
return pattern.test(path);
}
generateCSRFToken(sessionId) {
return this.csrfDetector.generateToken(sessionId);
}
sanitizeOutput(output) {
return this.xssDetector.sanitize(output);
}
/**
* Detect injections (SQL, NoSQL, Command, Path Traversal, etc.)
*/
detectInjections(input, context) {
return this.injectionDetector.detect(input, context);
}
/**
* Detect LDAP injection
*/
detectLDAPInjection(input) {
return this.advancedDetector.detectLDAPInjection(input);
}
/**
* Detect template injection (SSTI)
*/
detectTemplateInjection(input) {
return this.advancedDetector.detectTemplateInjection(input);
}
/**
* Validate file upload security
*/
validateFileUpload(filename, content, mimeType) {
return this.advancedDetector.validateFileUpload(filename, content, mimeType);
}
/**
* Analyze JWT token security
*/
analyzeJWT(token) {
return this.advancedDetector.analyzeJWT(token);
}
/**
* Detect GraphQL attacks
*/
detectGraphQLAttack(query) {
return this.advancedDetector.detectGraphQLAttack(query);
}
/**
* Detect prototype pollution
*/
detectPrototypePollution(input) {
return this.advancedDetector.detectPrototypePollution(input);
}
/**
* Detect deserialization attacks
*/
detectDeserialization(input) {
return this.advancedDetector.detectDeserialization(input);
}
/**
* Get direct access to detectors for advanced use cases
*/
getInjectionDetector() {
return this.injectionDetector;
}
getXSSDetector() {
return this.xssDetector;
}
getCSRFDetector() {
return this.csrfDetector;
}
getAnomalyDetector() {
return this.anomalyDetector;
}
getAdvancedDetector() {
return this.advancedDetector;
}
getHeader(headers, name) {
const value = headers[name.toLowerCase()];
return Array.isArray(value) ? value[0] : value;
}
}
exports.RASP = RASP;
//# sourceMappingURL=index.js.map