@evalguardai/cli 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +87 -0
  2. package/bin/evalguard.js +2 -0
  3. package/dist/commands/compare.d.ts +6 -0
  4. package/dist/commands/compare.d.ts.map +1 -0
  5. package/dist/commands/compare.js +109 -0
  6. package/dist/commands/compare.js.map +1 -0
  7. package/dist/commands/compliance-check.d.ts +18 -0
  8. package/dist/commands/compliance-check.d.ts.map +1 -0
  9. package/dist/commands/compliance-check.js +474 -0
  10. package/dist/commands/compliance-check.js.map +1 -0
  11. package/dist/commands/debug.d.ts +6 -0
  12. package/dist/commands/debug.d.ts.map +1 -0
  13. package/dist/commands/debug.js +151 -0
  14. package/dist/commands/debug.js.map +1 -0
  15. package/dist/commands/delete.d.ts +6 -0
  16. package/dist/commands/delete.d.ts.map +1 -0
  17. package/dist/commands/delete.js +105 -0
  18. package/dist/commands/delete.js.map +1 -0
  19. package/dist/commands/eval-local.d.ts +7 -0
  20. package/dist/commands/eval-local.d.ts.map +1 -0
  21. package/dist/commands/eval-local.js +376 -0
  22. package/dist/commands/eval-local.js.map +1 -0
  23. package/dist/commands/export.d.ts +6 -0
  24. package/dist/commands/export.d.ts.map +1 -0
  25. package/dist/commands/export.js +135 -0
  26. package/dist/commands/export.js.map +1 -0
  27. package/dist/commands/firewall.d.ts +6 -0
  28. package/dist/commands/firewall.d.ts.map +1 -0
  29. package/dist/commands/firewall.js +56 -0
  30. package/dist/commands/firewall.js.map +1 -0
  31. package/dist/commands/gate.d.ts +14 -0
  32. package/dist/commands/gate.d.ts.map +1 -0
  33. package/dist/commands/gate.js +232 -0
  34. package/dist/commands/gate.js.map +1 -0
  35. package/dist/commands/generate.d.ts +7 -0
  36. package/dist/commands/generate.d.ts.map +1 -0
  37. package/dist/commands/generate.js +182 -0
  38. package/dist/commands/generate.js.map +1 -0
  39. package/dist/commands/history.d.ts +7 -0
  40. package/dist/commands/history.d.ts.map +1 -0
  41. package/dist/commands/history.js +59 -0
  42. package/dist/commands/history.js.map +1 -0
  43. package/dist/commands/import-promptfoo.d.ts +7 -0
  44. package/dist/commands/import-promptfoo.d.ts.map +1 -0
  45. package/dist/commands/import-promptfoo.js +218 -0
  46. package/dist/commands/import-promptfoo.js.map +1 -0
  47. package/dist/commands/index.d.ts +21 -0
  48. package/dist/commands/index.d.ts.map +1 -0
  49. package/dist/commands/index.js +21 -0
  50. package/dist/commands/index.js.map +1 -0
  51. package/dist/commands/init.d.ts +7 -0
  52. package/dist/commands/init.d.ts.map +1 -0
  53. package/dist/commands/init.js +509 -0
  54. package/dist/commands/init.js.map +1 -0
  55. package/dist/commands/list.d.ts +10 -0
  56. package/dist/commands/list.d.ts.map +1 -0
  57. package/dist/commands/list.js +165 -0
  58. package/dist/commands/list.js.map +1 -0
  59. package/dist/commands/logs.d.ts +6 -0
  60. package/dist/commands/logs.d.ts.map +1 -0
  61. package/dist/commands/logs.js +153 -0
  62. package/dist/commands/logs.js.map +1 -0
  63. package/dist/commands/model-scan.d.ts +7 -0
  64. package/dist/commands/model-scan.d.ts.map +1 -0
  65. package/dist/commands/model-scan.js +276 -0
  66. package/dist/commands/model-scan.js.map +1 -0
  67. package/dist/commands/retry.d.ts +6 -0
  68. package/dist/commands/retry.d.ts.map +1 -0
  69. package/dist/commands/retry.js +83 -0
  70. package/dist/commands/retry.js.map +1 -0
  71. package/dist/commands/scan-local.d.ts +6 -0
  72. package/dist/commands/scan-local.d.ts.map +1 -0
  73. package/dist/commands/scan-local.js +138 -0
  74. package/dist/commands/scan-local.js.map +1 -0
  75. package/dist/commands/share.d.ts +6 -0
  76. package/dist/commands/share.d.ts.map +1 -0
  77. package/dist/commands/share.js +74 -0
  78. package/dist/commands/share.js.map +1 -0
  79. package/dist/commands/store.d.ts +23 -0
  80. package/dist/commands/store.d.ts.map +1 -0
  81. package/dist/commands/store.js +54 -0
  82. package/dist/commands/store.js.map +1 -0
  83. package/dist/commands/validate.d.ts +6 -0
  84. package/dist/commands/validate.d.ts.map +1 -0
  85. package/dist/commands/validate.js +171 -0
  86. package/dist/commands/validate.js.map +1 -0
  87. package/dist/commands/watch.d.ts +6 -0
  88. package/dist/commands/watch.d.ts.map +1 -0
  89. package/dist/commands/watch.js +92 -0
  90. package/dist/commands/watch.js.map +1 -0
  91. package/dist/index.d.ts +3 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +342 -0
  94. package/dist/index.js.map +1 -0
  95. package/package.json +73 -0
@@ -0,0 +1,165 @@
1
+ import chalk from "chalk";
2
+ export function registerList(program) {
3
+ const list = program
4
+ .command("list")
5
+ .description("List available components");
6
+ // ─── list scorers ───
7
+ list
8
+ .command("scorers")
9
+ .description("List all built-in scorers")
10
+ .option("--json", "Output as JSON", false)
11
+ .action(async (opts) => {
12
+ const { BUILT_IN_SCORERS } = await import("@evalguard/core");
13
+ const scorers = Object.entries(BUILT_IN_SCORERS).map(([key, scorer]) => ({
14
+ name: key,
15
+ description: scorer.description ?? scorer.name,
16
+ }));
17
+ if (opts.json) {
18
+ console.log(JSON.stringify(scorers, null, 2));
19
+ return;
20
+ }
21
+ console.log();
22
+ console.log(chalk.bold(` Available Scorers (${scorers.length})`));
23
+ console.log();
24
+ for (const s of scorers) {
25
+ console.log(` ${chalk.cyan(s.name.padEnd(25))} ${chalk.dim(s.description)}`);
26
+ }
27
+ console.log();
28
+ });
29
+ // ─── list plugins ───
30
+ list
31
+ .command("plugins")
32
+ .description("List all red team attack plugins")
33
+ .option("--json", "Output as JSON", false)
34
+ .action(async (opts) => {
35
+ const { ALL_PLUGINS } = await import("@evalguard/core");
36
+ const plugins = ALL_PLUGINS.map((p) => ({
37
+ id: p.id,
38
+ name: p.name,
39
+ description: p.description,
40
+ severity: p.severity,
41
+ attackTypes: p.attackTypes,
42
+ }));
43
+ if (opts.json) {
44
+ console.log(JSON.stringify(plugins, null, 2));
45
+ return;
46
+ }
47
+ console.log();
48
+ console.log(chalk.bold(` Attack Plugins (${plugins.length})`));
49
+ console.log();
50
+ for (const p of plugins) {
51
+ const sevColor = p.severity === "critical" ? chalk.red : p.severity === "high" ? chalk.yellow : chalk.dim;
52
+ console.log(` ${chalk.cyan(p.id.padEnd(25))} ${sevColor(p.severity.padEnd(10))} ${chalk.dim(p.description)}`);
53
+ }
54
+ console.log();
55
+ });
56
+ // ─── list strategies ───
57
+ list
58
+ .command("strategies")
59
+ .description("List all encoding/obfuscation strategies")
60
+ .option("--json", "Output as JSON", false)
61
+ .action(async (opts) => {
62
+ const { ALL_STRATEGIES } = await import("@evalguard/core");
63
+ const strategies = ALL_STRATEGIES.map((s) => ({
64
+ id: s.id,
65
+ name: s.name,
66
+ description: s.description,
67
+ }));
68
+ if (opts.json) {
69
+ console.log(JSON.stringify(strategies, null, 2));
70
+ return;
71
+ }
72
+ console.log();
73
+ console.log(chalk.bold(` Encoding Strategies (${strategies.length})`));
74
+ console.log();
75
+ for (const s of strategies) {
76
+ console.log(` ${chalk.cyan(s.id.padEnd(25))} ${chalk.dim(s.description)}`);
77
+ }
78
+ console.log();
79
+ });
80
+ // ─── list graders ───
81
+ list
82
+ .command("graders")
83
+ .description("List all security graders")
84
+ .option("--json", "Output as JSON", false)
85
+ .action(async (opts) => {
86
+ const { ALL_GRADERS } = await import("@evalguard/core");
87
+ const graders = ALL_GRADERS.map((g) => ({
88
+ id: g.id,
89
+ name: g.name,
90
+ description: g.description,
91
+ }));
92
+ if (opts.json) {
93
+ console.log(JSON.stringify(graders, null, 2));
94
+ return;
95
+ }
96
+ console.log();
97
+ console.log(chalk.bold(` Security Graders (${graders.length})`));
98
+ console.log();
99
+ for (const g of graders) {
100
+ console.log(` ${chalk.cyan(g.id.padEnd(25))} ${chalk.dim(g.description)}`);
101
+ }
102
+ console.log();
103
+ });
104
+ // ─── list providers ───
105
+ list
106
+ .command("providers")
107
+ .description("List all supported LLM providers")
108
+ .option("--json", "Output as JSON", false)
109
+ .action(async (opts) => {
110
+ const { listProviders } = await import("@evalguard/core");
111
+ const providers = listProviders();
112
+ if (opts.json) {
113
+ console.log(JSON.stringify(providers, null, 2));
114
+ return;
115
+ }
116
+ console.log();
117
+ console.log(chalk.bold(` Supported Providers (${providers.length})`));
118
+ console.log();
119
+ const categories = {
120
+ "Cloud AI": ["openai", "anthropic", "gemini", "mistral", "groq", "cohere", "ai21", "deepseek", "perplexity", "xai", "cerebras"],
121
+ "Cloud Platforms": ["azure-openai", "bedrock", "vertex", "sagemaker", "watsonx", "snowflake", "databricks"],
122
+ "Open Source / Local": ["ollama", "vllm", "localai", "llamafile", "llamacpp", "huggingface"],
123
+ "Gateways / Routers": ["openrouter", "litellm", "helicone", "portkey", "cloudflare"],
124
+ "Specialized": ["together", "fireworks", "replicate", "fal", "alibaba", "voyage", "github-models"],
125
+ "Utility": ["echo", "script", "sequence", "custom-http"],
126
+ };
127
+ for (const [category, ids] of Object.entries(categories)) {
128
+ const available = ids.filter((id) => providers.includes(id));
129
+ if (available.length === 0)
130
+ continue;
131
+ console.log(` ${chalk.bold(category)}`);
132
+ for (const id of available) {
133
+ console.log(` ${chalk.cyan(id)}`);
134
+ }
135
+ console.log();
136
+ }
137
+ });
138
+ // ─── list attacks ───
139
+ list
140
+ .command("attacks")
141
+ .description("List legacy attack types")
142
+ .option("--json", "Output as JSON", false)
143
+ .action(async (opts) => {
144
+ const { ATTACK_TYPES } = await import("@evalguard/core");
145
+ const attacks = ATTACK_TYPES.map((a) => ({
146
+ type: a.type,
147
+ name: a.name,
148
+ severity: a.severity,
149
+ payloadCount: a.payloads.length,
150
+ }));
151
+ if (opts.json) {
152
+ console.log(JSON.stringify(attacks, null, 2));
153
+ return;
154
+ }
155
+ console.log();
156
+ console.log(chalk.bold(` Legacy Attack Types (${attacks.length})`));
157
+ console.log();
158
+ for (const a of attacks) {
159
+ const sevColor = a.severity === "critical" ? chalk.red : a.severity === "high" ? chalk.yellow : chalk.dim;
160
+ console.log(` ${chalk.cyan(a.type.padEnd(25))} ${sevColor(a.severity.padEnd(10))} ${a.payloadCount} payloads`);
161
+ }
162
+ console.log();
163
+ });
164
+ }
165
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `evalguard logs` — View recent eval/scan run logs
3
+ */
4
+ import { Command } from "commander";
5
+ export declare function registerLogs(program: Command): void;
6
+ //# sourceMappingURL=logs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0CpC,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0HnD"}
@@ -0,0 +1,153 @@
1
+ import chalk from "chalk";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as os from "os";
5
+ const LOG_DIR = path.join(os.homedir(), ".evalguard", "logs");
6
+ function getLogFiles(type) {
7
+ if (!fs.existsSync(LOG_DIR))
8
+ return [];
9
+ const files = fs.readdirSync(LOG_DIR).filter((f) => f.endsWith(".log"));
10
+ if (type) {
11
+ return files.filter((f) => f.startsWith(type));
12
+ }
13
+ return files;
14
+ }
15
+ function tailFile(filePath, lines) {
16
+ if (!fs.existsSync(filePath))
17
+ return [];
18
+ const content = fs.readFileSync(filePath, "utf-8");
19
+ const allLines = content.split("\n");
20
+ return allLines.slice(-lines);
21
+ }
22
+ function formatLogLine(line) {
23
+ if (!line.trim())
24
+ return "";
25
+ // Colorize log levels
26
+ if (line.includes("[ERROR]") || line.includes("[FAIL]")) {
27
+ return chalk.red(line);
28
+ }
29
+ if (line.includes("[WARN]")) {
30
+ return chalk.yellow(line);
31
+ }
32
+ if (line.includes("[PASS]") || line.includes("[OK]")) {
33
+ return chalk.green(line);
34
+ }
35
+ if (line.includes("[INFO]")) {
36
+ return chalk.dim(line);
37
+ }
38
+ return line;
39
+ }
40
+ export function registerLogs(program) {
41
+ program
42
+ .command("logs")
43
+ .description("View recent eval/scan run logs")
44
+ .option("-n, --lines <n>", "Number of lines to show", "50")
45
+ .option("-f, --follow", "Follow log output (watch for new lines)", false)
46
+ .option("-t, --type <type>", "Filter by type: eval or scan")
47
+ .option("--list", "List available log files", false)
48
+ .action(async (opts) => {
49
+ // Ensure log directory exists
50
+ if (!fs.existsSync(LOG_DIR)) {
51
+ fs.mkdirSync(LOG_DIR, { recursive: true, mode: 0o700 });
52
+ }
53
+ const logFiles = getLogFiles(opts.type);
54
+ if (opts.list) {
55
+ if (logFiles.length === 0) {
56
+ console.log(chalk.dim("\n No log files found.\n"));
57
+ return;
58
+ }
59
+ console.log();
60
+ console.log(chalk.bold(" Log Files"));
61
+ console.log(chalk.dim(" " + "-".repeat(60)));
62
+ for (const f of logFiles.sort().reverse()) {
63
+ const stat = fs.statSync(path.join(LOG_DIR, f));
64
+ const size = (stat.size / 1024).toFixed(1);
65
+ const date = stat.mtime.toLocaleString();
66
+ console.log(` ${chalk.cyan(f.padEnd(40))} ${chalk.dim(`${size} KB`)} ${chalk.dim(date)}`);
67
+ }
68
+ console.log();
69
+ return;
70
+ }
71
+ // Find the most recent log file
72
+ if (logFiles.length === 0) {
73
+ // Check results.json as a fallback log source
74
+ const resultsFile = path.join(os.homedir(), ".evalguard", "results.json");
75
+ if (fs.existsSync(resultsFile)) {
76
+ try {
77
+ const runs = JSON.parse(fs.readFileSync(resultsFile, "utf-8"));
78
+ const filteredRuns = opts.type
79
+ ? runs.filter((r) => r.type === opts.type)
80
+ : runs;
81
+ const recentRuns = filteredRuns.slice(-parseInt(opts.lines, 10));
82
+ if (recentRuns.length === 0) {
83
+ console.log(chalk.dim("\n No log entries found.\n"));
84
+ return;
85
+ }
86
+ console.log();
87
+ console.log(chalk.bold(" Recent Run Log"));
88
+ console.log(chalk.dim(" " + "-".repeat(70)));
89
+ for (const run of recentRuns.reverse()) {
90
+ const passRate = run.passRate;
91
+ const color = passRate >= 0.8 ? chalk.green : passRate >= 0.5 ? chalk.yellow : chalk.red;
92
+ const icon = passRate >= 0.8 ? chalk.green("PASS") : chalk.red("FAIL");
93
+ console.log(` ${chalk.dim(run.timestamp)} [${icon}] ${chalk.bold(String(run.name).slice(0, 30))} ` +
94
+ `${color((passRate * 100).toFixed(0) + "%")} ${chalk.dim(`(${run.model})`)}`);
95
+ }
96
+ console.log();
97
+ }
98
+ catch {
99
+ console.log(chalk.dim("\n No log entries found.\n"));
100
+ }
101
+ }
102
+ else {
103
+ console.log(chalk.dim("\n No log files found. Run an eval or scan first.\n"));
104
+ }
105
+ return;
106
+ }
107
+ const latestFile = logFiles.sort().reverse()[0];
108
+ const logPath = path.join(LOG_DIR, latestFile);
109
+ const numLines = parseInt(opts.lines, 10);
110
+ console.log(chalk.dim(` Reading ${latestFile}`));
111
+ console.log(chalk.dim(" " + "-".repeat(60)));
112
+ const lines = tailFile(logPath, numLines);
113
+ for (const line of lines) {
114
+ const formatted = formatLogLine(line);
115
+ if (formatted)
116
+ console.log(` ${formatted}`);
117
+ }
118
+ if (opts.follow) {
119
+ console.log(chalk.dim("\n Watching for new log entries... (Ctrl+C to stop)\n"));
120
+ let lastSize = fs.statSync(logPath).size;
121
+ const watcher = setInterval(() => {
122
+ try {
123
+ const stat = fs.statSync(logPath);
124
+ if (stat.size > lastSize) {
125
+ const fd = fs.openSync(logPath, "r");
126
+ const buf = Buffer.alloc(stat.size - lastSize);
127
+ fs.readSync(fd, buf, 0, buf.length, lastSize);
128
+ fs.closeSync(fd);
129
+ const newLines = buf.toString("utf-8").split("\n");
130
+ for (const line of newLines) {
131
+ const formatted = formatLogLine(line);
132
+ if (formatted)
133
+ console.log(` ${formatted}`);
134
+ }
135
+ lastSize = stat.size;
136
+ }
137
+ }
138
+ catch {
139
+ // File may have been rotated
140
+ }
141
+ }, 1000);
142
+ process.on("SIGINT", () => {
143
+ clearInterval(watcher);
144
+ console.log(chalk.dim("\n Stopped watching.\n"));
145
+ process.exit(0);
146
+ });
147
+ // Keep the process alive
148
+ await new Promise(() => { });
149
+ }
150
+ console.log();
151
+ });
152
+ }
153
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `evalguard model-scan` — Scan model files for backdoors/trojans
3
+ * Checks pickle, PyTorch (.pt/.pth), ONNX, and SafeTensors files
4
+ */
5
+ import { Command } from "commander";
6
+ export declare function registerModelScan(program: Command): void;
7
+ //# sourceMappingURL=model-scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-scan.d.ts","sourceRoot":"","sources":["../../src/commands/model-scan.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2MpC,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwHxD"}
@@ -0,0 +1,276 @@
1
+ import chalk from "chalk";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ // Dangerous pickle opcodes that can execute arbitrary code
5
+ const DANGEROUS_PICKLE_OPS = [
6
+ { bytes: Buffer.from([0x52]), name: "REDUCE", severity: "critical", desc: "Calls arbitrary callable with args" },
7
+ { bytes: Buffer.from([0x81]), name: "NEWOBJ", severity: "high", desc: "Creates new object instance" },
8
+ { bytes: Buffer.from([0x92]), name: "NEWOBJ_EX", severity: "high", desc: "Creates new object with kwargs" },
9
+ { bytes: Buffer.from([0x83]), name: "STACK_GLOBAL", severity: "critical", desc: "Pushes global by name from stack" },
10
+ { bytes: Buffer.from([0x63]), name: "GLOBAL", severity: "critical", desc: "Pushes global variable" },
11
+ { bytes: Buffer.from([0x69]), name: "INST", severity: "critical", desc: "Instantiates class" },
12
+ { bytes: Buffer.from([0x62]), name: "BUILD", severity: "medium", desc: "Calls __setstate__ or updates __dict__" },
13
+ ];
14
+ // Suspicious Python imports commonly used in malicious pickles
15
+ const SUSPICIOUS_IMPORTS = [
16
+ { pattern: "os.system", severity: "critical", desc: "System command execution" },
17
+ { pattern: "os.popen", severity: "critical", desc: "Process execution with pipe" },
18
+ { pattern: "subprocess", severity: "critical", desc: "Subprocess execution" },
19
+ { pattern: "builtins.eval", severity: "critical", desc: "Arbitrary code evaluation" },
20
+ { pattern: "builtins.exec", severity: "critical", desc: "Arbitrary code execution" },
21
+ { pattern: "builtins.__import__", severity: "critical", desc: "Dynamic module import" },
22
+ { pattern: "webbrowser", severity: "high", desc: "Browser launch capability" },
23
+ { pattern: "socket", severity: "high", desc: "Network socket access" },
24
+ { pattern: "http.client", severity: "high", desc: "HTTP client (data exfiltration)" },
25
+ { pattern: "urllib", severity: "high", desc: "URL handling (data exfiltration)" },
26
+ { pattern: "ctypes", severity: "high", desc: "C-level memory access" },
27
+ { pattern: "shutil.rmtree", severity: "critical", desc: "Recursive directory deletion" },
28
+ { pattern: "pickle.loads", severity: "medium", desc: "Nested pickle deserialization" },
29
+ { pattern: "marshal.loads", severity: "high", desc: "Marshal deserialization (code objects)" },
30
+ { pattern: "compile", severity: "medium", desc: "Code compilation" },
31
+ { pattern: "codecs.decode", severity: "medium", desc: "Potential obfuscation via codec" },
32
+ ];
33
+ // Obfuscation indicators
34
+ const OBFUSCATION_PATTERNS = [
35
+ { pattern: "\\x", severity: "medium", desc: "Hex-escaped bytes (potential obfuscation)" },
36
+ { pattern: "base64", severity: "medium", desc: "Base64 encoding (potential obfuscation)" },
37
+ { pattern: "zlib.decompress", severity: "medium", desc: "Compressed payload (potential obfuscation)" },
38
+ { pattern: "lambda", severity: "low", desc: "Lambda function in model data" },
39
+ { pattern: "exec(", severity: "critical", desc: "Direct exec call in model data" },
40
+ { pattern: "eval(", severity: "critical", desc: "Direct eval call in model data" },
41
+ ];
42
+ const SEVERITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
43
+ function scanFile(filePath) {
44
+ const findings = [];
45
+ const ext = path.extname(filePath).toLowerCase();
46
+ const stat = fs.statSync(filePath);
47
+ // Read file (limit to first 50MB for very large models)
48
+ const maxRead = Math.min(stat.size, 50 * 1024 * 1024);
49
+ const fd = fs.openSync(filePath, "r");
50
+ const buf = Buffer.alloc(maxRead);
51
+ fs.readSync(fd, buf, 0, maxRead, 0);
52
+ fs.closeSync(fd);
53
+ const content = buf.toString("latin1"); // Binary-safe string
54
+ // Check file type
55
+ if (ext === ".safetensors") {
56
+ findings.push({
57
+ severity: "info",
58
+ type: "format",
59
+ message: "SafeTensors format detected - this is the safest model format (no code execution)",
60
+ });
61
+ // SafeTensors files should not contain executable code
62
+ for (const imp of SUSPICIOUS_IMPORTS) {
63
+ if (content.includes(imp.pattern)) {
64
+ findings.push({
65
+ severity: "critical",
66
+ type: "suspicious_import",
67
+ message: `SafeTensors file contains suspicious string "${imp.pattern}" - ${imp.desc}`,
68
+ offset: content.indexOf(imp.pattern),
69
+ });
70
+ }
71
+ }
72
+ return findings;
73
+ }
74
+ // Pickle/PyTorch scan
75
+ if ([".pkl", ".pickle", ".pt", ".pth", ".bin"].includes(ext)) {
76
+ // Check for pickle magic number
77
+ const hasPickleMagic = buf[0] === 0x80;
78
+ if (hasPickleMagic) {
79
+ findings.push({
80
+ severity: "info",
81
+ type: "format",
82
+ message: `Pickle format v${buf[1]} detected - pickle files can execute arbitrary code on load`,
83
+ });
84
+ }
85
+ // PyTorch files are ZIP archives containing pickles
86
+ const isZip = buf[0] === 0x50 && buf[1] === 0x4b;
87
+ if (isZip) {
88
+ findings.push({
89
+ severity: "info",
90
+ type: "format",
91
+ message: "PyTorch ZIP archive detected - contains pickle data that can execute code on load",
92
+ });
93
+ }
94
+ // Scan for dangerous pickle opcodes
95
+ for (const op of DANGEROUS_PICKLE_OPS) {
96
+ let idx = 0;
97
+ let count = 0;
98
+ while ((idx = buf.indexOf(op.bytes, idx)) !== -1) {
99
+ count++;
100
+ idx += 1;
101
+ }
102
+ if (count > 0) {
103
+ findings.push({
104
+ severity: op.severity,
105
+ type: "pickle_opcode",
106
+ message: `${op.name} opcode found ${count}x - ${op.desc}`,
107
+ });
108
+ }
109
+ }
110
+ // Scan for suspicious imports
111
+ for (const imp of SUSPICIOUS_IMPORTS) {
112
+ const idx = content.indexOf(imp.pattern);
113
+ if (idx !== -1) {
114
+ const contextStart = Math.max(0, idx - 20);
115
+ const contextEnd = Math.min(content.length, idx + imp.pattern.length + 20);
116
+ const context = content.slice(contextStart, contextEnd).replace(/[^\x20-\x7e]/g, ".");
117
+ findings.push({
118
+ severity: imp.severity,
119
+ type: "suspicious_import",
120
+ message: `Found "${imp.pattern}" - ${imp.desc}`,
121
+ offset: idx,
122
+ context,
123
+ });
124
+ }
125
+ }
126
+ }
127
+ // ONNX scan
128
+ if (ext === ".onnx") {
129
+ findings.push({
130
+ severity: "info",
131
+ type: "format",
132
+ message: "ONNX format detected - generally safer than pickle but can contain custom ops",
133
+ });
134
+ // Check for custom operators that could hide malicious code
135
+ if (content.includes("custom_op") || content.includes("CustomOp")) {
136
+ findings.push({
137
+ severity: "medium",
138
+ type: "custom_op",
139
+ message: "Custom operator detected - review for unexpected behavior",
140
+ });
141
+ }
142
+ }
143
+ // Universal obfuscation checks
144
+ for (const ob of OBFUSCATION_PATTERNS) {
145
+ const idx = content.indexOf(ob.pattern);
146
+ if (idx !== -1) {
147
+ findings.push({
148
+ severity: ob.severity,
149
+ type: "obfuscation",
150
+ message: `Found "${ob.pattern}" - ${ob.desc}`,
151
+ offset: idx,
152
+ });
153
+ }
154
+ }
155
+ return findings;
156
+ }
157
+ function scanDirectory(dirPath) {
158
+ const results = new Map();
159
+ const scanExtensions = new Set([".pkl", ".pickle", ".pt", ".pth", ".bin", ".onnx", ".safetensors"]);
160
+ function walk(dir) {
161
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
162
+ for (const entry of entries) {
163
+ const fullPath = path.join(dir, entry.name);
164
+ if (entry.isDirectory()) {
165
+ walk(fullPath);
166
+ }
167
+ else if (scanExtensions.has(path.extname(entry.name).toLowerCase())) {
168
+ const findings = scanFile(fullPath);
169
+ results.set(fullPath, findings);
170
+ }
171
+ }
172
+ }
173
+ walk(dirPath);
174
+ return results;
175
+ }
176
+ export function registerModelScan(program) {
177
+ program
178
+ .command("model-scan")
179
+ .description("Scan model files for backdoors, trojans, and suspicious code")
180
+ .argument("<path>", "Path to model file or directory")
181
+ .option("-f, --format <format>", "Output format: text or json", "text")
182
+ .option("-s, --severity <level>", "Minimum severity to report: critical, high, medium, low, info", "low")
183
+ .action((targetPath, opts) => {
184
+ const resolved = path.resolve(targetPath);
185
+ if (!fs.existsSync(resolved)) {
186
+ console.error(chalk.red(`Path not found: ${resolved}`));
187
+ process.exit(1);
188
+ }
189
+ const minSeverity = SEVERITY_ORDER[opts.severity] ?? 3;
190
+ const stat = fs.statSync(resolved);
191
+ let allResults;
192
+ if (stat.isDirectory()) {
193
+ allResults = scanDirectory(resolved);
194
+ }
195
+ else {
196
+ allResults = new Map([[resolved, scanFile(resolved)]]);
197
+ }
198
+ if (allResults.size === 0) {
199
+ console.log(chalk.dim("\n No scannable model files found (.pkl, .pt, .pth, .onnx, .safetensors)\n"));
200
+ return;
201
+ }
202
+ // Filter by severity
203
+ for (const [file, findings] of allResults) {
204
+ const filtered = findings.filter((f) => SEVERITY_ORDER[f.severity] <= minSeverity);
205
+ allResults.set(file, filtered);
206
+ }
207
+ // JSON output
208
+ if (opts.format === "json") {
209
+ const jsonOutput = [];
210
+ for (const [file, findings] of allResults) {
211
+ jsonOutput.push({
212
+ file,
213
+ findings: findings.map((f) => ({ ...f })),
214
+ hasCritical: findings.some((f) => f.severity === "critical"),
215
+ hasHigh: findings.some((f) => f.severity === "high"),
216
+ });
217
+ }
218
+ console.log(JSON.stringify(jsonOutput, null, 2));
219
+ return;
220
+ }
221
+ // Text output
222
+ console.log();
223
+ console.log(chalk.bold(" EvalGuard Model Security Scan"));
224
+ console.log(chalk.dim(" " + "-".repeat(70)));
225
+ let totalCritical = 0;
226
+ let totalHigh = 0;
227
+ let totalFindings = 0;
228
+ for (const [file, findings] of allResults) {
229
+ const relPath = path.relative(process.cwd(), file);
230
+ const critical = findings.filter((f) => f.severity === "critical").length;
231
+ const high = findings.filter((f) => f.severity === "high").length;
232
+ totalCritical += critical;
233
+ totalHigh += high;
234
+ totalFindings += findings.length;
235
+ const fileIcon = critical > 0 ? chalk.red("!!!") : high > 0 ? chalk.yellow("!!") : chalk.green("OK");
236
+ console.log();
237
+ console.log(` ${fileIcon} ${chalk.bold(relPath)}`);
238
+ if (findings.length === 0) {
239
+ console.log(chalk.dim(" No issues found"));
240
+ continue;
241
+ }
242
+ // Sort findings by severity
243
+ const sorted = [...findings].sort((a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity]);
244
+ for (const f of sorted) {
245
+ const sevColor = f.severity === "critical" ? chalk.red.bold :
246
+ f.severity === "high" ? chalk.red :
247
+ f.severity === "medium" ? chalk.yellow :
248
+ f.severity === "low" ? chalk.dim :
249
+ chalk.dim;
250
+ const sevLabel = sevColor(f.severity.toUpperCase().padEnd(8));
251
+ console.log(` ${sevLabel} ${f.message}`);
252
+ if (f.context) {
253
+ console.log(chalk.dim(` context: ...${f.context}...`));
254
+ }
255
+ }
256
+ }
257
+ // Summary
258
+ console.log();
259
+ console.log(chalk.dim(" " + "-".repeat(70)));
260
+ console.log(` Scanned ${chalk.bold(String(allResults.size))} file(s), ` +
261
+ `${chalk.bold(String(totalFindings))} finding(s): ` +
262
+ `${totalCritical > 0 ? chalk.red.bold(`${totalCritical} critical`) : chalk.dim("0 critical")}, ` +
263
+ `${totalHigh > 0 ? chalk.red(`${totalHigh} high`) : chalk.dim("0 high")}`);
264
+ if (totalCritical > 0) {
265
+ console.log();
266
+ console.log(chalk.red.bold(" WARNING: Critical findings detected. Do NOT load this model without review."));
267
+ console.log(chalk.dim(" Consider using SafeTensors format instead of pickle-based formats."));
268
+ }
269
+ console.log();
270
+ // Exit code: 1 if critical findings
271
+ if (totalCritical > 0) {
272
+ process.exit(1);
273
+ }
274
+ });
275
+ }
276
+ //# sourceMappingURL=model-scan.js.map