websec-audit
Version:
A universal security scanning and audit tool for websites
1 lines • 20.9 kB
Source Map (JSON)
{"version":3,"sources":["../../src/core/request.ts","../../src/modules/formDetection.ts","../../src/modules/securityHeaders.ts","../../src/frontend/index.ts"],"names":["input"],"mappings":"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","sourcesContent":["import { default as axios, AxiosRequestConfig } from 'axios';\r\nimport { ScannerInput } from '../types';\r\n\r\n/**\r\n * Makes HTTP requests with appropriate timeouts and error handling\r\n */\r\nexport interface MakeRequestResult {\r\n status: number;\r\n headers: Record<string, string | string[]>;\r\n data: any;\r\n error: string | null;\r\n}\r\n\r\nexport const makeRequest = async (\r\n url: string,\r\n options?: {\r\n headers?: Record<string, string>;\r\n timeout?: number;\r\n method?: 'GET' | 'POST' | 'HEAD';\r\n data?: any;\r\n }\r\n): Promise<MakeRequestResult> => {\r\n try {\r\n const config: AxiosRequestConfig = {\r\n url,\r\n method: options?.method || 'GET',\r\n headers: {\r\n 'User-Agent': 'Mozilla/5.0 (compatible; SecurityScanner/1.0)',\r\n ...options?.headers\r\n },\r\n timeout: options?.timeout || 10000, // Default 10s timeout\r\n data: options?.data,\r\n validateStatus: () => true // Don't throw on any HTTP status code\r\n };\r\n \r\n const response = await axios(config);\r\n return {\r\n status: response.status,\r\n headers: response.headers as Record<string, string | string[]>,\r\n data: response.data,\r\n error: null\r\n };\r\n } catch (error: any) {\r\n // This will only catch network errors, timeouts, etc.\r\n // HTTP errors like 404, 500 are handled by validateStatus above\r\n return {\r\n status: 0,\r\n headers: {},\r\n data: null,\r\n error: error.message || 'Request failed'\r\n };\r\n }\r\n};\r\n\r\n/**\r\n * Gets the normalized base URL from an input\r\n */\r\nexport const normalizeUrl = (input: string): string => {\r\n if (!input) return '';\r\n \r\n // Add protocol if missing\r\n let url = input;\r\n if (!url.startsWith('http://') && !url.startsWith('https://')) {\r\n url = 'https://' + url;\r\n }\r\n \r\n try {\r\n const parsed = new URL(url);\r\n return parsed.origin;\r\n } catch (e) {\r\n return url;\r\n }\r\n};\r\n\r\n/**\r\n * Extracts domain from URL\r\n */\r\nexport const extractDomain = (url: string): string => {\r\n try {\r\n // Remove protocol and www if present\r\n const parsed = new URL(normalizeUrl(url));\r\n let domain = parsed.hostname;\r\n \r\n if (domain.startsWith('www.')) {\r\n domain = domain.substring(4);\r\n }\r\n \r\n return domain;\r\n } catch (e) {\r\n return url.replace(/^(https?:\\/\\/)?(www\\.)?/, '').split('/')[0];\r\n }\r\n};\r\n\r\n/**\r\n * Helper to safely parse JSON\r\n */\r\nexport const safeJsonParse = (text: string): any => {\r\n try {\r\n return JSON.parse(text);\r\n } catch (e) {\r\n return null;\r\n }\r\n};\r\n\r\n/**\r\n * Creates common scanner input from various formats\r\n */\r\nexport const createScannerInput = (target: string | ScannerInput): ScannerInput => {\r\n if (typeof target === 'string') {\r\n return {\r\n target: normalizeUrl(target),\r\n timeout: 10000\r\n };\r\n }\r\n \r\n return {\r\n ...target,\r\n target: normalizeUrl(target.target)\r\n };\r\n};\r\n","import { FormDetectionResult, Scanner, ScannerInput } from '../types.js';\r\nimport { makeRequest, createScannerInput } from '../core/request.js';\r\nimport * as cheerio from 'cheerio';\r\n\r\n/**\r\n * Detect forms on a webpage and analyze their security\r\n */\r\nexport const detectForms: Scanner<FormDetectionResult> = async (\r\n input: ScannerInput\r\n) => {\r\n const startTime = Date.now();\r\n const normalizedInput = createScannerInput(input);\r\n \r\n try {\r\n let html: string;\r\n \r\n // If content is provided in options, use that instead of making a request\r\n if (normalizedInput.options?.html) {\r\n html = normalizedInput.options.html;\r\n } else {\r\n // Make a request to get the HTML content\r\n const response = await makeRequest(normalizedInput.target, {\r\n method: 'GET',\r\n timeout: normalizedInput.timeout,\r\n headers: normalizedInput.headers\r\n });\r\n \r\n if (response.error || !response.data) {\r\n return {\r\n status: 'failure',\r\n scanner: 'formDetection',\r\n error: response.error || 'Failed to retrieve HTML content',\r\n data: { forms: [], total: 0 },\r\n timeTaken: Date.now() - startTime\r\n };\r\n }\r\n \r\n html = typeof response.data === 'string' ? response.data : String(response.data);\r\n }\r\n \r\n // Load HTML into cheerio for DOM parsing\r\n const $ = cheerio.load(html);\r\n const forms = $('form');\r\n const formResults: FormDetectionResult['forms'] = []; forms.each((_i: number, formElement: any) => {\r\n const form = $(formElement);\r\n const action = form.attr('action') || '';\r\n const method = (form.attr('method') || 'get').toLowerCase();\r\n \r\n // Parse inputs\r\n const inputs: {\r\n name?: string;\r\n type: string;\r\n id?: string;\r\n required: boolean;\r\n autocomplete?: string;\r\n }[] = [];\r\n \r\n const formInputs = form.find('input, select, textarea, button[type=\"submit\"]');\r\n \r\n formInputs.each((_j: number, inputElement: any) => {\r\n const input = $(inputElement);\r\n const type = input.attr('type') || 'text';\r\n \r\n inputs.push({\r\n name: input.attr('name'),\r\n type,\r\n id: input.attr('id'),\r\n required: input.attr('required') !== undefined,\r\n autocomplete: input.attr('autocomplete')\r\n });\r\n });\r\n \r\n // Check if the form has a password field\r\n const hasPassword = inputs.some(input => input.type === 'password');\r\n \r\n // Check for CSRF protection tokens\r\n const hasCSRF = inputs.some(input => {\r\n const name = (input.name || '').toLowerCase();\r\n return name.includes('csrf') || \r\n name.includes('token') || \r\n name.includes('nonce') || \r\n name === '_token';\r\n });\r\n \r\n // Identify potential security issues\r\n const issues: {\r\n severity: 'high' | 'medium' | 'low' | 'info';\r\n description: string;\r\n }[] = [];\r\n \r\n // For login/auth forms\r\n if (hasPassword) {\r\n // Insecure method\r\n if (method !== 'post') {\r\n issues.push({\r\n severity: 'high',\r\n description: 'Login form uses insecure method (GET). Should use POST to prevent credentials in URL.'\r\n });\r\n }\r\n \r\n // Missing CSRF protection\r\n if (!hasCSRF) {\r\n issues.push({\r\n severity: 'high',\r\n description: 'Form appears to be missing CSRF protection token.'\r\n });\r\n }\r\n \r\n // Check for HTTPS action\r\n if (action && action.startsWith('http:')) {\r\n issues.push({\r\n severity: 'high',\r\n description: 'Form submits to insecure (HTTP) endpoint.'\r\n });\r\n }\r\n \r\n // Check for autocomplete on password fields\r\n const passwordInputs = inputs.filter(input => input.type === 'password');\r\n if (passwordInputs.some(input => input.autocomplete !== 'off' && input.autocomplete !== 'new-password')) {\r\n issues.push({\r\n severity: 'medium',\r\n description: 'Password field doesn\\'t have autocomplete=\"off\" or autocomplete=\"new-password\".'\r\n });\r\n }\r\n }\r\n \r\n formResults.push({\r\n action,\r\n method,\r\n inputs,\r\n hasPassword,\r\n hasCSRF,\r\n issues\r\n });\r\n });\r\n \r\n return {\r\n status: 'success',\r\n scanner: 'formDetection',\r\n data: {\r\n forms: formResults,\r\n total: formResults.length\r\n },\r\n timeTaken: Date.now() - startTime\r\n };\r\n } catch (error) {\r\n return {\r\n status: 'failure',\r\n scanner: 'formDetection',\r\n error: (error as Error).message || 'Unknown error',\r\n data: { forms: [], total: 0 },\r\n timeTaken: Date.now() - startTime\r\n };\r\n }\r\n};\r\n","import { SecurityHeadersResult, Scanner, ScannerInput } from '../types.js';\r\nimport { makeRequest, createScannerInput } from '../core/request.js';\r\n\r\n// Define required security headers and their descriptions\r\nconst SECURITY_HEADERS = {\r\n 'strict-transport-security': {\r\n description: 'HTTP Strict Transport Security (HSTS) enforces secure (HTTPS) connections',\r\n severity: 'high',\r\n },\r\n 'content-security-policy': {\r\n description: 'Content Security Policy prevents XSS and data injection attacks',\r\n severity: 'high',\r\n },\r\n 'x-content-type-options': {\r\n description: 'X-Content-Type-Options prevents MIME-sniffing',\r\n severity: 'medium',\r\n },\r\n 'x-frame-options': {\r\n description: 'X-Frame-Options protects against clickjacking',\r\n severity: 'medium',\r\n },\r\n 'x-xss-protection': {\r\n description: 'X-XSS-Protection enables the cross-site scripting filter',\r\n severity: 'medium',\r\n },\r\n 'referrer-policy': {\r\n description: 'Referrer Policy controls how much information is sent in the Referer header',\r\n severity: 'low',\r\n },\r\n 'permissions-policy': {\r\n description: 'Permissions Policy controls which browser features can be used',\r\n severity: 'low',\r\n },\r\n 'cross-origin-embedder-policy': {\r\n description: 'Cross-Origin Embedder Policy prevents loading cross-origin resources',\r\n severity: 'low',\r\n },\r\n 'cross-origin-opener-policy': {\r\n description: 'Cross-Origin Opener Policy prevents opening cross-origin windows',\r\n severity: 'low',\r\n },\r\n 'cross-origin-resource-policy': {\r\n description: 'Cross-Origin Resource Policy prevents cross-origin loading',\r\n severity: 'low',\r\n }\r\n} as const;\r\n\r\n/**\r\n * Scan security headers of a website\r\n */\r\nexport const scanSecurityHeaders: Scanner<SecurityHeadersResult> = async (\r\n input: ScannerInput\r\n) => {\r\n const startTime = Date.now();\r\n const normalizedInput = createScannerInput(input);\r\n \r\n try {\r\n // Make a HEAD request to get headers with minimal data transfer\r\n const response = await makeRequest(normalizedInput.target, {\r\n method: 'HEAD',\r\n timeout: normalizedInput.timeout,\r\n headers: normalizedInput.headers\r\n });\r\n \r\n if (response.error || !response.headers) {\r\n return {\r\n status: 'failure',\r\n scanner: 'securityHeaders',\r\n error: response.error || 'Failed to retrieve headers',\r\n data: {\r\n headers: {},\r\n missing: Object.keys(SECURITY_HEADERS),\r\n issues: [],\r\n score: 0\r\n },\r\n timeTaken: Date.now() - startTime\r\n };\r\n }\r\n \r\n // Convert all header names to lowercase for case-insensitive matching\r\n const headers: Record<string, string> = {};\r\n const headerNames = Object.keys(response.headers);\r\n \r\n headerNames.forEach(name => {\r\n headers[name.toLowerCase()] = response.headers[name] as string;\r\n });\r\n \r\n // Check which headers are missing\r\n const missing: string[] = [];\r\n const issues: {\r\n severity: 'high' | 'medium' | 'low' | 'info';\r\n header: string;\r\n description: string;\r\n }[] = [];\r\n \r\n Object.keys(SECURITY_HEADERS).forEach(header => {\r\n if (!headers[header]) {\r\n missing.push(header);\r\n issues.push({\r\n severity: SECURITY_HEADERS[header as keyof typeof SECURITY_HEADERS].severity as any,\r\n header,\r\n description: `Missing ${header} header. ${SECURITY_HEADERS[header as keyof typeof SECURITY_HEADERS].description}`\r\n });\r\n }\r\n });\r\n \r\n // Calculate a simple score (100 - deductions)\r\n const totalHeaders = Object.keys(SECURITY_HEADERS).length;\r\n const presentHeaders = totalHeaders - missing.length;\r\n const score = Math.round((presentHeaders / totalHeaders) * 100);\r\n \r\n // Check content of headers for common issues\r\n if (headers['strict-transport-security'] && \r\n !headers['strict-transport-security'].includes('max-age=')) {\r\n issues.push({\r\n severity: 'medium',\r\n header: 'strict-transport-security',\r\n description: 'HSTS header does not include max-age directive'\r\n });\r\n }\r\n \r\n if (headers['x-frame-options'] && \r\n !['DENY', 'SAMEORIGIN'].includes(headers['x-frame-options'].toUpperCase())) {\r\n issues.push({\r\n severity: 'medium',\r\n header: 'x-frame-options',\r\n description: 'X-Frame-Options should be set to DENY or SAMEORIGIN'\r\n });\r\n }\r\n \r\n return {\r\n status: 'success',\r\n scanner: 'securityHeaders',\r\n data: {\r\n headers,\r\n missing,\r\n issues,\r\n score\r\n },\r\n timeTaken: Date.now() - startTime\r\n };\r\n } catch (error) {\r\n return {\r\n status: 'failure',\r\n scanner: 'securityHeaders',\r\n error: (error as Error).message || 'Unknown error',\r\n data: {\r\n headers: {},\r\n missing: Object.keys(SECURITY_HEADERS),\r\n issues: [],\r\n score: 0\r\n },\r\n timeTaken: Date.now() - startTime\r\n };\r\n }\r\n};\r\n","/**\r\n * Frontend-specific exports for browser environments\r\n */\r\n\r\n// Import from modules that work in browsers\r\n// Note: All front-end functionality is exported from the main index\r\n// This file exists to allow for selective imports\r\n\r\nimport { detectForms } from '../modules/formDetection.js';\r\nimport { scanSecurityHeaders } from '../modules/securityHeaders.js';\r\n\r\n// Re-export for separate import paths\r\nexport { detectForms, scanSecurityHeaders };\r\n\r\n/**\r\n * Browser-based DOM form detection that works directly with DOM\r\n */\r\nexport const detectFormsInDOM = async () => {\r\n // This function only works in browser environments\r\n if (typeof document === 'undefined') {\r\n throw new Error('detectFormsInDOM can only be used in browser environments');\r\n }\r\n \r\n // Get the HTML from the current document\r\n const html = document.documentElement.outerHTML;\r\n \r\n // Use the regular form detection with the HTML\r\n return detectForms({\r\n target: window.location.href,\r\n options: { html }\r\n });\r\n};\r\n"]}