import { fetchMalwareDatabase, fetchMalwareDatabaseVersion, } from "../api/aikido.js"; import { readDatabaseFromLocalCache, writeDatabaseToLocalCache, } from "../config/configFile.js"; import { ui } from "../environment/userInteraction.js"; /** * @typedef {Object} MalwareDatabase * @property {function(string, string): string} getPackageStatus * @property {function(string, string): boolean} isMalware */ /** @type {MalwareDatabase | null} */ let cachedMalwareDatabase = null; export async function openMalwareDatabase() { if (cachedMalwareDatabase) { return cachedMalwareDatabase; } const malwareDatabase = await getMalwareDatabase(); /** * @param {string} name * @param {string} version * @returns {string} */ function getPackageStatus(name, version) { const packageData = malwareDatabase.find( (pkg) => pkg.package_name === name && (pkg.version === version || pkg.version === "*") ); if (!packageData) { return MALWARE_STATUS_OK; } return packageData.reason; } // This implicitly caches the malware database // that's closed over by the getPackageStatus function cachedMalwareDatabase = { getPackageStatus, isMalware: (name, version) => { const status = getPackageStatus(name, version); return isMalwareStatus(status); }, }; return cachedMalwareDatabase; } /** * @returns {Promise} */ async function getMalwareDatabase() { const { malwareDatabase: cachedDatabase, version: cachedVersion } = readDatabaseFromLocalCache(); try { if (cachedDatabase) { const currentVersion = await fetchMalwareDatabaseVersion(); if (cachedVersion === currentVersion) { return cachedDatabase; } } const { malwareDatabase, version } = await fetchMalwareDatabase(); // @ts-expect-error version can be undefined writeDatabaseToLocalCache(malwareDatabase, version); return malwareDatabase; } catch (/** @type any */ error) { if (cachedDatabase) { ui.writeWarning( "Failed to fetch the latest malware database. Using cached version." ); return cachedDatabase; } throw error; } } /** * @param {string} status * * @returns {boolean} */ function isMalwareStatus(status) { let malwareStatus = status.toUpperCase(); return malwareStatus === MALWARE_STATUS_MALWARE; } export const MALWARE_STATUS_OK = "OK"; export const MALWARE_STATUS_MALWARE = "MALWARE"; export const MALWARE_STATUS_TELEMETRY = "TELEMETRY"; export const MALWARE_STATUS_PROTESTWARE = "PROTESTWARE";