trufflehog-js
Version:
TypeScript wrapper for TruffleHog secret scanner
211 lines (181 loc) • 5.67 kB
text/typescript
/**
* Copyright (c) 2025 maloma7. All rights reserved.
* SPDX-License-Identifier: MIT
*/
import { ExitCode } from "./types.ts";
export class TruffleHogError extends Error {
public readonly exitCode: ExitCode;
public override readonly cause?: Error;
constructor(
message: string,
exitCode: ExitCode = ExitCode.ERROR,
cause?: Error,
) {
super(message);
this.name = "TruffleHogError";
this.exitCode = exitCode;
if (cause !== undefined) {
this.cause = cause;
}
}
}
export class BinaryNotFoundError extends TruffleHogError {
constructor(binaryPath: string, cause?: Error) {
super(
`TruffleHog binary not found at: ${binaryPath}`,
ExitCode.ERROR,
cause,
);
this.name = "BinaryNotFoundError";
}
}
export class BinaryDownloadError extends TruffleHogError {
constructor(url: string, cause?: Error) {
super(
`Failed to download TruffleHog binary from: ${url}`,
ExitCode.ERROR,
cause,
);
this.name = "BinaryDownloadError";
}
}
export class BinaryVerificationError extends TruffleHogError {
constructor(reason: string, cause?: Error) {
super(`Binary verification failed: ${reason}`, ExitCode.ERROR, cause);
this.name = "BinaryVerificationError";
}
}
export class BinaryExecutionError extends TruffleHogError {
constructor(command: string, stderr?: string, cause?: Error) {
const message = stderr
? `TruffleHog execution failed: ${stderr}`
: `TruffleHog execution failed: ${command}`;
super(message, ExitCode.ERROR, cause);
this.name = "BinaryExecutionError";
}
}
export class ScanTimeoutError extends TruffleHogError {
constructor(timeout: number, cause?: Error) {
super(`Scan timed out after ${timeout}ms`, ExitCode.ERROR, cause);
this.name = "ScanTimeoutError";
}
}
export class SecretsFoundError extends TruffleHogError {
public readonly secretCount: number;
constructor(secretCount: number) {
super(
`Found ${secretCount} secret${secretCount === 1 ? "" : "s"}`,
ExitCode.SECRETS_FOUND,
);
this.name = "SecretsFoundError";
this.secretCount = secretCount;
}
}
export class GitError extends TruffleHogError {
constructor(operation: string, cause?: Error) {
super(`Git operation failed: ${operation}`, ExitCode.ERROR, cause);
this.name = "GitError";
}
}
export class PlatformNotSupportedError extends TruffleHogError {
constructor(platform: string, arch: string, cause?: Error) {
super(`Platform not supported: ${platform}-${arch}`, ExitCode.ERROR, cause);
this.name = "PlatformNotSupportedError";
}
}
export class ConfigurationError extends TruffleHogError {
constructor(reason: string, cause?: Error) {
super(`Configuration error: ${reason}`, ExitCode.ERROR, cause);
this.name = "ConfigurationError";
}
}
export class NetworkError extends TruffleHogError {
constructor(operation: string, cause?: Error) {
super(`Network error during ${operation}`, ExitCode.ERROR, cause);
this.name = "NetworkError";
}
}
export class CacheError extends TruffleHogError {
constructor(operation: string, cause?: Error) {
super(`Cache error during ${operation}`, ExitCode.ERROR, cause);
this.name = "CacheError";
}
}
export class FileSystemError extends TruffleHogError {
constructor(operation: string, path: string, cause?: Error) {
super(
`Filesystem error during ${operation}: ${path}`,
ExitCode.ERROR,
cause,
);
this.name = "FileSystemError";
}
}
export function isRecoverableError(error: Error): boolean {
return (
error instanceof NetworkError ||
error instanceof BinaryDownloadError ||
error instanceof CacheError ||
(error instanceof BinaryExecutionError &&
!error.message.includes("not found"))
);
}
export function getExitCodeFromError(error: Error): ExitCode {
if (error instanceof TruffleHogError) {
return error.exitCode;
}
return ExitCode.ERROR;
}
export function formatErrorForCLI(
error: Error,
verbose: boolean = false,
): string {
if (error instanceof SecretsFoundError) {
return `🚨 Secrets detected! Please remove before committing. (${error.secretCount} found)`;
}
if (error instanceof BinaryNotFoundError) {
return verbose
? `❌ TruffleHog binary not found. Try running: bun install trufflehog-js\n${error.message}`
: "❌ TruffleHog binary not found. Run: bun install trufflehog-js";
}
if (error instanceof PlatformNotSupportedError) {
return verbose
? `❌ Platform not supported. Set TRUFFLEHOG_BINARY_PATH to use custom binary.\n${error.message}`
: "❌ Platform not supported. Set TRUFFLEHOG_BINARY_PATH for custom binary.";
}
if (error instanceof ScanTimeoutError) {
return verbose
? `⏱️ Scan timed out. Increase timeout or check large files.\n${error.message}`
: "⏱️ Scan timed out. Increase timeout or check large files.";
}
if (error instanceof BinaryExecutionError) {
return verbose
? `❌ TruffleHog execution failed:\n${error.message}`
: "❌ TruffleHog execution failed. Run with --verbose for details.";
}
if (error instanceof GitError) {
return verbose
? `❌ Git error (not in repository or no staged files?):\n${error.message}`
: "❌ Git error. Ensure you are in a git repository with staged files.";
}
if (verbose) {
return `❌ Unexpected error:\n${error.message}`;
}
return "❌ Unexpected error. Run with --verbose for details.";
}
export function handleProcessExit(
error: Error,
logger?: { error: (msg: string) => void },
): never {
const exitCode = getExitCodeFromError(error);
const message = formatErrorForCLI(
error,
Bun.env.TRUFFLEHOG_VERBOSE === "true",
);
if (logger) {
logger.error(message);
} else {
globalThis.console.error(message);
}
process.exit(exitCode);
}