diff --git a/config.js b/config.js new file mode 100644 index 0000000..382d287 --- /dev/null +++ b/config.js @@ -0,0 +1,20 @@ +// Main interval of each cycle. +// In production mode, it's 3 hours, and in development mode, it's 8 seconds. +const CYCLE_INTERVAL = process.env.NODE_ENV === 'production' ? 3 * 60 * 60 * 1000 : 8 * 1000; + +// The minimum time that must pass after reporting an IP address before it can be reported again. +// The required time is >= 15 minutes, according to AbuseIPDB API limits. +const REPORTED_IP_COOLDOWN_MS = 7 * 60 * 60 * 1000; // 7h + +// The maximum URI length that can be reported to AbuseIPDB. +// If Cloudflare returns a longer URI, the API request will fail. +const MAX_URL_LENGTH = 1000; + +// Additional delay after each successful IP report to avoid overloading the AbuseIPDB API. +const SUCCESS_COOLDOWN_MS = 2 * 1000; // 2s + +// Interval for refreshing your IP address. +// This ensures that WAF violations originating from your IP address are not reported to AbuseIPDB. +const IP_REFRESH_INTERVAL = 9 * 60 * 1000; // 9m + +module.exports = { CYCLE_INTERVAL, REPORTED_IP_COOLDOWN_MS, MAX_URL_LENGTH, SUCCESS_COOLDOWN_MS, IP_REFRESH_INTERVAL }; \ No newline at end of file diff --git a/index.js b/index.js index 9b9a569..bbca378 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ require('dotenv').config(); const { axios, moduleVersion } = require('./services/axios.js'); +const { CYCLE_INTERVAL, REPORTED_IP_COOLDOWN_MS, MAX_URL_LENGTH, SUCCESS_COOLDOWN_MS } = require('./config.js'); const PAYLOAD = require('./scripts/payload.js'); const generateComment = require('./scripts/generateComment.js'); const isImageRequest = require('./scripts/isImageRequest.js'); @@ -10,14 +11,6 @@ const formatDelay = require('./scripts/formatDelay.js'); const clientIp = require('./scripts/clientIp.js'); const log = require('./scripts/log.js'); -const MAIN_DELAY = process.env.NODE_ENV === 'production' - ? 3 * 60 * 60 * 1000 - : 8 * 1000; - -const REPORTED_IP_COOLDOWN_MS = 7 * 60 * 60 * 1000; -const COOLDOWN_MS = 2000; -const MAX_URL_LENGTH = 2000; - const fetchBlockedIPs = async () => { try { const { data, status } = await axios.post('https://api.cloudflare.com/client/v4/graphql', PAYLOAD(), { headers: headers.CLOUDFLARE }); @@ -56,7 +49,7 @@ const reportIP = async (event, url, country, cycleErrorCounts) => { if (event.clientIP === clientIp.address) { logToCSV(event.rayName, event.clientIP, url, 'Your IP address', country); - log('warn', `Your IP address (${event.clientIP}) was unexpectedly received from Cloudflare. URI: ${url}; Ignoring...`); + log('log', `Your IP address (${event.clientIP}) was unexpectedly received from Cloudflare. URI: ${url}; Ignoring...`); return false; } @@ -100,10 +93,10 @@ const reportIP = async (event, url, country, cycleErrorCounts) => { } } - log('info', 'Starting, please wait...'); + log('info', 'Loading data, please wait...'); await clientIp.fetchIPAddress(); - let cycleId = 1; + let cycleId = 1; while (true) { log('info', `===================== New Reporting Cycle (v${moduleVersion}) =====================`); @@ -113,6 +106,9 @@ const reportIP = async (event, url, country, cycleErrorCounts) => { continue; } + const userIp = clientIp.getAddress(); + if (!userIp) log('warn', `Your IP address is missing! Received: ${userIp}`); + const reportedIPs = readReportedIPs(); let cycleImageSkippedCount = 0, cycleProcessedCount = 0, cycleReportedCount = 0, cycleSkippedCount = 0; const cycleErrorCounts = { blocked: 0, noResponse: 0, otherErrors: 0 }; @@ -150,7 +146,7 @@ const reportIP = async (event, url, country, cycleErrorCounts) => { const wasReported = await reportIP(event, url, country, cycleErrorCounts); if (wasReported) { cycleReportedCount++; - await new Promise(resolve => setTimeout(resolve, COOLDOWN_MS)); + await new Promise(resolve => setTimeout(resolve, SUCCESS_COOLDOWN_MS)); } } @@ -164,8 +160,8 @@ const reportIP = async (event, url, country, cycleErrorCounts) => { log('info', `- Other errors: ${cycleErrorCounts.otherErrors}`); log('info', '==================== End of Reporting Cycle ===================='); - log('info', `Waiting ${formatDelay(MAIN_DELAY)}...`); + log('info', `Waiting ${formatDelay(CYCLE_INTERVAL)}...`); cycleId++; - await new Promise(resolve => setTimeout(resolve, MAIN_DELAY)); + await new Promise(resolve => setTimeout(resolve, CYCLE_INTERVAL)); } })(); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 79f0e72..487c324 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cf-waf-to-abuseipdb", - "version": "1.0.1", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cf-waf-to-abuseipdb", - "version": "1.0.1", + "version": "1.1.0", "license": "MIT", "dependencies": { "axios": "^1.7.7", diff --git a/package.json b/package.json index 3f0091b..9757295 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cf-waf-to-abuseipdb", - "version": "1.0.1", + "version": "1.1.0", "description": "A Node.js project that enables automatic reporting of incidents detected by Cloudflare WAF to the AbuseIPDB database.", "keywords": [ "abuseipdb", diff --git a/scripts/clientIp.js b/scripts/clientIp.js index daea44c..04d4463 100644 --- a/scripts/clientIp.js +++ b/scripts/clientIp.js @@ -1,8 +1,8 @@ const { axios } = require('../services/axios.js'); +const { IP_REFRESH_INTERVAL } = require('../config.js'); const log = require('./log.js'); let address = null; // Holds the IP address -const refreshInterval = 360000; // 6 minutes const fetchIPAddress = async () => { try { @@ -17,6 +17,7 @@ const fetchIPAddress = async () => { } }; -setInterval(fetchIPAddress, refreshInterval); -module.exports = { fetchIPAddress, address }; \ No newline at end of file +setInterval(fetchIPAddress, IP_REFRESH_INTERVAL); + +module.exports = { fetchIPAddress, getAddress: () => address }; \ No newline at end of file diff --git a/scripts/log.js b/scripts/log.js index 1d93fef..3dad375 100644 --- a/scripts/log.js +++ b/scripts/log.js @@ -4,7 +4,4 @@ const levels = { error: '[FAIL]' }; -module.exports = (level, message) => { - const timestamp = process.env.NODE_ENV === 'development' ? `${new Date().toISOString()}: ` : ''; - console[level](`${levels[level]} ${timestamp}${message}`); -}; \ No newline at end of file +module.exports = (level, msg) => console[level](`${levels[level]} ${msg}`); \ No newline at end of file