UNPKG

@usehall/bot-gate

Version:

React/Vue/Svelte components for conditionally rendering content based on bot detection with IP validation

167 lines (132 loc) 4.99 kB
'use strict'; var fs = require('fs'); var path = require('path'); var url = require('url'); var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; function isServerSide() { return typeof window === 'undefined' && typeof process !== 'undefined' && process.versions && process.versions.node; } function warnIfClientSide() { if (!isServerSide()) { console.warn('bot-gate: This library is designed for server-side use only. Bot detection requires access to request headers and IP addresses that are not available in the browser.'); } } const __dirname$2 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))); let userAgentPatterns = null; function loadUserAgentPatterns() { if (!userAgentPatterns) { try { const dataPath = path.join(__dirname$2, '../../data/user-agents.json'); const data = fs.readFileSync(dataPath, 'utf8'); userAgentPatterns = JSON.parse(data); } catch (error) { console.warn('Failed to load user agent patterns:', error.message); userAgentPatterns = { bots: [] }; } } return userAgentPatterns; } function detectBot(userAgent) { if (!userAgent || typeof userAgent !== 'string') { return null; } const patterns = loadUserAgentPatterns(); const normalizedUA = userAgent.toLowerCase(); for (const bot of patterns.bots) { for (const pattern of bot.patterns) { const regex = new RegExp(pattern.toLowerCase(), 'i'); if (regex.test(normalizedUA)) { return { name: bot.name, type: bot.type, detected: true, pattern: pattern }; } } } return null; } const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))); const ipRangeCache = new Map(); function loadBotIpRanges(botName) { if (!ipRangeCache.has(botName)) { try { // Handle applebot-extended using the same IP ranges as applebot let actualBotName = botName.toLowerCase(); if (actualBotName === 'applebot-extended') { actualBotName = 'applebot'; } const dataPath = path.join(__dirname$1, `../../data/bots/${actualBotName}.json`); const data = fs.readFileSync(dataPath, 'utf8'); const rawData = JSON.parse(data); // Official API format: { creationTime, prefixes: [{ ipv4Prefix: "..." }] } const ranges = rawData.prefixes ? rawData.prefixes.map(prefix => prefix.ipv4Prefix || prefix.ipv6Prefix).filter(Boolean) : []; ipRangeCache.set(botName, { ranges }); } catch (error) { console.warn(`Failed to load IP ranges for ${botName}:`, error.message); ipRangeCache.set(botName, { ranges: [] }); } } return ipRangeCache.get(botName); } function ipToInt(ip) { return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0; } function cidrToRange(cidr) { const [ip, prefixLength] = cidr.split('/'); const ipInt = ipToInt(ip); const mask = (-1 << (32 - parseInt(prefixLength, 10))) >>> 0; const networkInt = ipInt & mask; const broadcastInt = networkInt | (~mask >>> 0); return { start: networkInt, end: broadcastInt }; } function isIpInRange(ip, range) { const ipInt = ipToInt(ip); if (range.includes('/')) { const { start, end } = cidrToRange(range); return ipInt >= start && ipInt <= end; } if (range.includes('-')) { const [startIp, endIp] = range.split('-'); const startInt = ipToInt(startIp.trim()); const endInt = ipToInt(endIp.trim()); return ipInt >= startInt && ipInt <= endInt; } return ipInt === ipToInt(range); } function isValidIpAddress(ip) { const ipv4Regex = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; return ipv4Regex.test(ip); } function validateBotByName(botName, ipAddress) { if (!botName || !ipAddress) { return false; } if (!isValidIpAddress(ipAddress)) { return false; } const botData = loadBotIpRanges(botName); if (!botData.ranges || botData.ranges.length === 0) { return false; } return botData.ranges.some(range => isIpInRange(ipAddress, range)); } function validateBot(userAgent, ipAddress) { if (!userAgent || !ipAddress) { return false; } const botInfo = detectBot(userAgent); if (!botInfo) { return false; } return validateBotByName(botInfo.name, ipAddress); } exports.validateBot = validateBot; exports.warnIfClientSide = warnIfClientSide; //# sourceMappingURL=index.cjs.map