fortify2-js
Version:
MOST POWERFUL JavaScript Security Library! Military-grade cryptography + 19 enhanced object methods + quantum-resistant algorithms + perfect TypeScript support. More powerful than Lodash with built-in security.
333 lines (331 loc) • 11.2 kB
JavaScript
/**
* String Validator Module
* Handles validation and analysis of string content
*/
/**
* Handles string validation and analysis
*/
class StringValidator {
/**
* Validates if a string meets password requirements
*/
static validatePassword(password, requirements = {}) {
const { minLength = 8, maxLength = 128, requireUppercase = true, requireLowercase = true, requireNumbers = true, requireSpecialChars = true, forbiddenPatterns = [], customRules = [], } = requirements;
const errors = [];
const warnings = [];
// Length validation
if (password.length < minLength) {
errors.push(`Password must be at least ${minLength} characters long`);
}
if (password.length > maxLength) {
errors.push(`Password must not exceed ${maxLength} characters`);
}
// Character type validation
if (requireUppercase && !/[A-Z]/.test(password)) {
errors.push("Password must contain at least one uppercase letter");
}
if (requireLowercase && !/[a-z]/.test(password)) {
errors.push("Password must contain at least one lowercase letter");
}
if (requireNumbers && !/\d/.test(password)) {
errors.push("Password must contain at least one number");
}
if (requireSpecialChars && !/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) {
errors.push("Password must contain at least one special character");
}
// Forbidden patterns
for (const pattern of forbiddenPatterns) {
if (pattern.test(password)) {
errors.push(`Password contains forbidden pattern: ${pattern.source}`);
}
}
// Common weak patterns
if (/(.)\1{2,}/.test(password)) {
warnings.push("Password contains repeated characters");
}
if (/123|abc|qwe|asd|zxc/i.test(password)) {
warnings.push("Password contains common sequences");
}
// Custom rules
for (const rule of customRules) {
const ruleResult = rule(password);
if (ruleResult) {
errors.push(ruleResult);
}
}
// Calculate strength score
const score = this.calculatePasswordStrength(password);
return {
isValid: errors.length === 0,
errors,
warnings,
score,
};
}
/**
* Validates email format
*/
static validateEmail(email) {
const errors = [];
const warnings = [];
// Basic email regex (RFC 5322 compliant)
const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
if (!emailRegex.test(email)) {
errors.push("Invalid email format");
}
// Additional checks
if (email.length > 254) {
errors.push("Email address is too long");
}
const [localPart, domain] = email.split('@');
if (localPart && localPart.length > 64) {
errors.push("Local part of email is too long");
}
if (domain && domain.includes('..')) {
errors.push("Domain contains consecutive dots");
}
return {
isValid: errors.length === 0,
errors,
warnings,
};
}
/**
* Validates URL format
*/
static validateURL(url) {
const errors = [];
const warnings = [];
try {
const urlObj = new URL(url);
// Check for common issues
if (!['http:', 'https:', 'ftp:', 'ftps:'].includes(urlObj.protocol)) {
warnings.push(`Unusual protocol: ${urlObj.protocol}`);
}
if (urlObj.hostname.includes('..')) {
errors.push("Hostname contains consecutive dots");
}
}
catch (error) {
errors.push("Invalid URL format");
}
return {
isValid: errors.length === 0,
errors,
warnings,
};
}
/**
* Validates phone number format
*/
static validatePhoneNumber(phone, format = 'flexible') {
const errors = [];
const warnings = [];
// Remove common formatting characters
const cleanPhone = phone.replace(/[\s\-\(\)\+\.]/g, '');
switch (format) {
case 'international':
if (!/^\+?[1-9]\d{1,14}$/.test(cleanPhone)) {
errors.push("Invalid international phone number format");
}
break;
case 'us':
if (!/^(\+?1)?[2-9]\d{2}[2-9]\d{2}\d{4}$/.test(cleanPhone)) {
errors.push("Invalid US phone number format");
}
break;
case 'flexible':
if (!/^\+?[\d\s\-\(\)\.]{7,15}$/.test(phone)) {
errors.push("Phone number should be 7-15 digits");
}
break;
}
return {
isValid: errors.length === 0,
errors,
warnings,
};
}
/**
* Validates credit card number using Luhn algorithm
*/
static validateCreditCard(cardNumber) {
const errors = [];
const warnings = [];
// Remove spaces and dashes
const cleanNumber = cardNumber.replace(/[\s\-]/g, '');
// Check if all characters are digits
if (!/^\d+$/.test(cleanNumber)) {
errors.push("Credit card number must contain only digits");
}
else {
// Luhn algorithm
let sum = 0;
let isEven = false;
for (let i = cleanNumber.length - 1; i >= 0; i--) {
let digit = parseInt(cleanNumber.charAt(i), 10);
if (isEven) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
isEven = !isEven;
}
if (sum % 10 !== 0) {
errors.push("Invalid credit card number (failed Luhn check)");
}
// Check length for common card types
const length = cleanNumber.length;
if (![13, 14, 15, 16, 17, 18, 19].includes(length)) {
warnings.push("Unusual credit card number length");
}
}
return {
isValid: errors.length === 0,
errors,
warnings,
};
}
/**
* Calculates password strength score (0-100)
*/
static calculatePasswordStrength(password) {
let score = 0;
// Length bonus
score += Math.min(password.length * 2, 25);
// Character variety bonus
if (/[a-z]/.test(password))
score += 5;
if (/[A-Z]/.test(password))
score += 5;
if (/\d/.test(password))
score += 5;
if (/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password))
score += 10;
// Complexity bonus
const uniqueChars = new Set(password).size;
score += Math.min(uniqueChars * 2, 20);
// Entropy bonus
const entropy = this.calculateEntropy(password);
score += Math.min(entropy / 2, 20);
// Penalties
if (/(.)\1{2,}/.test(password))
score -= 10; // Repeated characters
if (/123|abc|qwe|asd|zxc/i.test(password))
score -= 15; // Common sequences
if (/password|123456|qwerty/i.test(password))
score -= 25; // Common passwords
return Math.max(0, Math.min(100, score));
}
/**
* Calculates Shannon entropy of a string
*/
static calculateEntropy(str) {
const frequencies = {};
// Count character frequencies
for (const char of str) {
frequencies[char] = (frequencies[char] || 0) + 1;
}
// Calculate entropy
let entropy = 0;
const length = str.length;
for (const count of Object.values(frequencies)) {
const probability = count / length;
entropy -= probability * Math.log2(probability);
}
return entropy * length;
}
/**
* Gets detailed string statistics
*/
static getStringStatistics(str) {
const characterCount = {};
// Count characters
for (const char of str) {
characterCount[char] = (characterCount[char] || 0) + 1;
}
return {
length: str.length,
byteLength: new TextEncoder().encode(str).length,
characterCount,
hasUpperCase: /[A-Z]/.test(str),
hasLowerCase: /[a-z]/.test(str),
hasNumbers: /\d/.test(str),
hasSpecialChars: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(str),
entropy: this.calculateEntropy(str),
};
}
/**
* Checks if string contains only printable ASCII characters
*/
static isPrintableASCII(str) {
return /^[\x20-\x7E]*$/.test(str);
}
/**
* Checks if string is valid UTF-8
*/
static isValidUTF8(str) {
try {
// Try to encode and decode
const encoded = new TextEncoder().encode(str);
const decoded = new TextDecoder('utf-8', { fatal: true }).decode(encoded);
return decoded === str;
}
catch {
return false;
}
}
/**
* Validates JSON string
*/
static validateJSON(str) {
const errors = [];
const warnings = [];
try {
JSON.parse(str);
}
catch (error) {
errors.push(`Invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
}
return {
isValid: errors.length === 0,
errors,
warnings,
};
}
/**
* Validates XML string (basic check)
*/
static validateXML(str) {
const errors = [];
const warnings = [];
try {
if (typeof DOMParser !== 'undefined') {
const parser = new DOMParser();
const doc = parser.parseFromString(str, 'text/xml');
const parseError = doc.querySelector('parsererror');
if (parseError) {
errors.push('Invalid XML format');
}
}
else {
// Basic XML validation for Node.js environment
if (!str.trim().startsWith('<') || !str.trim().endsWith('>')) {
errors.push('XML must start with < and end with >');
}
}
}
catch (error) {
errors.push(`XML validation error: ${error instanceof Error ? error.message : String(error)}`);
}
return {
isValid: errors.length === 0,
errors,
warnings,
};
}
}
export { StringValidator };
//# sourceMappingURL=string-validator.js.map