From 041d24de75f796b5ca758470a97e8f5f148d70b0 Mon Sep 17 00:00:00 2001 From: jzunigax2 Date: Mon, 3 Mar 2025 07:51:49 -0600 Subject: [PATCH] chore: add TypeScript support and type definitions --- package.json | 4 +- tsconfig.json | 21 ++++ types/index.d.ts | 244 +++++++++++++++++++++++++++++++++++++++++++++++ yarn.lock | 12 +++ 4 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 tsconfig.json create mode 100644 types/index.d.ts diff --git a/package.json b/package.json index ba9673d..d58c3ff 100755 --- a/package.json +++ b/package.json @@ -1,9 +1,10 @@ { "name": "@internxt/scan", - "version": "1.0.2", + "version": "1.0.3", "author": "Kyle Farris (https://infotechinc.com)", "description": "Use Node JS to scan files on your server with ClamAV's clamscan/clamdscan binary or via TCP to a remote server or local UNIX Domain socket. This is especially useful for scanning uploaded files provided by un-trusted sources.", "main": "index.js", + "types": "types/index.d.ts", "contributors": [ "dietervds", "nicolaspeixoto", @@ -37,6 +38,7 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.19.1", + "@types/node": "^22.13.8", "axios": "^1.2.0", "chai": "^4.4.1", "chai-as-promised": "^7.1.1", diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..cde6585 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "allowJs": true, + "declaration": true, + "emitDeclarationOnly": false, + "outDir": "./dist", + "strict": false, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": [ + "index.js", + "lib/**/*.js" + ], + "exclude": [ + "node_modules" + ] + } \ No newline at end of file diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 0000000..ebec50b --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,244 @@ +/// + +import { Socket } from 'dgram'; +import { Readable } from 'stream'; + +declare namespace NodeClam { + interface ClamScanSettings { + /** If true, removes infected files */ + removeInfected?: boolean; + /** False: Don't quarantine, Path: Moves files to this place. */ + quarantineInfected?: boolean | string; + /** Path to a writeable log file to write scan results into */ + scanLog?: string | null; + /** Whether to log info/debug/error msg to the console */ + debugMode?: boolean; + /** path to file containing list of files to scan (for scanFiles method) */ + fileList?: string | null; + /** If true, deep scan folders recursively */ + scanRecursively?: boolean; + clamscan?: { + /** Path to clamscan binary on your server */ + path?: string; + /** Path to a custom virus definition database */ + db?: string | null; + /** If true, scan archives (ex. zip, rar, tar, dmg, iso, etc...) */ + scanArchives?: boolean; + /** If true, this module will consider using the clamscan binary */ + active?: boolean; + }; + clamdscan?: { + /** Socket file for connecting via TCP */ + socket?: string | boolean; + /** IP of host to connect to TCP interface */ + host?: string | boolean; + /** Port of host to use when connecting via TCP interface */ + port?: number | boolean; + /** Timeout for scanning files */ + timeout?: number; + /** Do not fail over to binary-method of scanning */ + localFallback?: boolean; + /** Path to the clamdscan binary on your server */ + path?: string; + /** Specify config file if it's in an unusual place */ + configFile?: string | null; + /** Scan using all available cores! Yay! */ + multiscan?: boolean; + /** If true, will re-load the DB on every call (slow) */ + reloadDb?: boolean; + /** If true, this module will consider using the clamdscan binary */ + active?: boolean; + /** Check to see if socket is available when applicable */ + bypassTest?: boolean; + /** If true, connect to a TLS-Termination proxy in front of ClamAV */ + tls?: boolean; + }; + /** If clamdscan is found and active, it will be used by default */ + preference?: any; + } + + interface ScanResult { + file: string; + isInfected: boolean; + viruses: string[]; + resultString?: string; + timeout?: boolean; + } + + interface ScanFileResult extends ScanResult { + viruses: string[]; + file: string; + isInfected: boolean; + } + + interface FileScanResult { + [filePath: string]: { + isInfected: boolean; + viruses: string[]; + }; + } + + interface ScanDirResult { + path: string; + isInfected: boolean; + goodFiles: string[]; + goodFileCount: number; + infectedFiles: FileScanResult; + infectedFileCount: number; + error: Error | null; + errorFiles: string[]; + errorFileCount: number; + files: FileScanResult; + fileCount: number; + } + + interface ScanBufferResult extends ScanResult { + isInfected: boolean; + viruses: string[]; + } + + interface ScanStreamResult extends ScanResult { + isInfected: boolean; + viruses: string[]; + } + + // Create a union type that combines ClamScanner and NodeClam functionality + interface ClamScanner { + scanFile: (filePath: string) => Promise; + scanDir: ( + directoryPath: string, + endCallback?: (err: Error | null, goodFiles?: string[], badFiles?: string[], viruses?: string[]) => void, + fileCallback?: (err: Error | null, file?: string, isInfected?: boolean, viruses?: string[], scannedCount?: number, progress?: string) => void + ) => Promise; + scanFiles: ( + files: string[] | string, + endCallback?: (err: Error | null, goodFiles?: string[], badFiles?: string[], errors?: object, viruses?: string[]) => void, + fileCallback?: (err: Error | null, file?: string, isInfected?: boolean, viruses?: string[], scannedCount?: number, progress?: string) => void + ) => Promise<{goodFiles: string[], badFiles: string[], errors: object, viruses: string[]}>; + scanStream: (stream: Readable) => Promise; + getVersion: (cb?: (err: Error | null, version: string) => void) => Promise; + isInfected: (file: string) => Promise; + passthrough: () => ClamScanner; + /** + * Closes all active socket connections tracked by the scanner + * @returns A promise that resolves when all sockets have been closed + */ + closeAllSockets: () => Promise; + + /** + * Quick check to see if the remote/local socket is working + * @param cb - Optional callback function to handle the result + * @returns A Promise that resolves with a Socket client instance + */ + ping: (cb?: (err: Error | null, client: Socket | null) => void) => Promise; + + /** + * Initialize the NodeClam instance + * @param settings - Configuration settings + * @param cb - Optional callback function + * @returns The initialized NodeClam instance + */ + init: (settings?: ClamScanSettings, cb?: (err: Error | null, instance: any) => void) => Promise; + + /** + * Reset and reinitialize the NodeClam instance with new options + * @param options - New configuration settings + * @param cb - Optional callback function + * @returns The reset NodeClam instance + */ + reset: (options?: ClamScanSettings, cb?: (err: Error | null, instance: any) => void) => Promise; + } +} + +declare class NodeClam { + /** + * Initialize the NodeClam instance + * @param settings - Configuration settings + * @param cb - Optional callback function + * @returns The initialized NodeClam instance with all scanning capabilities + */ + init(settings?: NodeClam.ClamScanSettings, cb?: (err: Error | null, instance: NodeClam) => void): Promise; + + /** + * Reset and reinitialize the NodeClam instance with new options + * @param options - New configuration settings + * @param cb - Optional callback function + * @returns The reset NodeClam instance + */ + reset(options?: NodeClam.ClamScanSettings, cb?: (err: Error | null, instance: NodeClam) => void): Promise; + + /** + * Scan a file for viruses + * @param filePath - The path to the file to be scanned + * @returns Scan results + */ + scanFile(filePath: string): Promise; + + /** + * Scan a directory for viruses + * @param directoryPath - The path to the directory to be scanned + * @param endCallback - Optional callback function for when scanning is complete + * @param fileCallback - Optional callback function for each scanned file + * @returns Scan results + */ + scanDir( + directoryPath: string, + endCallback?: (err: Error | null, goodFiles?: string[], badFiles?: string[], viruses?: string[]) => void, + fileCallback?: (err: Error | null, file?: string, isInfected?: boolean, viruses?: string[], scannedCount?: number, progress?: string) => void + ): Promise; + + /** + * Scan multiple files for viruses + * @param files - Array of file paths to scan + * @param endCallback - Optional callback function for when scanning is complete + * @param fileCallback - Optional callback function for each scanned file + * @returns Scan results + */ + scanFiles( + files: string[] | string, + endCallback?: (err: Error | null, goodFiles?: string[], badFiles?: string[], errors?: object, viruses?: string[]) => void, + fileCallback?: (err: Error | null, file?: string, isInfected?: boolean, viruses?: string[], scannedCount?: number, progress?: string) => void + ): Promise<{goodFiles: string[], badFiles: string[], errors: object, viruses: string[]}>; + + /** + * Scan a stream for viruses + * @param stream - The readable stream to be scanned + * @returns Scan results + */ + scanStream(stream: Readable): Promise; + + /** + * Get the version information + * @param cb - Optional callback function + * @returns Version information + */ + getVersion(cb?: (err: Error | null, version: string) => void): Promise; + + /** + * Check if a file is infected + * @param file - Path to the file to check + * @returns Scan results + */ + isInfected(file: string): Promise; + + /** + * Quick check to see if the remote/local socket is working + * @param cb - Optional callback function + * @returns A Promise that resolves with a Socket client instance + */ + ping(cb?: (err: Error | null, client: Socket | null) => void): Promise; + + /** + * Passthrough method to directly access the raw scanner + * @returns The raw scanner object + */ + passthrough(): NodeClam.ClamScanner; + + /** + * Closes all active socket connections tracked by the scanner + * @returns A promise that resolves when all sockets have been closed + */ + closeAllSockets(): Promise; +} + +export = NodeClam; diff --git a/yarn.lock b/yarn.lock index 3ad1487..8f7444e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -164,6 +164,13 @@ resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-2.0.0.tgz#d43878b5b20222682163ae6f897b20447233bdfd" integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== +"@types/node@^22.13.8": + version "22.13.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.8.tgz#57e2450295b33a6518d6fd4f65f47236d3e41d8d" + integrity sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ== + dependencies: + undici-types "~6.20.0" + "@ungap/structured-clone@^1.2.0": version "1.2.1" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.1.tgz#28fa185f67daaf7b7a1a8c1d445132c5d979f8bd" @@ -2543,6 +2550,11 @@ underscore@~1.13.2: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.7.tgz#970e33963af9a7dda228f17ebe8399e5fbe63a10" integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"