diff --git a/examples/basic_async_await.js b/examples/basic_async_await.js index df49e4b..07d2439 100755 --- a/examples/basic_async_await.js +++ b/examples/basic_async_await.js @@ -2,40 +2,64 @@ const axios = require('axios'); const fs = require('fs'); -const fakeVirusUrl = 'https://secure.eicar.org/eicar_com.txt'; +//switch between these two for happy/sad path testing +const fakeVirusUrl = 'https://www.eicar.org/download/eicar-com-2/?wpdmdl=8842&refresh=661ef9d576b211713306069'; +const samplePDFUrl = 'https://pdfobject.com/pdf/sample.pdf'; const tempDir = __dirname; const scanFile = `${tempDir}/tmp_file.txt`; -const config = { - removeInfected: true, - debugMode: false, - scanRecursively: false, - clamdscan: { - path: '/usr/bin/clamdscan', - // config_file: '/etc/clamd.d/daemon.conf' - }, - preference: 'clamdscan', -}; +/** + * This script demonstrates how to scan a file for viruses without requiring a permanent ClamAV daemon. + * It provides an example of configuring the 'clamscan' command for one-time file scans, including downloading + * a test virus file from the internet, scanning it with ClamAV, and handling the results. + * + * Configuration options for both 'clamscan' and 'clamdscan' are included to showcase different + * scanning setups, such as bypassing certain tests, defining timeouts, and setting file scan preferences. + * + * This example uses a test virus file from the EICAR website, which is safe and non-malicious, + * designed to verify that the virus scanning functionality works correctly. + */ // Initialize the clamscan module const NodeClam = require('../index'); // Offically: require('clamscan'); +const { type } = require('os'); (async () => { - const clamscan = await new NodeClam().init(config); + const clamscan = await new NodeClam().init({ + debugMode: true, + clamdscan: { + bypassTest: true, + host: 'localhost', + port: 3310, + // socket: '/var/run/clamd.scan/clamd.sock', + active: false, + }, + clamscan: { + path: '/usr/local/bin/clamscan', + scanPdf: false, + maxScanTime: 120000, + active: true, + }, + preference: 'clamdscan', + }); + let body; // Request a test file from the internet... try { - body = await axios.get(fakeVirusUrl); + body = await axios.get(fakeVirusUrl, { + responseType: 'arraybuffer', // Ensure the file is returned as binary data + }); + //console.log(JSON.stringify(body.data)) } catch (err) { - if (err.response) console.err(`${err.response.status}: Request Failed. `, err.response.data); + if (err.response) console.error(`${err.response.status}: Request Failed. `, err.response.data); else if (err.request) console.error('Error with Request: ', err.request); else console.error('Error: ', err.message); process.exit(1); } // Write the file to the filesystem - fs.writeFileSync(scanFile, body); + fs.writeFileSync(scanFile, body.data); // Scan the file try { @@ -55,7 +79,7 @@ const NodeClam = require('../index'); // Offically: require('clamscan'); } // Remove the file (for good measure) - if (fs.existsSync(scanFile)) fs.unlinkSync(scanFile); + //if (fs.existsSync(scanFile)) fs.unlinkSync(scanFile); process.exit(0); } catch (err) { console.error(`ERROR: ${err}`); diff --git a/examples/scanWithClamScan.js b/examples/scanWithClamScan.js new file mode 100644 index 0000000..803a236 --- /dev/null +++ b/examples/scanWithClamScan.js @@ -0,0 +1,89 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +const axios = require('axios'); +const fs = require('fs'); + +//switch between these two for happy/sad path testing +const fakeVirusUrl = 'https://www.eicar.org/download/eicar-com-2/?wpdmdl=8842&refresh=661ef9d576b211713306069'; +const samplePDFUrl = 'https://pdfobject.com/pdf/sample.pdf'; +const tempDir = __dirname; +const scanFile = `${tempDir}/tmp_file.txt`; + +/** + * This script demonstrates how to scan a file for viruses without requiring a permanent ClamAV daemon. + * It provides an example of configuring the 'clamscan' command for one-time file scans, including downloading + * a test virus file from the internet, scanning it with ClamAV, and handling the results. + * + * Configuration options for both 'clamscan' and 'clamdscan' are included to showcase different + * scanning setups, such as bypassing certain tests, defining timeouts, and setting file scan preferences. + * + * This example uses a test virus file from the EICAR website, which is safe and non-malicious, + * designed to verify that the virus scanning functionality works correctly. + */ + +// Initialize the clamscan module +const NodeClam = require('../index'); // Offically: require('clamscan'); +const { type } = require('os'); + +(async () => { + const clamscan = await new NodeClam().init({ + debugMode: true, + clamdscan: { + bypassTest: true, + host: 'localhost', + port: 3310, + // socket: '/var/run/clamd.scan/clamd.sock', + active: false, + }, + clamscan: { + path: '/usr/local/bin/clamscan', + scanPdf: false, + maxScanTime: 120000, + active: true, + }, + preference: 'clamscan', + }); + + let body; + + // Request a test file from the internet... + try { + body = await axios.get(fakeVirusUrl, { + responseType: 'arraybuffer', // Ensure the file is returned as binary data + }); + //console.log(JSON.stringify(body.data)) + } catch (err) { + if (err.response) console.error(`${err.response.status}: Request Failed. `, err.response.data); + else if (err.request) console.error('Error with Request: ', err.request); + else console.error('Error: ', err.message); + process.exit(1); + } + + // Write the file to the filesystem + fs.writeFileSync(scanFile, body.data); + + // Scan the file + try { + const { file, isInfected, viruses } = await clamscan.isInfected(scanFile); + + // If `isInfected` is TRUE, file is a virus! + if (isInfected === true) { + console.log( + `You've downloaded a virus (${viruses.join( + '' + )})! Don't worry, it's only a test one and is not malicious...` + ); + } else if (isInfected === null) { + console.log("Something didn't work right..."); + } else if (isInfected === false) { + console.log(`The file (${file}) you downloaded was just fine... Carry on...`); + } + + // Remove the file (for good measure) + //if (fs.existsSync(scanFile)) fs.unlinkSync(scanFile); + process.exit(0); + } catch (err) { + console.error(`ERROR: ${err}`); + console.trace(err.stack); + process.exit(1); + } +})(); diff --git a/index.js b/index.js index d5afd4b..f2f86d4 100755 --- a/index.js +++ b/index.js @@ -452,12 +452,244 @@ class NodeClam { flagsArray.push('--scan-archive=no'); } - // Recursive scanning (flag is specific, feature is not) + // Recursive scanning if (settings.scanRecursively === true) { flagsArray.push('-r'); } else { flagsArray.push('--recursive=no'); } + + // Scan other options: these flags can be added based on settings + if (settings.clamscan.officialDbOnly !== undefined) { + flagsArray.push(`--official-db-only=${settings.clamscan.officialDbOnly ? 'yes' : 'no'}`); + } + + if (settings.clamscan.failIfCvdOlderThan) { + flagsArray.push(`--fail-if-cvd-older-than=${settings.clamscan.failIfCvdOlderThan}`); + } + + if (settings.clamscan.allmatch !== undefined) { + flagsArray.push(`--allmatch=${settings.clamscan.allmatch ? 'yes' : 'no'}`); + } + + if (settings.clamscan.crossFs !== undefined) { + flagsArray.push(`--cross-fs=${settings.clamscan.crossFs ? 'yes' : 'no'}`); + } + + if (settings.clamscan.followDirSymlinks !== undefined) { + flagsArray.push(`--follow-dir-symlinks=${settings.clamscan.followDirSymlinks}`); + } + + if (settings.clamscan.followFileSymlinks !== undefined) { + flagsArray.push(`--follow-file-symlinks=${settings.clamscan.followFileSymlinks}`); + } + + if (settings.clamscan.excludeRegex) { + flagsArray.push(`--exclude=${settings.clamscan.excludeRegex}`); + } + + if (settings.clamscan.excludeDirRegex) { + flagsArray.push(`--exclude-dir=${settings.clamscan.excludeDirRegex}`); + } + + if (settings.clamscan.includeRegex) { + flagsArray.push(`--include=${settings.clamscan.includeRegex}`); + } + + if (settings.clamscan.includeDirRegex) { + flagsArray.push(`--include-dir=${settings.clamscan.includeDirRegex}`); + } + + if (settings.clamscan.bytecode !== undefined) { + flagsArray.push(`--bytecode=${settings.clamscan.bytecode ? 'yes' : 'no'}`); + } + + if (settings.clamscan.bytecodeUnsigned !== undefined) { + flagsArray.push(`--bytecode-unsigned=${settings.clamscan.bytecodeUnsigned ? 'yes' : 'no'}`); + } + + if (settings.clamscan.bytecodeTimeout) { + flagsArray.push(`--bytecode-timeout=${settings.clamscan.bytecodeTimeout}`); + } + + if (settings.clamscan.statistics) { + flagsArray.push(`--statistics=${settings.clamscan.statistics}`); + } + + if (settings.clamscan.detectPua !== undefined) { + flagsArray.push(`--detect-pua=${settings.clamscan.detectPua ? 'yes' : 'no'}`); + } + + if (settings.clamscan.excludePua) { + flagsArray.push(`--exclude-pua=${settings.clamscan.excludePua}`); + } + + if (settings.clamscan.includePua) { + flagsArray.push(`--include-pua=${settings.clamscan.includePua}`); + } + + if (settings.clamscan.detectStructured !== undefined) { + flagsArray.push(`--detect-structured=${settings.clamscan.detectStructured ? 'yes' : 'no'}`); + } + + if (settings.clamscan.structuredSsnFormat) { + flagsArray.push(`--structured-ssn-format=${settings.clamscan.structuredSsnFormat}`); + } + + if (settings.clamscan.structuredSsnCount) { + flagsArray.push(`--structured-ssn-count=${settings.clamscan.structuredSsnCount}`); + } + + if (settings.clamscan.structuredCcCount) { + flagsArray.push(`--structured-cc-count=${settings.clamscan.structuredCcCount}`); + } + + if (settings.clamscan.structuredCcMode) { + flagsArray.push(`--structured-cc-mode=${settings.clamscan.structuredCcMode}`); + } + + if (settings.clamscan.scanMail !== undefined) { + flagsArray.push(`--scan-mail=${settings.clamscan.scanMail ? 'yes' : 'no'}`); + } + + if (settings.clamscan.phishingSigs !== undefined) { + flagsArray.push(`--phishing-sigs=${settings.clamscan.phishingSigs ? 'yes' : 'no'}`); + } + + if (settings.clamscan.phishingScanUrls !== undefined) { + flagsArray.push(`--phishing-scan-urls=${settings.clamscan.phishingScanUrls ? 'yes' : 'no'}`); + } + + if (settings.clamscan.heuristicAlerts !== undefined) { + flagsArray.push(`--heuristic-alerts=${settings.clamscan.heuristicAlerts ? 'yes' : 'no'}`); + } + + if (settings.clamscan.heuristicScanPrecedence !== undefined) { + flagsArray.push( + `--heuristic-scan-precedence=${settings.clamscan.heuristicScanPrecedence ? 'yes' : 'no'}` + ); + } + + if (settings.clamscan.normalize !== undefined) { + flagsArray.push(`--normalize=${settings.clamscan.normalize ? 'yes' : 'no'}`); + } + + if (settings.clamscan.scanPe !== undefined) { + flagsArray.push(`--scan-pe=${settings.clamscan.scanPe ? 'yes' : 'no'}`); + } + + if (settings.clamscan.scanElf !== undefined) { + flagsArray.push(`--scan-elf=${settings.clamscan.scanElf ? 'yes' : 'no'}`); + } + + if (settings.clamscan.scanOle2 !== undefined) { + flagsArray.push(`--scan-ole2=${settings.clamscan.scanOle2 ? 'yes' : 'no'}`); + } + + if (settings.clamscan.scanPdf !== undefined) { + flagsArray.push(`--scan-pdf=${settings.clamscan.scanPdf ? 'yes' : 'no'}`); + } + + if (settings.clamscan.scanSwf !== undefined) { + flagsArray.push(`--scan-swf=${settings.clamscan.scanSwf ? 'yes' : 'no'}`); + } + + if (settings.clamscan.scanHtml !== undefined) { + flagsArray.push(`--scan-html=${settings.clamscan.scanHtml ? 'yes' : 'no'}`); + } + + if (settings.clamscan.scanXmldocs !== undefined) { + flagsArray.push(`--scan-xmldocs=${settings.clamscan.scanXmldocs ? 'yes' : 'no'}`); + } + + if (settings.clamscan.scanHwp3 !== undefined) { + flagsArray.push(`--scan-hwp3=${settings.clamscan.scanHwp3 ? 'yes' : 'no'}`); + } + + if (settings.clamscan.scanOnenote !== undefined) { + flagsArray.push(`--scan-onenote=${settings.clamscan.scanOnenote ? 'yes' : 'no'}`); + } + + if (settings.clamscan.alertBroken !== undefined) { + flagsArray.push(`--alert-broken=${settings.clamscan.alertBroken ? 'yes' : 'no'}`); + } + + if (settings.clamscan.alertEncrypted !== undefined) { + flagsArray.push(`--alert-encrypted=${settings.clamscan.alertEncrypted ? 'yes' : 'no'}`); + } + + // Add the missing flags for max options + if (settings.clamscan.maxScanTime) { + flagsArray.push(`--max-scantime=${settings.clamscan.maxScanTime}`); + } + + if (settings.clamscan.maxFileSize) { + flagsArray.push(`--max-filesize=${settings.clamscan.maxFileSize}`); + } + + if (settings.clamscan.maxScanSize) { + flagsArray.push(`--max-scansize=${settings.clamscan.maxScanSize}`); + } + + if (settings.clamscan.maxFiles) { + flagsArray.push(`--max-files=${settings.clamscan.maxFiles}`); + } + + if (settings.clamscan.maxRecursion) { + flagsArray.push(`--max-recursion=${settings.clamscan.maxRecursion}`); + } + + if (settings.clamscan.maxDirRecursion) { + flagsArray.push(`--max-dir-recursion=${settings.clamscan.maxDirRecursion}`); + } + + if (settings.clamscan.maxEmbeddedPe) { + flagsArray.push(`--max-embeddedpe=${settings.clamscan.maxEmbeddedPe}`); + } + + if (settings.clamscan.maxHtmlnormalize) { + flagsArray.push(`--max-htmlnormalize=${settings.clamscan.maxHtmlnormalize}`); + } + + if (settings.clamscan.maxHtmlnotags) { + flagsArray.push(`--max-htmlnotags=${settings.clamscan.maxHtmlnotags}`); + } + + if (settings.clamscan.maxScriptnormalize) { + flagsArray.push(`--max-scriptnormalize=${settings.clamscan.maxScriptnormalize}`); + } + + if (settings.clamscan.maxZipTypercg) { + flagsArray.push(`--max-ziptypercg=${settings.clamscan.maxZipTypercg}`); + } + + if (settings.clamscan.maxPartitions) { + flagsArray.push(`--max-partitions=${settings.clamscan.maxPartitions}`); + } + + if (settings.clamscan.maxIconspe) { + flagsArray.push(`--max-iconspe=${settings.clamscan.maxIconspe}`); + } + + if (settings.clamscan.maxRechwp3) { + flagsArray.push(`--max-rechwp3=${settings.clamscan.maxRechwp3}`); + } + + if (settings.clamscan.pcreMatchLimit) { + flagsArray.push(`--pcre-match-limit=${settings.clamscan.pcreMatchLimit}`); + } + + if (settings.clamscan.pcreRecMatchLimit) { + flagsArray.push(`--pcre-recmatch-limit=${settings.clamscan.pcreRecMatchLimit}`); + } + + if (settings.clamscan.pcreMaxFilesize) { + flagsArray.push(`--pcre-max-filesize=${settings.clamscan.pcreMaxFilesize}`); + } + + if (settings.clamscan.disableCache !== undefined) { + flagsArray.push(`--disable-cache=${settings.clamscan.disableCache ? 'yes' : 'no'}`); + } } // Flags specific to clamdscan @@ -507,7 +739,17 @@ class NodeClam { if ('fileList' in settings && settings.fileList && typeof settings.fileList === 'string') flagsArray.push(`--file-list=${settings.fileList}`); - // Build the String + // Scan files specified via --file-list + if (settings.filesFromList) { + flagsArray.push('--file-from-list'); + } + + // Add timeout for scanning + if (settings.scanTimeout) { + flagsArray.push(`--timeout=${settings.scanTimeout}`); + } + + // Return the final flags return flagsArray; }