UNPKG

trufflehog-js

Version:

TypeScript wrapper for TruffleHog secret scanner

211 lines (182 loc) 5.88 kB
/** * Copyright (c) 2025 maloma7. All rights reserved. * SPDX-License-Identifier: MIT */ import type { BinaryCacheInfo, Logger, PlatformInfo } from "./types.ts"; import { TRUFFLEHOG_VERSION } from "./types.ts"; import { CacheError } from "./errors.ts"; import { defaultLogger } from "./logger.ts"; import { getPlatformKey } from "./platform.ts"; export class BinaryCache { private readonly cacheDir: string; private readonly logger: Logger; constructor(cacheDir?: string, logger: Logger = defaultLogger) { this.cacheDir = cacheDir ?? this.getDefaultCacheDir(); this.logger = logger; } private getDefaultCacheDir(): string { const homeDir = Bun.env.HOME ?? Bun.env.USERPROFILE ?? "/tmp"; return `${homeDir}/.cache/trufflehog-js`; } async getBinaryPath(platformInfo: PlatformInfo): Promise<string | null> { try { const cacheInfo = await this.getCacheInfo(platformInfo); if (!cacheInfo) { return null; } const exists = await this.fileExists(cacheInfo.path); if (!exists) { this.logger.debug(`Cached binary not found at ${cacheInfo.path}`); await this.removeCacheInfo(platformInfo); return null; } if (!cacheInfo.verified) { this.logger.debug("Cached binary not verified, re-downloading"); await this.removeBinary(platformInfo); return null; } this.logger.debug(`Using cached binary: ${cacheInfo.path}`); return cacheInfo.path; } catch (_error) { this.logger.warn("Error accessing cache, re-downloading binary"); return null; } } async setCachedBinary( platformInfo: PlatformInfo, binaryPath: string, ): Promise<void> { try { const cacheInfo: BinaryCacheInfo = { version: TRUFFLEHOG_VERSION, platform: platformInfo.platform, arch: platformInfo.arch, path: binaryPath, checksum: "", // Will be calculated if needed downloadedAt: new Date(), verified: true, }; await this.saveCacheInfo(platformInfo, cacheInfo); this.logger.debug(`Cached binary info saved for ${getPlatformKey()}`); } catch (error) { throw new CacheError("saving cache info", error as Error); } } async removeBinary(platformInfo: PlatformInfo): Promise<void> { try { const cacheInfo = await this.getCacheInfo(platformInfo); if (cacheInfo) { await this.deleteFile(cacheInfo.path); await this.removeCacheInfo(platformInfo); this.logger.debug(`Removed cached binary for ${getPlatformKey()}`); } } catch (error) { throw new CacheError("removing cached binary", error as Error); } } async clearCache(): Promise<void> { try { await Bun.$`rm -rf ${this.cacheDir}`; this.logger.debug("Cache cleared"); } catch (error) { // Ignore errors when clearing cache (e.g., directory doesn't exist) this.logger.debug(`Cache clear failed: ${error}`); } } getCachedBinaryPath(platformInfo: PlatformInfo): string { const platformKey = `${platformInfo.platform}-${platformInfo.arch}`; const binaryName = platformInfo.platform === "win32" ? "trufflehog.exe" : "trufflehog"; return `${this.cacheDir}/${TRUFFLEHOG_VERSION}/${platformKey}/${binaryName}`; } private async getCacheInfo( platformInfo: PlatformInfo, ): Promise<BinaryCacheInfo | null> { try { const cacheFile = this.getCacheInfoPath(platformInfo); const exists = await this.fileExists(cacheFile); if (!exists) { return null; } const content = await Bun.file(cacheFile).text(); const cacheInfo = JSON.parse(content) as BinaryCacheInfo; // Verify cache is for current version if (cacheInfo.version !== TRUFFLEHOG_VERSION) { this.logger.debug( `Cache version mismatch: ${cacheInfo.version} !== ${TRUFFLEHOG_VERSION}`, ); await this.removeCacheInfo(platformInfo); return null; } return cacheInfo; } catch (error) { this.logger.debug( `Error reading cache info: ${(error as Error).message}`, ); return null; } } private async saveCacheInfo( platformInfo: PlatformInfo, cacheInfo: BinaryCacheInfo, ): Promise<void> { const cacheFile = this.getCacheInfoPath(platformInfo); const cacheDir = createPath(cacheFile).dir(); await Bun.$`mkdir -p ${cacheDir}`; const content = JSON.stringify(cacheInfo, null, 2); await Bun.write(cacheFile, content); } private async removeCacheInfo(platformInfo: PlatformInfo): Promise<void> { const cacheFile = this.getCacheInfoPath(platformInfo); await this.deleteFile(cacheFile); } private getCacheInfoPath(platformInfo: PlatformInfo): string { const platformKey = `${platformInfo.platform}-${platformInfo.arch}`; return `${this.cacheDir}/${TRUFFLEHOG_VERSION}/${platformKey}/cache-info.json`; } private async fileExists(path: string): Promise<boolean> { try { const file = Bun.file(path); return await file.exists(); } catch { return false; } } private async deleteFile(path: string): Promise<void> { try { await Bun.$`rm -f ${path}`; } catch { // Ignore errors when deleting files } } } export function createPath(filePath: string) { return { dir(): string { const lastSlash = filePath.lastIndexOf("/"); return lastSlash === -1 ? "." : filePath.substring(0, lastSlash); }, basename(): string { const lastSlash = filePath.lastIndexOf("/"); return lastSlash === -1 ? filePath : filePath.substring(lastSlash + 1); }, join(...paths: string[]): string { return [filePath, ...paths].join("/"); }, }; } export async function getBinaryFromCache( platformInfo: PlatformInfo, logger?: Logger, ): Promise<string | null> { const cache = new BinaryCache(undefined, logger); return await cache.getBinaryPath(platformInfo); } export async function setCachedBinary( platformInfo: PlatformInfo, binaryPath: string, logger?: Logger, ): Promise<void> { const cache = new BinaryCache(undefined, logger); await cache.setCachedBinary(platformInfo, binaryPath); }