@ordojs/security
Version:
Security package for OrdoJS with XSS, CSRF, and injection protection
265 lines (232 loc) • 7.66 kB
text/typescript
/**
* XSS vulnerability detection and analysis
*/
/**
* Types of XSS vulnerabilities
*/
export enum XssVulnerabilityType {
REFLECTED = 'reflected',
STORED = 'stored',
DOM_BASED = 'dom-based',
TEMPLATE_INJECTION = 'template-injection',
}
/**
* Severity levels for vulnerabilities
*/
export enum VulnerabilitySeverity {
LOW = 'low',
MEDIUM = 'medium',
HIGH = 'high',
CRITICAL = 'critical',
}
/**
* XSS vulnerability detection result
*/
export interface XssVulnerability {
type: XssVulnerabilityType;
severity: VulnerabilitySeverity;
description: string;
location: string;
payload: string;
recommendation: string;
}
/**
* Common XSS attack patterns
*/
const XSS_PATTERNS = [
// Script tags
/<script[^>]*>.*?<\/script>/gi,
/<script[^>]*>/gi,
// Event handlers
/on\w+\s*=\s*['"]/gi,
// JavaScript URLs
/javascript\s*:/gi,
// Data URLs with JavaScript
/data\s*:\s*text\/html/gi,
// SVG with script
/<svg[^>]*>.*?<\/svg>/gi,
// Object/embed tags
/<(object|embed|applet)[^>]*>/gi,
// Meta refresh
/<meta[^>]*http-equiv\s*=\s*['"]\s*refresh/gi,
// Link with JavaScript
/<link[^>]*href\s*=\s*['"]\s*javascript/gi,
// Style with expression
/expression\s*\(/gi,
// Import statements
/@import/gi,
];
/**
* Dangerous HTML attributes that can execute JavaScript
*/
const DANGEROUS_ATTRIBUTES = [
'onabort', 'onblur', 'onchange', 'onclick', 'ondblclick', 'onerror',
'onfocus', 'onkeydown', 'onkeypress', 'onkeyup', 'onload', 'onmousedown',
'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onreset',
'onresize', 'onselect', 'onsubmit', 'onunload', 'onafterprint',
'onbeforeprint', 'onbeforeunload', 'onhashchange', 'onmessage',
'onoffline', 'ononline', 'onpagehide', 'onpageshow', 'onpopstate',
'onstorage', 'oncontextmenu', 'oninput', 'oninvalid', 'onsearch',
];
/**
* XSS vulnerability detector
*/
export class XssVulnerabilityDetector {
private patterns: RegExp[];
private dangerousAttributes: string[];
/**
* Create a new XSS vulnerability detector
*/
constructor() {
this.patterns = [...XSS_PATTERNS];
this.dangerousAttributes = [...DANGEROUS_ATTRIBUTES];
}
/**
* Scan content for potential XSS vulnerabilities
* @param content Content to scan
* @param location Location identifier for the content
* @returns Array of detected vulnerabilities
*/
scanContent(content: string, location: string = 'unknown'): XssVulnerability[] {
const vulnerabilities: XssVulnerability[] = [];
// Check for script injection patterns
for (const pattern of this.patterns) {
const matches = content.match(pattern);
if (matches) {
for (const match of matches) {
vulnerabilities.push({
type: this.determineVulnerabilityType(match),
severity: this.determineSeverity(match),
description: `Potential XSS vulnerability detected: ${match.substring(0, 100)}...`,
location,
payload: match,
recommendation: this.getRecommendation(match),
});
}
}
}
// Check for dangerous attributes
for (const attr of this.dangerousAttributes) {
const attrPattern = new RegExp(`${attr}\\s*=`, 'gi');
if (attrPattern.test(content)) {
vulnerabilities.push({
type: XssVulnerabilityType.DOM_BASED,
severity: VulnerabilitySeverity.HIGH,
description: `Dangerous event handler attribute detected: ${attr}`,
location,
payload: attr,
recommendation: 'Remove event handler attributes and use proper event listeners instead.',
});
}
}
return vulnerabilities;
}
/**
* Check if a string contains potential XSS payload
* @param input Input string to check
* @returns True if potential XSS is detected
*/
containsXss(input: string): boolean {
return this.patterns.some(pattern => pattern.test(input)) ||
this.dangerousAttributes.some(attr =>
new RegExp(`${attr}\\s*=`, 'gi').test(input)
);
}
/**
* Analyze template for potential injection vulnerabilities
* @param template Template string
* @param variables Variables used in template
* @returns Array of vulnerabilities
*/
analyzeTemplate(template: string, variables: Record<string, unknown> = {}): XssVulnerability[] {
const vulnerabilities: XssVulnerability[] = [];
// Check for unescaped variable interpolation
const interpolationPattern = /\$\{([^}]+)\}/g;
let match;
while ((match = interpolationPattern.exec(template)) !== null) {
const variableName = match[1]?.trim();
if (!variableName) continue;
const variableValue = variables[variableName];
if (typeof variableValue === 'string' && this.containsXss(variableValue)) {
vulnerabilities.push({
type: XssVulnerabilityType.TEMPLATE_INJECTION,
severity: VulnerabilitySeverity.CRITICAL,
description: `Unescaped variable interpolation with potential XSS: \${${variableName}}`,
location: 'template',
payload: String(variableValue),
recommendation: 'Use proper HTML escaping for all template variables.',
});
}
}
return vulnerabilities;
}
/**
* Determine the type of XSS vulnerability based on the payload
* @param payload The detected payload
* @returns Vulnerability type
*/
private determineVulnerabilityType(payload: string): XssVulnerabilityType {
if (payload.includes('<script')) {
return XssVulnerabilityType.REFLECTED;
}
if (payload.includes('javascript:')) {
return XssVulnerabilityType.DOM_BASED;
}
if (payload.includes('${') || payload.includes('{{')) {
return XssVulnerabilityType.TEMPLATE_INJECTION;
}
return XssVulnerabilityType.STORED;
}
/**
* Determine the severity of a vulnerability based on the payload
* @param payload The detected payload
* @returns Vulnerability severity
*/
private determineSeverity(payload: string): VulnerabilitySeverity {
if (payload.includes('<script') || payload.includes('javascript:')) {
return VulnerabilitySeverity.CRITICAL;
}
if (payload.includes('on') && payload.includes('=')) {
return VulnerabilitySeverity.HIGH;
}
if (payload.includes('<') || payload.includes('>')) {
return VulnerabilitySeverity.MEDIUM;
}
return VulnerabilitySeverity.LOW;
}
/**
* Get recommendation for fixing a vulnerability
* @param payload The detected payload
* @returns Recommendation string
*/
private getRecommendation(payload: string): string {
if (payload.includes('<script')) {
return 'Remove script tags and use proper JavaScript loading mechanisms.';
}
if (payload.includes('javascript:')) {
return 'Replace javascript: URLs with proper event handlers.';
}
if (payload.includes('on') && payload.includes('=')) {
return 'Remove inline event handlers and use addEventListener instead.';
}
return 'Sanitize or escape the content before rendering.';
}
/**
* Add a custom XSS pattern to detect
* @param pattern Regular expression pattern
*/
addPattern(pattern: RegExp): void {
this.patterns.push(pattern);
}
/**
* Add a custom dangerous attribute to detect
* @param attribute Attribute name
*/
addDangerousAttribute(attribute: string): void {
this.dangerousAttributes.push(attribute.toLowerCase());
}
}
/**
* Default XSS vulnerability detector instance
*/
export const defaultXssDetector = new XssVulnerabilityDetector();