vanguard-cli 3.1.14 ā 3.1.16
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.
- package/README.md +12 -0
- package/bin/vanguard.js +10 -5
- package/lib/commands/config.js +1 -1
- package/lib/commands/scan.js +16 -2
- package/lib/services/scanner.js +20 -2
- package/lib/utils/ui.js +4 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,6 +64,18 @@ npm install -g vanguard-cli
|
|
|
64
64
|
|
|
65
65
|
## š Quick Start
|
|
66
66
|
|
|
67
|
+
### š”ļø Enable Invisible Protection
|
|
68
|
+
Integrate Vanguard with your shell (Bash, Zsh, Fish, PowerShell) to automatically scan every `git clone`.
|
|
69
|
+
```bash
|
|
70
|
+
vanguard integrate
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### š Check Status
|
|
74
|
+
Verify if the firewall is active or if you are in a protected session.
|
|
75
|
+
```bash
|
|
76
|
+
vanguard status
|
|
77
|
+
```
|
|
78
|
+
|
|
67
79
|
### āļø Configuration
|
|
68
80
|
Setup your Gemini API key or local Ollama endpoint:
|
|
69
81
|
```bash
|
package/bin/vanguard.js
CHANGED
|
@@ -9,6 +9,10 @@ import { handlePull, handleClone } from '../lib/commands/scan.js';
|
|
|
9
9
|
import { showBanner, showFooter } from '../lib/utils/ui.js';
|
|
10
10
|
import config from '../lib/utils/config.js';
|
|
11
11
|
|
|
12
|
+
import { createRequire } from 'module';
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
const pkg = require('../package.json');
|
|
15
|
+
|
|
12
16
|
const program = new Command();
|
|
13
17
|
|
|
14
18
|
async function handleAction(actionName, logicFn) {
|
|
@@ -17,7 +21,7 @@ async function handleAction(actionName, logicFn) {
|
|
|
17
21
|
(config.get('AI_PROVIDER') === 'ollama');
|
|
18
22
|
|
|
19
23
|
if (!isConfigured) {
|
|
20
|
-
showBanner();
|
|
24
|
+
// showBanner();
|
|
21
25
|
console.log(chalk.yellow(`š”ļø Vanguard V3 is not fully configured. Starting setup...\n`));
|
|
22
26
|
await runConfigWizard();
|
|
23
27
|
// After wizard, check again
|
|
@@ -27,7 +31,7 @@ async function handleAction(actionName, logicFn) {
|
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
showBanner();
|
|
34
|
+
// showBanner();
|
|
31
35
|
const options = program.opts();
|
|
32
36
|
config.set('VERBOSE', !!options.verbose);
|
|
33
37
|
|
|
@@ -52,7 +56,7 @@ async function handleAction(actionName, logicFn) {
|
|
|
52
56
|
program
|
|
53
57
|
.name('vanguard')
|
|
54
58
|
.description('Enterprise-Grade AI Supply Chain Firewall')
|
|
55
|
-
.version(
|
|
59
|
+
.version(pkg.version)
|
|
56
60
|
.option('-m, --model <name>', 'Override AI model')
|
|
57
61
|
.option('-v, --verbose', 'Show verbose connection logs')
|
|
58
62
|
.option('--clear-cache', 'Flush local audit cache');
|
|
@@ -64,7 +68,7 @@ program
|
|
|
64
68
|
.command('status')
|
|
65
69
|
.description('Check firewall protection status')
|
|
66
70
|
.action(() => {
|
|
67
|
-
showBanner();
|
|
71
|
+
// showBanner();
|
|
68
72
|
const isInternal = process.env.VANGUARD_INTERNAL === '1';
|
|
69
73
|
console.log(chalk.bold('š„ Firewall Status:'));
|
|
70
74
|
if (isInternal) {
|
|
@@ -94,6 +98,7 @@ program
|
|
|
94
98
|
await handleAction('CLONE', () => handleClone(url, directory, program.opts()));
|
|
95
99
|
});
|
|
96
100
|
|
|
101
|
+
showBanner(pkg.version);
|
|
97
102
|
program.parse(process.argv);
|
|
98
103
|
|
|
99
104
|
// Default action: if no command provided, check config
|
|
@@ -103,7 +108,7 @@ if (!process.argv.slice(2).length) {
|
|
|
103
108
|
(config.get('AI_PROVIDER') === 'ollama');
|
|
104
109
|
|
|
105
110
|
if (!isConfigured) {
|
|
106
|
-
showBanner();
|
|
111
|
+
// showBanner();
|
|
107
112
|
console.log(chalk.yellow(`š”ļø Vanguard V3 is not fully configured. Starting setup...\n`));
|
|
108
113
|
await runConfigWizard();
|
|
109
114
|
} else {
|
package/lib/commands/config.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createSpinner } from '../utils/spinner.js';
|
|
|
5
5
|
import { showBanner } from '../utils/ui.js';
|
|
6
6
|
|
|
7
7
|
export async function runConfigWizard() {
|
|
8
|
-
showBanner();
|
|
8
|
+
// showBanner();
|
|
9
9
|
console.log('š°ļø Vanguard Configuration Wizard');
|
|
10
10
|
console.log(chalk.dim(`š Config Path: ${config.path}\n`));
|
|
11
11
|
|
package/lib/commands/scan.js
CHANGED
|
@@ -41,7 +41,13 @@ export async function handlePull(cmdOptions, programOptions) {
|
|
|
41
41
|
try {
|
|
42
42
|
analysis = await scanner.scan('git_diff', diff, spinner);
|
|
43
43
|
} catch (err) {
|
|
44
|
-
|
|
44
|
+
let msg = err.message;
|
|
45
|
+
if (msg.includes('[GoogleGenerativeAI Error]')) {
|
|
46
|
+
msg = msg.split('[')[0].trim();
|
|
47
|
+
if (msg.includes('400')) msg = 'Invalid API Request (400). Check configuration.';
|
|
48
|
+
else if (msg.length > 100) msg = 'AI Service Error (Check connection/quota)';
|
|
49
|
+
}
|
|
50
|
+
spinner.fail(`AI Audit Failed: ${msg}`);
|
|
45
51
|
console.log(chalk.yellow('\nš” Tip: You might be rate-limited. Try again in 60s or run "vanguard config" to switch to local Ollama.'));
|
|
46
52
|
return;
|
|
47
53
|
}
|
|
@@ -125,7 +131,15 @@ export async function handleClone(url, directory, programOptions) {
|
|
|
125
131
|
CacheManager.save(file, content, 'SAFE');
|
|
126
132
|
}
|
|
127
133
|
} catch (err) {
|
|
128
|
-
|
|
134
|
+
let msg = err.message;
|
|
135
|
+
// Clean up Google's noisy JSON error if it slipped through
|
|
136
|
+
if (msg.includes('[GoogleGenerativeAI Error]')) {
|
|
137
|
+
msg = msg.split('[')[0].trim(); // Take the first part or default to simple text
|
|
138
|
+
if (msg.includes('400')) msg = 'Invalid API Request (400). Check configuration.';
|
|
139
|
+
else if (msg.length > 100) msg = 'AI Service Error (Check connection/quota)';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
scanSpinner.fail(`Audit Interrupted: ${msg}`);
|
|
129
143
|
console.log(chalk.yellow('\nā ļø Service Error. Part of the codebase remains unverified.'));
|
|
130
144
|
await cleanupSandbox(tempPath);
|
|
131
145
|
return;
|
package/lib/services/scanner.js
CHANGED
|
@@ -89,12 +89,14 @@ RESPONSE FORMAT (JSON ONLY):
|
|
|
89
89
|
if (spinner) {
|
|
90
90
|
const originalText = spinner.text;
|
|
91
91
|
for (let i = waitTime; i > 0; i--) {
|
|
92
|
-
|
|
92
|
+
// Only update once per second to prevent terminal flickering
|
|
93
|
+
if (spinner.text.includes(`${i}s`)) continue;
|
|
94
|
+
spinner.text = `āļø Quota hit (${i}s). Waiting... (Tip: Ctrl+C -> 'vanguard config' to switch AI)`;
|
|
93
95
|
await sleep(1000);
|
|
94
96
|
}
|
|
95
97
|
spinner.text = originalText;
|
|
96
98
|
} else {
|
|
97
|
-
console.log(chalk.yellow(`\nā ļø Rate limit hit. Cooling down for ${waitTime}s (
|
|
99
|
+
console.log(chalk.yellow(`\nā ļø Rate limit hit. Cooling down for ${waitTime}s... (Tip: 'vanguard config' to switch AI)`));
|
|
98
100
|
await sleep(waitTime * 1000);
|
|
99
101
|
}
|
|
100
102
|
return this.scanWithRetry(filePath, content, spinner, attempt + 1);
|
|
@@ -110,6 +112,19 @@ RESPONSE FORMAT (JSON ONLY):
|
|
|
110
112
|
console.log(chalk.cyan(' vanguard config -> Select "Google Gemini"'));
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
const isAuthError =
|
|
116
|
+
error.message.includes('API key') ||
|
|
117
|
+
error.message.includes('400') ||
|
|
118
|
+
error.message.includes('PERMISSION_DENIED');
|
|
119
|
+
|
|
120
|
+
if (isAuthError) {
|
|
121
|
+
if (spinner) spinner.fail('Authentication Failed');
|
|
122
|
+
console.log(chalk.red('\nā Critical: Gemini API Key is invalid, expired, or missing permissions.'));
|
|
123
|
+
console.log(chalk.yellow('š Run "vanguard config" to update your credentials.'));
|
|
124
|
+
// We fail closed - do not allow the scan to proceed or return 'safe'
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
|
|
113
128
|
if (isServerErr && attempt === 1) {
|
|
114
129
|
if (spinner) spinner.text = 'š AI Server hiccup. Retrying immediately...';
|
|
115
130
|
return this.scanWithRetry(filePath, content, spinner, attempt + 1);
|
|
@@ -123,6 +138,9 @@ RESPONSE FORMAT (JSON ONLY):
|
|
|
123
138
|
const apiKey = config.get('GEMINI_KEY');
|
|
124
139
|
if (!apiKey) throw new Error('Gemini API Key missing. Run "vanguard config"');
|
|
125
140
|
|
|
141
|
+
if (!apiKey || apiKey.trim() === '') {
|
|
142
|
+
throw new Error('API key is missing.');
|
|
143
|
+
}
|
|
126
144
|
const genAI = new GoogleGenerativeAI(apiKey.trim());
|
|
127
145
|
// Use the latest stable pro flash model for the best performance/quota ratio
|
|
128
146
|
const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
|
package/lib/utils/ui.js
CHANGED
|
@@ -3,9 +3,10 @@ import chalk from 'chalk';
|
|
|
3
3
|
import boxen from 'boxen';
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
|
|
6
|
-
export function showBanner() {
|
|
7
|
-
console.log(chalk.cyan(figlet.textSync('VANGUARD', { horizontalLayout: 'full' })));
|
|
8
|
-
|
|
6
|
+
export function showBanner(version) {
|
|
7
|
+
console.log(chalk.cyan(figlet.textSync('VANGUARD', { font: 'Standard', horizontalLayout: 'full' })));
|
|
8
|
+
const versionStr = version ? ` v${version}` : '';
|
|
9
|
+
console.log(chalk.dim(`š”ļø Enterprise AI Supply Chain Firewall${versionStr}\n`));
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export function showAlert(analysis) {
|