Skip to main content
Glama
owasp-api-security-tests.jsβ€’30.6 kB
/** * OWASP API Security Top 10:2023 Automated Test Suite * Smart AI Bridge v1.3.0 Security Validation * * Tests all 10 OWASP API Security categories with evidence collection * * Run: node security/tests/owasp-api-security-tests.js */ import { authManager, AuthManager } from '../../auth-manager.js'; import { RateLimiter } from '../../rate-limiter.js'; import { InputValidator } from '../../input-validator.js'; import { ErrorSanitizer } from '../../error-sanitizer.js'; import { CircuitBreaker, FallbackResponseGenerator } from '../../circuit-breaker.js'; import { validatePath, safeJoin } from '../../path-security.js'; import { validateFuzzyEditComplexity, createFuzzyTimeoutWrapper, FUZZY_SECURITY_LIMITS } from '../../fuzzy-matching-security.js'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; import fs from 'fs/promises'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Test result tracking const testResults = { passed: 0, failed: 0, skipped: 0, tests: [], categories: {} }; // Utility functions function logTest(category, testId, name, passed, details = '', evidence = null) { const result = { category, testId, name, passed, details, evidence, timestamp: new Date().toISOString() }; testResults.tests.push(result); if (passed) { testResults.passed++; console.log(` βœ… [${testId}] ${name}`); } else { testResults.failed++; console.error(` ❌ [${testId}] ${name}`); if (details) console.error(` Details: ${details}`); } // Track by category if (!testResults.categories[category]) { testResults.categories[category] = { passed: 0, failed: 0 }; } testResults.categories[category][passed ? 'passed' : 'failed']++; } // ============================================================================ // API1:2023 - Broken Object Level Authorization // ============================================================================ async function testAPI1_BrokenObjectLevelAuth() { console.log('\nπŸ”’ API1:2023 - Broken Object Level Authorization\n'); const category = 'API1'; // Test 1.1: Tool permission enforcement const testAuth = new AuthManager(); const limitedToken = testAuth.generateToken(['read']); const canRead = testAuth.hasToolPermission(limitedToken, 'read'); const canWrite = testAuth.hasToolPermission(limitedToken, 'write'); logTest(category, 'API1-001', 'Limited token can access permitted tool', canRead === true, canRead ? '' : 'Permission granted incorrectly', { token: limitedToken.substring(0, 8), permissions: ['read'], testedTool: 'read', result: canRead } ); logTest(category, 'API1-002', 'Limited token cannot access unpermitted tool', canWrite === false, canWrite ? 'Permission should be denied' : '', { token: limitedToken.substring(0, 8), permissions: ['read'], testedTool: 'write', result: canWrite } ); // Test 1.3: Wildcard permission const wildcardToken = testAuth.generateToken(['*']); const canDoAnything = testAuth.hasToolPermission(wildcardToken, 'anyTool'); logTest(category, 'API1-003', 'Wildcard token has universal access', canDoAnything === true, canDoAnything ? '' : 'Wildcard should grant access', { permissions: ['*'], testedTool: 'anyTool', result: canDoAnything } ); // Test 1.4: No token provided testAuth.validTokens.clear(); // Clear to test development mode const noTokenAccess = testAuth.hasToolPermission(null, 'read'); logTest(category, 'API1-004', 'Development mode behavior when no auth configured', noTokenAccess === true, // This is expected in dev mode '', { note: 'Development mode allows access when no tokens configured' } ); // Test 1.5: Invalid token denied testAuth.initializeMasterToken(); const invalidTokenAccess = testAuth.hasToolPermission('invalid-token', 'read'); logTest(category, 'API1-005', 'Invalid token denied access', invalidTokenAccess === false, invalidTokenAccess ? 'Invalid token should be denied' : '', { token: 'invalid-token', result: invalidTokenAccess } ); } // ============================================================================ // API2:2023 - Broken Authentication // ============================================================================ async function testAPI2_BrokenAuth() { console.log('\nπŸ” API2:2023 - Broken Authentication\n'); const category = 'API2'; // Test 2.1: Token generation produces secure tokens const testAuth = new AuthManager(); const token1 = testAuth.generateToken(); const token2 = testAuth.generateToken(); logTest(category, 'API2-001', 'Token generation produces 64-character hex', token1.length === 64 && /^[0-9a-f]+$/.test(token1), token1.length !== 64 ? `Got ${token1.length} chars` : '', { tokenLength: token1.length, isHex: /^[0-9a-f]+$/.test(token1) } ); logTest(category, 'API2-002', 'Tokens are unique', token1 !== token2, token1 === token2 ? 'Tokens should be unique' : '', { token1Preview: token1.substring(0, 8), token2Preview: token2.substring(0, 8) } ); // Test 2.3: Token validation logTest(category, 'API2-003', 'Generated token is immediately valid', testAuth.isValidToken(token1), '', { token: token1.substring(0, 8), valid: testAuth.isValidToken(token1) } ); // Test 2.4: Token revocation testAuth.revokeToken(token1); logTest(category, 'API2-004', 'Revoked token is no longer valid', !testAuth.isValidToken(token1), testAuth.isValidToken(token1) ? 'Token should be invalid after revocation' : '', { token: token1.substring(0, 8), valid: testAuth.isValidToken(token1) } ); // Test 2.5: Null token handling logTest(category, 'API2-005', 'Null token handled correctly', testAuth.isValidToken(null) === true || testAuth.isValidToken(null) === false, '', { note: 'Returns true in dev mode (no tokens), false otherwise' } ); // Test 2.6: Undefined token handling logTest(category, 'API2-006', 'Undefined token handled correctly', testAuth.isValidToken(undefined) === true || testAuth.isValidToken(undefined) === false, '', { note: 'Returns true in dev mode (no tokens), false otherwise' } ); // Test 2.7: Master token from environment const envToken = 'test-env-token-' + Date.now(); process.env.MCP_AUTH_TOKEN = envToken; const envAuth = new AuthManager(); logTest(category, 'API2-007', 'Master token loaded from environment', envAuth.isValidToken(envToken), '', { envVar: 'MCP_AUTH_TOKEN', tokenValid: envAuth.isValidToken(envToken) } ); } // ============================================================================ // API3:2023 - Broken Object Property Level Authorization // ============================================================================ async function testAPI3_BrokenObjectPropertyAuth() { console.log('\nπŸ›‘οΈ API3:2023 - Broken Object Property Level Authorization\n'); const category = 'API3'; // Test 3.1: File path sanitization in errors const pathError = new Error('File /home/user/secrets/api-key.txt not found'); const sanitized = ErrorSanitizer.sanitize(pathError, false); logTest(category, 'API3-001', 'File paths sanitized in production errors', !sanitized.includes('/home/user'), sanitized.includes('/home/user') ? 'Path should be redacted' : '', { original: pathError.message, sanitized } ); // Test 3.2: Windows path sanitization const winError = new Error('File C:\\Users\\admin\\secret.txt not found'); const winSanitized = ErrorSanitizer.sanitize(winError, false); logTest(category, 'API3-002', 'Windows paths sanitized in production errors', !winSanitized.includes('C:\\Users\\admin'), winSanitized.includes('C:\\Users\\admin') ? 'Windows path should be redacted' : '', { original: winError.message, sanitized: winSanitized } ); // Test 3.3: API endpoint sanitization const apiError = new Error('Failed to call https://api.example.com/v1/users/secret'); const apiSanitized = ErrorSanitizer.sanitize(apiError, false); logTest(category, 'API3-003', 'API endpoints sanitized in errors', apiSanitized.includes('https://***'), !apiSanitized.includes('https://***') ? 'API endpoint should be redacted' : '', { original: apiError.message, sanitized: apiSanitized } ); // Test 3.4: Database connection strings const dbError = new Error('mongodb://admin:password@localhost:27017 connection failed'); const dbSanitized = ErrorSanitizer.sanitize(dbError, false); logTest(category, 'API3-004', 'Database credentials sanitized in errors', !dbSanitized.includes('admin:password'), dbSanitized.includes('admin:password') ? 'DB credentials should be redacted' : '', { original: dbError.message, sanitized: dbSanitized } ); // Test 3.5: Stack traces removed in production const stackError = new Error('Error occurred\n at someFunction (file.js:42:15)'); const stackSanitized = ErrorSanitizer.sanitize(stackError, false); logTest(category, 'API3-005', 'Stack traces removed in production', !stackSanitized.includes('at '), stackSanitized.includes('at ') ? 'Stack trace should be removed' : '', { original: stackError.message, sanitized: stackSanitized } ); // Test 3.6: Token partial display const testAuth = new AuthManager(); const fullToken = testAuth.generateToken(); const stats = testAuth.getStats(); const displayedToken = stats.tokens[0]?.token; logTest(category, 'API3-006', 'Tokens displayed partially in stats', displayedToken && displayedToken.includes('...') && displayedToken.length < fullToken.length, '', { fullLength: fullToken.length, displayedLength: displayedToken?.length } ); } // ============================================================================ // API4:2023 - Unrestricted Resource Consumption // ============================================================================ async function testAPI4_UnrestrictedResourceConsumption() { console.log('\n⚑ API4:2023 - Unrestricted Resource Consumption\n'); const category = 'API4'; // Test 4.1: Rate limiting - per minute const limiter = new RateLimiter({ perMinute: 3, perHour: 100, perDay: 1000 }); const testId = 'rate-test-' + Date.now(); let allowedCount = 0; for (let i = 0; i < 5; i++) { if (limiter.checkLimit(testId).allowed) allowedCount++; } logTest(category, 'API4-001', 'Rate limiting blocks after threshold', allowedCount === 3, allowedCount !== 3 ? `Expected 3 allowed, got ${allowedCount}` : '', { limit: 3, attempts: 5, allowed: allowedCount } ); // Test 4.2: Rate limit retry-after const blockedResult = limiter.checkLimit(testId); logTest(category, 'API4-002', 'Rate limit provides retry-after', !blockedResult.allowed && typeof blockedResult.retryAfter === 'number', '', { allowed: blockedResult.allowed, retryAfter: blockedResult.retryAfter } ); // Test 4.3: Rate limit statistics const stats = limiter.getStats(testId); logTest(category, 'API4-003', 'Rate limiter provides usage statistics', stats.perMinute && stats.perMinute.used >= 3, '', { stats } ); // Test 4.4: Fuzzy edit complexity limits const validEdits = [{ find: 'x'.repeat(100), replace: 'y'.repeat(100) }]; const validResult = validateFuzzyEditComplexity(validEdits); logTest(category, 'API4-004', 'Valid complexity edits accepted', validResult.valid === true, validResult.valid ? '' : validResult.errors.join(', '), { editLength: 100, valid: validResult.valid } ); // Test 4.5: Oversized edit rejected const oversizedEdits = [{ find: 'x'.repeat(6000), replace: 'y' }]; const oversizedResult = validateFuzzyEditComplexity(oversizedEdits); logTest(category, 'API4-005', 'Oversized edits rejected', oversizedResult.valid === false, oversizedResult.valid ? 'Oversized edit should be rejected' : '', { editLength: 6000, limit: FUZZY_SECURITY_LIMITS.MAX_FUZZY_EDIT_LENGTH, valid: oversizedResult.valid } ); // Test 4.6: Total complexity limit const manyEdits = Array(100).fill({ find: 'x'.repeat(1000), replace: 'y'.repeat(1000) }); const manyResult = validateFuzzyEditComplexity(manyEdits); logTest(category, 'API4-006', 'Total complexity limit enforced', manyResult.valid === false, manyResult.valid ? 'Should exceed total complexity' : '', { totalChars: manyResult.totalCharacters, limit: FUZZY_SECURITY_LIMITS.MAX_FUZZY_TOTAL_CHARS } ); // Test 4.7: Timeout wrapper const slowOperation = () => new Promise(resolve => setTimeout(resolve, 10000)); const timeoutStart = Date.now(); try { await createFuzzyTimeoutWrapper(slowOperation, 100); logTest(category, 'API4-007', 'Timeout wrapper enforces limits', false, 'Should have timed out'); } catch (error) { const elapsed = Date.now() - timeoutStart; logTest(category, 'API4-007', 'Timeout wrapper enforces limits', error.message.includes('timed out') && elapsed < 1000, '', { elapsed, message: error.message } ); } // Test 4.8: Line count limit const manyLines = { find: 'x\n'.repeat(250), replace: 'y' }; const lineResult = validateFuzzyEditComplexity([manyLines]); logTest(category, 'API4-008', 'Line count limit enforced', lineResult.valid === false, lineResult.valid ? 'Should exceed line limit' : '', { lineCount: 250, limit: FUZZY_SECURITY_LIMITS.MAX_FUZZY_LINE_COUNT } ); } // ============================================================================ // API5:2023 - Broken Function Level Authorization // ============================================================================ async function testAPI5_BrokenFunctionLevelAuth() { console.log('\nπŸ”‘ API5:2023 - Broken Function Level Authorization\n'); const category = 'API5'; // Test 5.1: Master vs generated token types const testAuth = new AuthManager(); process.env.MCP_AUTH_TOKEN = 'master-token-123'; testAuth.initializeMasterToken(); const masterMeta = testAuth.tokenMetadata.get('master-token-123'); const generatedToken = testAuth.generateToken(['read']); const generatedMeta = testAuth.tokenMetadata.get(generatedToken); logTest(category, 'API5-001', 'Master token type tracked correctly', masterMeta && masterMeta.type === 'master', '', { type: masterMeta?.type, permissions: masterMeta?.permissions } ); logTest(category, 'API5-002', 'Generated token type tracked correctly', generatedMeta && generatedMeta.type === 'generated', '', { type: generatedMeta?.type, permissions: generatedMeta?.permissions } ); // Test 5.3: Master has all permissions const masterCanReview = testAuth.hasToolPermission('master-token-123', 'review'); const masterCanAdmin = testAuth.hasToolPermission('master-token-123', 'admin'); logTest(category, 'API5-003', 'Master token has all permissions', masterCanReview && masterCanAdmin, '', { review: masterCanReview, admin: masterCanAdmin } ); // Test 5.4: Generated has limited permissions const genCanRead = testAuth.hasToolPermission(generatedToken, 'read'); const genCanAdmin = testAuth.hasToolPermission(generatedToken, 'admin'); logTest(category, 'API5-004', 'Generated token has limited permissions', genCanRead && !genCanAdmin, '', { read: genCanRead, admin: genCanAdmin } ); } // ============================================================================ // API6:2023 - Unrestricted Access to Sensitive Business Flows // ============================================================================ async function testAPI6_SensitiveBusinessFlows() { console.log('\n🏒 API6:2023 - Unrestricted Access to Sensitive Business Flows\n'); const category = 'API6'; // Test 6.1: Circuit breaker - initial state const breaker = new CircuitBreaker({ failureThreshold: 3, timeout: 1000 }); logTest(category, 'API6-001', 'Circuit breaker starts in CLOSED state', breaker.state === 'CLOSED', '', { state: breaker.state } ); // Test 6.2: Circuit breaker - opens after failures for (let i = 0; i < 3; i++) { try { await breaker.execute(() => Promise.reject(new Error('failure'))); } catch {} } logTest(category, 'API6-002', 'Circuit breaker opens after threshold failures', breaker.state === 'OPEN', breaker.state !== 'OPEN' ? `State is ${breaker.state}` : '', { state: breaker.state, failures: breaker.failures.length } ); // Test 6.3: Circuit breaker - rejects when open let rejected = false; try { await breaker.execute(() => Promise.resolve('success')); } catch (error) { rejected = error.message.includes('OPEN'); } logTest(category, 'API6-003', 'Open circuit breaker rejects requests', rejected, '', { state: breaker.state, rejected } ); // Test 6.4: Circuit breaker - fallback works const fallbackBreaker = new CircuitBreaker({ failureThreshold: 1, timeout: 100 }); await fallbackBreaker.execute( () => Promise.reject(new Error('fail')), () => Promise.resolve('fallback') ).catch(() => {}); fallbackBreaker.state = 'OPEN'; const result = await fallbackBreaker.execute( () => Promise.resolve('primary'), () => Promise.resolve('fallback') ); logTest(category, 'API6-004', 'Fallback executes when circuit open', result === 'fallback', '', { result } ); // Test 6.5: Circuit breaker - metrics tracking const status = breaker.getStatus(); logTest(category, 'API6-005', 'Circuit breaker tracks metrics', status.metrics && typeof status.metrics.totalRequests === 'number', '', { metrics: status.metrics } ); // Test 6.6: Fallback response generator const fallbackGen = new FallbackResponseGenerator({}); const fallbackResponse = await fallbackGen.generateFallbackResponse('Analyze this code'); logTest(category, 'API6-006', 'Fallback responses generated correctly', fallbackResponse.success && fallbackResponse.fallback === true, '', { model: fallbackResponse.model, fallback: fallbackResponse.fallback } ); } // ============================================================================ // API7:2023 - Server Side Request Forgery // ============================================================================ async function testAPI7_SSRF() { console.log('\n🌐 API7:2023 - Server Side Request Forgery\n'); const category = 'API7'; const baseDir = __dirname; // Test 7.1: Path traversal blocked try { await validatePath('../../../etc/passwd', baseDir); logTest(category, 'API7-001', 'Path traversal blocked', false, 'Should have thrown'); } catch (error) { logTest(category, 'API7-001', 'Path traversal blocked', error.message.includes('traversal') || error.message.includes('outside'), '', { path: '../../../etc/passwd', error: error.message } ); } // Test 7.2: Absolute path outside base blocked try { await validatePath('/etc/passwd', baseDir); logTest(category, 'API7-002', 'Absolute path outside base blocked', false, 'Should have thrown'); } catch (error) { logTest(category, 'API7-002', 'Absolute path outside base blocked', true, '', { path: '/etc/passwd', error: error.message } ); } // Test 7.3: Null byte injection blocked try { await validatePath('file.txt\0.jpg', baseDir); logTest(category, 'API7-003', 'Null byte injection blocked', false, 'Should have thrown'); } catch (error) { logTest(category, 'API7-003', 'Null byte injection blocked', true, '', { path: 'file.txt\\0.jpg', error: error.message } ); } // Test 7.4: Safe join protects against traversal try { safeJoin(baseDir, '..', '..', 'etc', 'passwd'); logTest(category, 'API7-004', 'safeJoin blocks traversal', false, 'Should have thrown'); } catch (error) { logTest(category, 'API7-004', 'safeJoin blocks traversal', true, '', { error: error.message } ); } // Test 7.5: Valid path accepted try { const validPath = await validatePath('./test-file.txt', baseDir); logTest(category, 'API7-005', 'Valid relative path accepted', validPath && validPath.startsWith(baseDir), '', { path: './test-file.txt', resolved: validPath } ); } catch (error) { logTest(category, 'API7-005', 'Valid relative path accepted', false, error.message); } // Test 7.6: Windows path on Unix blocked if (process.platform !== 'win32') { try { await validatePath('C:\\Windows\\System32\\config\\sam', baseDir); logTest(category, 'API7-006', 'Windows paths blocked on Unix', false, 'Should have thrown'); } catch (error) { logTest(category, 'API7-006', 'Windows paths blocked on Unix', error.message.includes('Windows'), '', { error: error.message } ); } } else { logTest(category, 'API7-006', 'Windows paths blocked on Unix', true, 'N/A on Windows'); } } // ============================================================================ // API8:2023 - Security Misconfiguration // ============================================================================ async function testAPI8_SecurityMisconfiguration() { console.log('\nβš™οΈ API8:2023 - Security Misconfiguration\n'); const category = 'API8'; // Test 8.1: Default rate limits configured const defaultLimiter = new RateLimiter(); logTest(category, 'API8-001', 'Default rate limits configured', defaultLimiter.limits.perMinute === 60 && defaultLimiter.limits.perHour === 500 && defaultLimiter.limits.perDay === 5000, '', { limits: defaultLimiter.limits } ); // Test 8.2: Default DoS limits configured logTest(category, 'API8-002', 'Default DoS limits configured', FUZZY_SECURITY_LIMITS.MAX_FUZZY_EDIT_LENGTH === 5000 && FUZZY_SECURITY_LIMITS.MAX_FUZZY_ITERATIONS === 10000 && FUZZY_SECURITY_LIMITS.FUZZY_TIMEOUT_MS === 5000, '', { limits: FUZZY_SECURITY_LIMITS } ); // Test 8.3: Production mode detection logTest(category, 'API8-003', 'Production mode detection available', typeof process.env.NODE_ENV !== 'undefined' || true, // Always passes, checks mechanism exists '', { nodeEnv: process.env.NODE_ENV } ); // Test 8.4: Example configs exist try { await fs.access(dirname(__dirname) + '/../.env.example'); logTest(category, 'API8-004', 'Example env file exists', true); } catch { logTest(category, 'API8-004', 'Example env file exists', false, '.env.example not found'); } // Test 8.5: Default circuit breaker configuration const defaultBreaker = new CircuitBreaker(); logTest(category, 'API8-005', 'Default circuit breaker configured', defaultBreaker.failureThreshold === 5 && defaultBreaker.timeout === 60000, '', { failureThreshold: defaultBreaker.failureThreshold, timeout: defaultBreaker.timeout } ); } // ============================================================================ // API9:2023 - Improper Inventory Management // ============================================================================ async function testAPI9_ImproperInventoryManagement() { console.log('\nπŸ“‹ API9:2023 - Improper Inventory Management\n'); const category = 'API9'; // Test 9.1: Package.json exists try { const packageJson = await fs.readFile(dirname(__dirname) + '/../package.json', 'utf8'); const pkg = JSON.parse(packageJson); logTest(category, 'API9-001', 'Package.json exists with version', pkg.version && /^\d+\.\d+\.\d+/.test(pkg.version), '', { version: pkg.version, name: pkg.name } ); } catch (error) { logTest(category, 'API9-001', 'Package.json exists with version', false, error.message); } // Test 9.2: Changelog exists try { await fs.access(dirname(__dirname) + '/../CHANGELOG.md'); logTest(category, 'API9-002', 'Changelog documentation exists', true); } catch { logTest(category, 'API9-002', 'Changelog documentation exists', false, 'CHANGELOG.md not found'); } // Test 9.3: README exists try { await fs.access(dirname(__dirname) + '/../README.md'); logTest(category, 'API9-003', 'README documentation exists', true); } catch { logTest(category, 'API9-003', 'README documentation exists', false, 'README.md not found'); } } // ============================================================================ // API10:2023 - Unsafe Consumption of APIs // ============================================================================ async function testAPI10_UnsafeConsumption() { console.log('\nπŸ”Œ API10:2023 - Unsafe Consumption of APIs\n'); const category = 'API10'; // Test 10.1: Input validation available logTest(category, 'API10-001', 'Input validation module available', typeof InputValidator.validateString === 'function' && typeof InputValidator.validateInteger === 'function' && typeof InputValidator.validateArray === 'function', '', { methods: ['validateString', 'validateInteger', 'validateArray'] } ); // Test 10.2: Error sanitization on backend errors const backendError = new Error('Backend error: mongodb://user:pass@localhost failed'); const sanitized = ErrorSanitizer.createErrorResponse(backendError, 'backend call'); logTest(category, 'API10-002', 'Backend errors sanitized', !JSON.stringify(sanitized).includes('user:pass'), '', { hasRequestId: !!sanitized.error.requestId } ); // Test 10.3: Circuit breaker protects backend calls const backendBreaker = new CircuitBreaker(); let backendProtected = false; try { await backendBreaker.execute(() => Promise.reject(new Error('Backend failure'))); } catch { backendProtected = true; } logTest(category, 'API10-003', 'Circuit breaker wraps backend calls', backendProtected && backendBreaker.failures.length > 0, '', { failures: backendBreaker.failures.length } ); // Test 10.4: Fallback available for backend failures const fallbackGen = new FallbackResponseGenerator({}); const fallback = await fallbackGen.generateFallbackResponse('Process file'); logTest(category, 'API10-004', 'Fallback available for backend failures', fallback.success && fallback.fallback, '', { model: fallback.model } ); // Test 10.5: String sanitization const malicious = 'test\0hidden\x00chars'; const clean = InputValidator.sanitizeString(malicious); logTest(category, 'API10-005', 'String sanitization removes null bytes', !clean.includes('\0'), '', { original: malicious.replace(/\0/g, '\\0'), sanitized: clean } ); } // ============================================================================ // Main Runner // ============================================================================ async function runAllTests() { console.log('╔════════════════════════════════════════════════════════════════╗'); console.log('β•‘ OWASP API Security Top 10:2023 Test Suite β•‘'); console.log('β•‘ Smart AI Bridge v1.3.0 Security Validation β•‘'); console.log('β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•'); console.log(''); console.log(`Started: ${new Date().toISOString()}`); const startTime = Date.now(); await testAPI1_BrokenObjectLevelAuth(); await testAPI2_BrokenAuth(); await testAPI3_BrokenObjectPropertyAuth(); await testAPI4_UnrestrictedResourceConsumption(); await testAPI5_BrokenFunctionLevelAuth(); await testAPI6_SensitiveBusinessFlows(); await testAPI7_SSRF(); await testAPI8_SecurityMisconfiguration(); await testAPI9_ImproperInventoryManagement(); await testAPI10_UnsafeConsumption(); const duration = Date.now() - startTime; // Summary console.log('\n╔════════════════════════════════════════════════════════════════╗'); console.log('β•‘ TEST RESULTS SUMMARY β•‘'); console.log('β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•'); console.log('\nπŸ“Š Results by Category:\n'); for (const [cat, results] of Object.entries(testResults.categories)) { const total = results.passed + results.failed; const percentage = Math.round((results.passed / total) * 100); const status = percentage >= 90 ? 'βœ…' : percentage >= 70 ? '⚠️' : '❌'; console.log(` ${status} ${cat}: ${results.passed}/${total} (${percentage}%)`); } console.log('\nπŸ“ˆ Overall Results:'); console.log(` Total Tests: ${testResults.passed + testResults.failed}`); console.log(` βœ… Passed: ${testResults.passed}`); console.log(` ❌ Failed: ${testResults.failed}`); console.log(` ⏱️ Duration: ${duration}ms`); console.log(` πŸ“Š Success Rate: ${Math.round((testResults.passed / (testResults.passed + testResults.failed)) * 100)}%`); // Save results to file const resultsFile = dirname(__dirname) + '/test-results/owasp-api-security-results.json'; try { await fs.mkdir(dirname(resultsFile), { recursive: true }); await fs.writeFile(resultsFile, JSON.stringify({ timestamp: new Date().toISOString(), duration, summary: { total: testResults.passed + testResults.failed, passed: testResults.passed, failed: testResults.failed, successRate: Math.round((testResults.passed / (testResults.passed + testResults.failed)) * 100) }, categories: testResults.categories, tests: testResults.tests }, null, 2)); console.log(`\nπŸ’Ύ Results saved to: ${resultsFile}`); } catch (error) { console.error(`\n⚠️ Could not save results: ${error.message}`); } if (testResults.failed === 0) { console.log('\nπŸŽ‰ ALL OWASP API SECURITY TESTS PASSED! πŸŽ‰'); process.exit(0); } else { console.log(`\n⚠️ ${testResults.failed} test(s) failed. Review and fix before release.`); process.exit(1); } } // Run runAllTests().catch(error => { console.error('\nπŸ’₯ FATAL ERROR:', error); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Platano78/Smart-AI-Bridge'

If you have feedback or need assistance with the MCP directory API, please join our Discord server