vanguard-cli 3.1.18 → 3.1.20

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 CHANGED
@@ -26,7 +26,8 @@ Modern software supply chain attacks are sophisticated. Traditional antiviruses
26
26
  * 🧠 **Hybrid Intelligence:** Combines **Google Gemini 2.0**'s reasoning with a curated **Threat Intelligence DB** and **OSV.dev** vulnerability data.
27
27
  * ⚡ **Zero-Latency Caching:** Uses SHA-256 hashing to memorize safe files. Re-scanning a project takes milliseconds.
28
28
  * 🔒 **Privacy First:** Supports **Local Ollama**. Your code never leaves your machine if you choose local models.
29
- * 🛡️ **Proactive Defense:** It doesn't just scan; it intercepts Git commands to prevent bad code from reaching your machine.
29
+ * 🛡️ **Proactive Defense:** It doesn't just scan; it intercepts Git commands to prevent bad code from reaching your disk.
30
+ * ⚡ **Flexible Audit Modes:** Choose between a **Quick Audit** (targeting configuration and entry files) or a **Deep Audit** (full behavioral analysis of all code files).
30
31
 
31
32
  ---
32
33
 
package/bin/vanguard.js CHANGED
@@ -8,11 +8,15 @@ import { runIntegrate } from '../lib/commands/integrate.js';
8
8
  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
+ import { checkForUpdates } from '../lib/utils/update.js';
11
12
 
12
13
  import { createRequire } from 'module';
13
14
  const require = createRequire(import.meta.url);
14
15
  const pkg = require('../package.json');
15
16
 
17
+ // Check for updates in the background
18
+ checkForUpdates(pkg.version);
19
+
16
20
  const program = new Command();
17
21
 
18
22
  async function handleAction(actionName, logicFn) {
@@ -35,38 +39,24 @@ async function handleAction(actionName, logicFn) {
35
39
  const options = program.opts();
36
40
  config.set('VERBOSE', !!options.verbose);
37
41
 
38
- const { proceed } = await inquirer.prompt([
42
+ const { mode } = await inquirer.prompt([
39
43
  {
40
- type: 'confirm',
41
- name: 'proceed',
42
- message: `🛡️ Vanguard Enterprise Edition detected a ${actionName}. Activate Hybrid Intelligence?`,
43
- default: true,
44
+ type: 'list',
45
+ name: 'mode',
46
+ message: `🛡️ Vanguard Enterprise Edition detected a ${actionName}. Select protection level:`,
47
+ choices: [
48
+ { name: chalk.green('⚡ Quick Audit ') + chalk.dim('(Config & Entry Files)'), value: 'quick' },
49
+ { name: chalk.cyan('🛡️ Deep Audit ') + chalk.dim('(Full behavioral scanning)'), value: 'deep' },
50
+ { name: chalk.yellow('⚠️ Skip ') + chalk.dim('(Direct execution - RISKY)'), value: 'skip' },
51
+ ],
52
+ default: 'quick',
44
53
  },
45
54
  ]);
46
55
 
47
- if (!proceed) {
48
- console.log(chalk.yellow('⚠️ Skipping protection. Proceeding at your own risk.'));
56
+ if (mode === 'skip') {
57
+ console.log(chalk.yellow('\n⚠️ Skipping protection. Proceeding at your own risk.'));
49
58
 
50
- // If the user skips Vanguard, we must execute the original git command
51
59
  if (actionName === 'CLONE') {
52
- // Reconstruct arguments
53
- const args = process.argv.slice(3); // Remove node, bin, clone
54
- // This is a simplified fallback. In a real integration, we'd want to execSync/spawn
55
- // "git clone ..." but since we are inside a node process wrapping git,
56
- // we can suggest the user run usage logic or attempt spawn.
57
-
58
- // However, for the 'integrate' shell function, if 'vanguard' exits with 0 and no output,
59
- // the shell function expects to be done.
60
- // The Shell Integration logic:
61
- // if [ "$1" = "clone" ] ... vanguard clone ... else git clone ...
62
-
63
- // If we return here, Vanguard finishes. The shell wrapper won't run `git clone` because
64
- // it delegated the job to `vanguard clone`.
65
-
66
- // SO: We must actually perform the git clone ourselves now, or exit with a specific code
67
- // that tells the shell wrapper to fallback (complex).
68
-
69
- // Easiest path: Spawn 'git' directly here.
70
60
  const { spawn } = await import('child_process');
71
61
  const gitArgs = [actionName.toLowerCase(), ...process.argv.slice(3)];
72
62
 
@@ -74,13 +64,12 @@ async function handleAction(actionName, logicFn) {
74
64
  child.on('close', (code) => {
75
65
  process.exit(code);
76
66
  });
77
- // We return promise to await exit
78
67
  return new Promise(() => { });
79
68
  }
80
69
  return;
81
70
  }
82
71
 
83
- await logicFn();
72
+ await logicFn(mode);
84
73
  showFooter();
85
74
  }
86
75
 
@@ -117,7 +106,7 @@ program
117
106
  .description('Audit and pull upstream changes')
118
107
  .option('-f, --force', 'Force merge even if threats are detected')
119
108
  .action(async (cmdOptions) => {
120
- await handleAction('PULL', () => handlePull(cmdOptions, program.opts()));
109
+ await handleAction('PULL', (mode) => handlePull(cmdOptions, program.opts(), mode));
121
110
  });
122
111
 
123
112
  program
@@ -126,7 +115,7 @@ program
126
115
  .argument('<url>', 'Git URL')
127
116
  .argument('[directory]', 'Target directory')
128
117
  .action(async (url, directory) => {
129
- await handleAction('CLONE', () => handleClone(url, directory, program.opts()));
118
+ await handleAction('CLONE', (mode) => handleClone(url, directory, program.opts(), mode));
130
119
  });
131
120
 
132
121
  showBanner(pkg.version);
@@ -11,24 +11,27 @@ import { walkProject } from '../utils/walker.js';
11
11
  import {
12
12
  fetchUpstream,
13
13
  getUpstreamDiff,
14
+ getUpstreamDiffFiles,
14
15
  mergeUpstream,
15
16
  createSandboxClone,
16
17
  finalizeClone,
17
18
  cleanupSandbox,
18
19
  } from '../services/git.js';
19
20
 
20
- export async function handlePull(cmdOptions, programOptions) {
21
+ export async function handlePull(cmdOptions, programOptions, mode = 'deep') {
21
22
  if (programOptions.clearCache) CacheManager.clear();
23
+ const scanner = new VanguardScanner(programOptions.model);
22
24
 
23
25
  const intel = new IntelligenceEngine(programOptions.verbose);
24
26
  const spinner = createSpinner('🚀 Syncing intelligence & probing connections...').start();
25
27
 
26
28
  const pkgContent = await fs.readFile(path.join(process.cwd(), 'package.json'), 'utf-8').catch(() => null);
27
29
  const context = await intel.sync(pkgContent);
30
+ scanner.intelligenceContext = context;
28
31
 
29
32
  spinner.text = '🔍 Auditing diff for supply chain threats...';
30
33
  await fetchUpstream();
31
- const diff = await getUpstreamDiff();
34
+ let diff = await getUpstreamDiff();
32
35
 
33
36
  if (!diff || diff.trim() === '') {
34
37
  spinner.succeed('✅ No upstream changes to audit.');
@@ -36,13 +39,28 @@ export async function handlePull(cmdOptions, programOptions) {
36
39
  return;
37
40
  }
38
41
 
39
- const scanner = new VanguardScanner(programOptions.model, context);
42
+ if (mode === 'quick') {
43
+ const filenames = await getUpstreamDiffFiles();
44
+ const highRiskFilenames = filenames.filter(f => scanner.isHighRisk(f));
45
+
46
+ if (highRiskFilenames.length === 0) {
47
+ spinner.succeed('✅ No high-risk files modified. Quick Scan Passed.');
48
+ await mergeUpstream();
49
+ return;
50
+ }
51
+
52
+ console.log(chalk.cyan(`\n⚡ Quick Audit: ${highRiskFilenames.length}/${filenames.length} modified files targeted.`));
53
+ // Get diff only for high risk files
54
+ diff = await getUpstreamDiff(highRiskFilenames);
55
+ }
56
+
40
57
  let analysis;
41
58
  try {
42
59
  analysis = await scanner.scan('git_diff', diff, spinner);
43
60
  } catch (err) {
44
61
  if (err.message === 'CRITICAL_AUTH_FAILURE') {
45
- process.exit(1);
62
+ process.exitCode = 1;
63
+ return;
46
64
  }
47
65
  let msg = err.message;
48
66
  if (msg.includes('[GoogleGenerativeAI Error]')) {
@@ -67,7 +85,7 @@ export async function handlePull(cmdOptions, programOptions) {
67
85
  : await inquirer.prompt([
68
86
  {
69
87
  type: 'confirm',
70
- name: 'force',
88
+ name: 'proceed',
71
89
  message: '⚠️ BLOCK VERDICT. Force override merge?',
72
90
  default: false,
73
91
  },
@@ -81,7 +99,7 @@ export async function handlePull(cmdOptions, programOptions) {
81
99
  }
82
100
  }
83
101
 
84
- export async function handleClone(url, directory, programOptions) {
102
+ export async function handleClone(url, directory, programOptions, mode = 'deep') {
85
103
  const targetPath = directory || path.basename(url, '.git');
86
104
 
87
105
  const intel = new IntelligenceEngine(programOptions.verbose);
@@ -93,20 +111,28 @@ export async function handleClone(url, directory, programOptions) {
93
111
  const pkgContent = await fs.readFile(path.join(tempPath, 'package.json'), 'utf-8').catch(() => null);
94
112
  const context = await intel.sync(pkgContent);
95
113
 
96
- const files = await walkProject(tempPath);
114
+ const allFiles = await walkProject(tempPath);
97
115
  spinner.stop();
98
116
 
99
- console.log(chalk.cyan(`\n📑 Batch Audit: ${files.length} files identified.`));
100
-
101
117
  const scanner = new VanguardScanner(programOptions.model, context);
118
+
119
+ // Filtering based on mode
120
+ let filesToScan = allFiles;
121
+ if (mode === 'quick') {
122
+ filesToScan = allFiles.filter(f => scanner.isHighRisk(f));
123
+ console.log(chalk.cyan(`\n⚡ Quick Audit: ${filesToScan.length}/${allFiles.length} critical files targeted.`));
124
+ } else {
125
+ console.log(chalk.cyan(`\n📑 Batch Audit: ${allFiles.length} files identified.`));
126
+ }
127
+
102
128
  let finalVerdict = 'SAFE';
103
129
  let allThreats = [];
104
130
  let cachedCount = 0;
105
131
  let skippedCount = 0;
106
132
 
107
- const scanSpinner = createSpinner(`🧠 Auditing ${files.length} files...`).start();
133
+ const scanSpinner = createSpinner(`🧠 Auditing ${filesToScan.length} files...`).start();
108
134
 
109
- for (const file of files) {
135
+ for (const file of filesToScan) {
110
136
  const stats = await fs.stat(file);
111
137
 
112
138
  // Enterprise Filtering
@@ -136,12 +162,12 @@ export async function handleClone(url, directory, programOptions) {
136
162
  } catch (err) {
137
163
  if (err.message === 'CRITICAL_AUTH_FAILURE') {
138
164
  await cleanupSandbox(tempPath);
139
- process.exit(1);
165
+ process.exitCode = 1;
166
+ return;
140
167
  }
141
168
  let msg = err.message;
142
- // Clean up Google's noisy JSON error if it slipped through
143
169
  if (msg.includes('[GoogleGenerativeAI Error]')) {
144
- msg = msg.split('[')[0].trim(); // Take the first part or default to simple text
170
+ msg = msg.split('[')[0].trim();
145
171
  if (msg.includes('400')) msg = 'Invalid API Request (400). Check configuration.';
146
172
  else if (msg.length > 100) msg = 'AI Service Error (Check connection/quota)';
147
173
  }
@@ -153,19 +179,20 @@ export async function handleClone(url, directory, programOptions) {
153
179
  }
154
180
  }
155
181
 
156
- scanSpinner.succeed(`Audit Complete: ${files.length} identified (${cachedCount} cached, ${skippedCount} skipped).`);
182
+ scanSpinner.succeed(`Audit Complete: ${filesToScan.length} scanned (${cachedCount} cached, ${skippedCount} skipped).`);
157
183
 
158
184
  if (finalVerdict === 'BLOCK') {
159
185
  showAlert({
160
186
  verdict: 'BLOCK',
161
187
  risk_score: 99,
162
188
  threats: allThreats,
163
- summary: 'Enterprise Deep Audit identified multiple high-risk vectors.',
189
+ summary: `Enterprise ${mode.toUpperCase()} Audit identified multiple high-risk vectors.`,
164
190
  });
165
191
  await cleanupSandbox(tempPath);
166
192
  console.log(chalk.red.bold('\n🛡️ Malware Detected! Clone aborted. Your disk is safe.'));
167
193
  } else {
168
- console.log(chalk.green(`\n✅ Deep Audit Passed. Finalizing clone to ${targetPath}...`));
194
+ const label = mode === 'quick' ? 'Quick Audit' : 'Deep Audit';
195
+ console.log(chalk.green(`\n✅ ${label} Passed. Finalizing clone to ${targetPath}...`));
169
196
  await finalizeClone(tempPath, targetPath);
170
197
  }
171
198
  }
@@ -17,9 +17,19 @@ export async function fetchUpstream() {
17
17
  /**
18
18
  * Gets the diff string for scanning.
19
19
  */
20
- export async function getUpstreamDiff() {
20
+ export async function getUpstreamDiff(files = []) {
21
21
  // Compare HEAD vs FETCH_HEAD (which is what was just fetched)
22
- return await git.diff(['HEAD..FETCH_HEAD']);
22
+ const args = ['HEAD..FETCH_HEAD'];
23
+ if (files.length > 0) args.push('--', ...files);
24
+ return await git.diff(args);
25
+ }
26
+
27
+ /**
28
+ * Gets the list of filenames changed in upstream.
29
+ */
30
+ export async function getUpstreamDiffFiles() {
31
+ const diffSummary = await git.diffSummary(['HEAD..FETCH_HEAD']);
32
+ return diffSummary.files.map(f => f.file);
23
33
  }
24
34
 
25
35
  /**
@@ -1,190 +1,217 @@
1
- import { GoogleGenerativeAI } from '@google/generative-ai';
2
- import config from '../utils/config.js';
3
- import fetch from 'node-fetch';
4
- import fs from 'fs/promises';
5
- import path from 'path';
6
- import { fileURLToPath } from 'url';
7
- import chalk from 'chalk';
8
- import pLimit from 'p-limit';
9
-
10
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
- const THREATS_PATH = path.join(__dirname, '../threats.json');
12
-
13
- const limit = pLimit(1); // Conservative concurrency for free tier stability
14
-
15
- export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
16
-
17
- export class VanguardScanner {
18
- constructor(modelOverride, intelligenceContext = '') {
19
- this.provider = config.get('AI_PROVIDER');
20
- this.modelOverride = modelOverride;
21
- this.intelligenceContext = intelligenceContext;
22
- }
23
-
24
- shouldSkip(filePath, stats) {
25
- const ext = path.extname(filePath).toLowerCase();
26
- const binaryExtensions = [
27
- '.exe', '.dll', '.so', '.bin', '.png', '.jpg', '.jpeg', '.gif', '.pdf', '.zip', '.gz',
28
- ];
29
- if (binaryExtensions.includes(ext)) return true;
30
- if (stats.size > 100 * 1024) return true;
31
- if (filePath.includes('node_modules')) return true;
32
- return false;
33
- }
34
-
35
- async getSystemInstruction() {
36
- const threatsData = await fs.readFile(THREATS_PATH, 'utf-8');
37
- const threats = JSON.parse(threatsData);
38
- const categoriesListing = threats
39
- .map((t, i) => `${i + 1}. **${t.name}:** ${t.description}`)
40
- .join('\n');
41
-
42
- return `
43
- You are a Senior Security Architect & Auditor. YOUR MISSION: Prevent Supply Chain Attacks.
44
- Analyze the provided code/diff with extreme scrutiny.
45
-
46
- YOU MUST BLOCK IF YOU DETECT ANY OF THESE CATEGORIES:
47
- ${categoriesListing}
48
-
49
- ${this.intelligenceContext}
50
-
51
- JSON OUTPUT RULES:
52
- - risk_score: 0-100.
53
- - verdict: "BLOCK" or "SAFE".
54
- - threats: Array of { "file", "line", "threat", "reason" }
55
- - summary: Clear explanation.
56
-
57
- RESPONSE FORMAT (JSON ONLY):
58
- { "risk_score": 95, "verdict": "BLOCK", "threats": [], "summary": "..." }
59
- `;
60
- }
61
-
62
- async scan(filePath, content, spinner = null) {
63
- // Random Jitter Throttling (1s - 3s)
64
- if (this.provider === 'gemini') {
65
- const jitter = Math.floor(Math.random() * 2000) + 1000;
66
- await sleep(jitter);
67
- }
68
- return limit(() => this.scanWithRetry(filePath, content, spinner));
69
- }
70
-
71
- async scanWithRetry(filePath, content, spinner, attempt = 1) {
72
- const maxAttempts = 3;
73
- try {
74
- if (this.provider === 'gemini') {
75
- return await this.scanWithGemini(content);
76
- } else {
77
- return await this.scanWithOllama(content);
78
- }
79
- } catch (error) {
80
- const isRateLimit =
81
- error.message.includes('429') ||
82
- error.message.includes('Too Many Requests') ||
83
- error.message.includes('Quota');
84
-
85
- const isServerErr = error.message.includes('500') || error.message.includes('503');
86
-
87
- if (isRateLimit && attempt <= maxAttempts) {
88
- const waitTime = attempt === 1 ? 30 : 60;
89
- if (spinner) {
90
- const originalText = spinner.text;
91
- for (let i = waitTime; i > 0; i--) {
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)`;
95
- await sleep(1000);
96
- }
97
- spinner.text = originalText;
98
- } else {
99
- console.log(chalk.yellow(`\n⚠️ Rate limit hit. Cooling down for ${waitTime}s... (Tip: 'vanguard config' to switch AI)`));
100
- await sleep(waitTime * 1000);
101
- }
102
- return this.scanWithRetry(filePath, content, spinner, attempt + 1);
103
- } else if (isRateLimit) {
104
- // Final failure after retries
105
- console.log(chalk.blue('\n💡 Tip: Gemini Quota exceeded? Switch to local offline mode:'));
106
- console.log(chalk.cyan(' vanguard config -> Select "Local Ollama"'));
107
- }
108
-
109
- const isOllamaError = error.message.includes('Ollama error') || error.message.includes('fetch failed');
110
- if (isOllamaError) {
111
- console.log(chalk.blue('\n💡 Tip: Local AI failing? Switch to Cloud fallback:'));
112
- console.log(chalk.cyan(' vanguard config -> Select "Google Gemini"'));
113
- }
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) can cause UV_HANDLE_CLOSING assertion on Windows if async ops are pending.
126
- // Instead, throw a specific error that the caller can catch and exit cleanly.
127
- throw new Error('CRITICAL_AUTH_FAILURE');
128
- }
129
-
130
- if (isServerErr && attempt === 1) {
131
- if (spinner) spinner.text = '🔄 AI Server hiccup. Retrying immediately...';
132
- return this.scanWithRetry(filePath, content, spinner, attempt + 1);
133
- }
134
-
135
- throw error;
136
- }
137
- }
138
-
139
- async scanWithGemini(content) {
140
- const apiKey = config.get('GEMINI_KEY');
141
- if (!apiKey) throw new Error('Gemini API Key missing. Run "vanguard config"');
142
-
143
- if (!apiKey || apiKey.trim() === '') {
144
- throw new Error('API key is missing.');
145
- }
146
- const genAI = new GoogleGenerativeAI(apiKey.trim());
147
- // Use the latest stable pro flash model for the best performance/quota ratio
148
- const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
149
- const instruction = await this.getSystemInstruction();
150
-
151
- const result = await model.generateContent({
152
- contents: [{ role: 'user', parts: [{ text: `${instruction}\n\nFILE CONTENT:\n${content}` }] }],
153
- });
154
-
155
- const response = await result.response;
156
- return this.parseResponse(response.text());
157
- }
158
-
159
- async scanWithOllama(content) {
160
- const model = this.modelOverride || config.get('OLLAMA_MODEL');
161
- const url = `${config.get('OLLAMA_URL')}/api/generate`;
162
- const instruction = await this.getSystemInstruction();
163
-
164
- const response = await fetch(url, {
165
- method: 'POST',
166
- headers: { 'Content-Type': 'application/json' },
167
- body: JSON.stringify({
168
- model: model,
169
- prompt: `${instruction}\n\nFILE CONTENT:\n${content}`,
170
- stream: false,
171
- format: 'json',
172
- }),
173
- });
174
-
175
- if (!response.ok) throw new Error(`Ollama error: ${response.statusText}`);
176
- const data = await response.json();
177
- return this.parseResponse(data.response);
178
- }
179
-
180
- parseResponse(text) {
181
- try {
182
- const start = text.indexOf('{');
183
- const end = text.lastIndexOf('}');
184
- if (start === -1 || end === -1) throw new Error('No JSON object found');
185
- return JSON.parse(text.substring(start, end + 1));
186
- } catch (e) {
187
- throw new Error(`Invalid AI JSON format: ${e.message}`);
188
- }
189
- }
190
- }
1
+ import { GoogleGenerativeAI } from '@google/generative-ai';
2
+ import config from '../utils/config.js';
3
+ import fetch from 'node-fetch';
4
+ import fs from 'fs/promises';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import chalk from 'chalk';
8
+ import pLimit from 'p-limit';
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ const THREATS_PATH = path.join(__dirname, '../threats.json');
12
+
13
+ const limit = pLimit(1); // Conservative concurrency for free tier stability
14
+
15
+ export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
16
+
17
+ export class VanguardScanner {
18
+ constructor(modelOverride, intelligenceContext = '') {
19
+ this.provider = config.get('AI_PROVIDER');
20
+ this.modelOverride = modelOverride;
21
+ this.intelligenceContext = intelligenceContext;
22
+ }
23
+
24
+ shouldSkip(filePath, stats) {
25
+ const ext = path.extname(filePath).toLowerCase();
26
+ const binaryExtensions = [
27
+ '.exe', '.dll', '.so', '.bin', '.png', '.jpg', '.jpeg', '.gif', '.pdf', '.zip', '.gz',
28
+ '.ico', '.svg', '.woff', '.woff2', '.ttf', '.eot', '.webp', '.map', '.pyc', '.jar',
29
+ ];
30
+ if (binaryExtensions.includes(ext)) return true;
31
+ if (stats.size > 100 * 1024) return true;
32
+ if (filePath.includes('node_modules')) return true;
33
+ return false;
34
+ }
35
+
36
+ isHighRisk(filePath) {
37
+ const filename = path.basename(filePath).toLowerCase();
38
+ const highRiskFiles = [
39
+ 'package.json',
40
+ 'package-lock.json',
41
+ 'yarn.lock',
42
+ 'pnpm-lock.yaml',
43
+ 'tsconfig.json',
44
+ 'jsconfig.json',
45
+ '.env',
46
+ 'install.js',
47
+ 'postinstall.js',
48
+ 'preinstall.js',
49
+ ];
50
+
51
+ if (highRiskFiles.includes(filename)) return true;
52
+ if (filePath.includes('.github/workflows')) return true;
53
+ if (filePath.includes('.vscode')) return true;
54
+
55
+ // Common entry points
56
+ const entryPoints = ['index.js', 'main.js', 'server.js', 'app.js', 'cli.js'];
57
+ if (entryPoints.includes(filename)) return true;
58
+
59
+ return false;
60
+ }
61
+
62
+ async getSystemInstruction() {
63
+ const threatsData = await fs.readFile(THREATS_PATH, 'utf-8');
64
+ const threats = JSON.parse(threatsData);
65
+ const categoriesListing = threats
66
+ .map((t, i) => `${i + 1}. **${t.name}:** ${t.description}`)
67
+ .join('\n');
68
+
69
+ return `
70
+ You are a Senior Security Architect & Auditor. YOUR MISSION: Prevent Supply Chain Attacks.
71
+ Analyze the provided code/diff with extreme scrutiny.
72
+
73
+ YOU MUST BLOCK IF YOU DETECT ANY OF THESE CATEGORIES:
74
+ ${categoriesListing}
75
+
76
+ ${this.intelligenceContext}
77
+
78
+ JSON OUTPUT RULES:
79
+ - risk_score: 0-100.
80
+ - verdict: "BLOCK" or "SAFE".
81
+ - threats: Array of { "file", "line", "threat", "reason" }
82
+ - summary: Clear explanation.
83
+
84
+ RESPONSE FORMAT (JSON ONLY):
85
+ { "risk_score": 95, "verdict": "BLOCK", "threats": [], "summary": "..." }
86
+ `;
87
+ }
88
+
89
+ async scan(filePath, content, spinner = null) {
90
+ // Random Jitter Throttling (1s - 3s)
91
+ if (this.provider === 'gemini') {
92
+ const jitter = Math.floor(Math.random() * 2000) + 1000;
93
+ await sleep(jitter);
94
+ }
95
+ return limit(() => this.scanWithRetry(filePath, content, spinner));
96
+ }
97
+
98
+ async scanWithRetry(filePath, content, spinner, attempt = 1) {
99
+ const maxAttempts = 3;
100
+ try {
101
+ if (this.provider === 'gemini') {
102
+ return await this.scanWithGemini(content);
103
+ } else {
104
+ return await this.scanWithOllama(content);
105
+ }
106
+ } catch (error) {
107
+ const isRateLimit =
108
+ error.message.includes('429') ||
109
+ error.message.includes('Too Many Requests') ||
110
+ error.message.includes('Quota');
111
+
112
+ const isServerErr = error.message.includes('500') || error.message.includes('503');
113
+
114
+ if (isRateLimit && attempt <= maxAttempts) {
115
+ const waitTime = attempt === 1 ? 30 : 60;
116
+ if (spinner) {
117
+ const originalText = spinner.text;
118
+ for (let i = waitTime; i > 0; i--) {
119
+ // Only update once per second to prevent terminal flickering
120
+ if (spinner.text.includes(`${i}s`)) continue;
121
+ spinner.text = `❄️ Quota hit (${i}s). Waiting... (Tip: Ctrl+C -> 'vanguard config' to switch AI)`;
122
+ await sleep(1000);
123
+ }
124
+ spinner.text = originalText;
125
+ } else {
126
+ console.log(chalk.yellow(`\n⚠️ Rate limit hit. Cooling down for ${waitTime}s... (Tip: 'vanguard config' to switch AI)`));
127
+ await sleep(waitTime * 1000);
128
+ }
129
+ return this.scanWithRetry(filePath, content, spinner, attempt + 1);
130
+ } else if (isRateLimit) {
131
+ // Final failure after retries
132
+ console.log(chalk.blue('\n💡 Tip: Gemini Quota exceeded? Switch to local offline mode:'));
133
+ console.log(chalk.cyan(' vanguard config -> Select "Local Ollama"'));
134
+ }
135
+
136
+ const isOllamaError = error.message.includes('Ollama error') || error.message.includes('fetch failed');
137
+ if (isOllamaError) {
138
+ console.log(chalk.blue('\n💡 Tip: Local AI failing? Switch to Cloud fallback:'));
139
+ console.log(chalk.cyan(' vanguard config -> Select "Google Gemini"'));
140
+ }
141
+
142
+ const isAuthError =
143
+ error.message.includes('API key') ||
144
+ error.message.includes('400') ||
145
+ error.message.includes('PERMISSION_DENIED');
146
+
147
+ if (isAuthError) {
148
+ if (spinner) spinner.fail('Authentication Failed');
149
+ console.log(chalk.red('\n❌ Critical: Gemini API Key is invalid, expired, or missing permissions.'));
150
+ console.log(chalk.yellow('👉 Run "vanguard config" to update your credentials.'));
151
+ // We fail closed - do not allow the scan to proceed or return 'safe'
152
+ // process.exit(1) can cause UV_HANDLE_CLOSING assertion on Windows if async ops are pending.
153
+ // Instead, throw a specific error that the caller can catch and exit cleanly.
154
+ throw new Error('CRITICAL_AUTH_FAILURE');
155
+ }
156
+
157
+ if (isServerErr && attempt === 1) {
158
+ if (spinner) spinner.text = '🔄 AI Server hiccup. Retrying immediately...';
159
+ return this.scanWithRetry(filePath, content, spinner, attempt + 1);
160
+ }
161
+
162
+ throw error;
163
+ }
164
+ }
165
+
166
+ async scanWithGemini(content) {
167
+ const apiKey = config.get('GEMINI_KEY');
168
+ if (!apiKey) throw new Error('Gemini API Key missing. Run "vanguard config"');
169
+
170
+ if (!apiKey || apiKey.trim() === '') {
171
+ throw new Error('API key is missing.');
172
+ }
173
+ const genAI = new GoogleGenerativeAI(apiKey.trim());
174
+ // Use the latest stable pro flash model for the best performance/quota ratio
175
+ const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
176
+ const instruction = await this.getSystemInstruction();
177
+
178
+ const result = await model.generateContent({
179
+ contents: [{ role: 'user', parts: [{ text: `${instruction}\n\nFILE CONTENT:\n${content}` }] }],
180
+ });
181
+
182
+ const response = await result.response;
183
+ return this.parseResponse(response.text());
184
+ }
185
+
186
+ async scanWithOllama(content) {
187
+ const model = this.modelOverride || config.get('OLLAMA_MODEL');
188
+ const url = `${config.get('OLLAMA_URL')}/api/generate`;
189
+ const instruction = await this.getSystemInstruction();
190
+
191
+ const response = await fetch(url, {
192
+ method: 'POST',
193
+ headers: { 'Content-Type': 'application/json' },
194
+ body: JSON.stringify({
195
+ model: model,
196
+ prompt: `${instruction}\n\nFILE CONTENT:\n${content}`,
197
+ stream: false,
198
+ format: 'json',
199
+ }),
200
+ });
201
+
202
+ if (!response.ok) throw new Error(`Ollama error: ${response.statusText}`);
203
+ const data = await response.json();
204
+ return this.parseResponse(data.response);
205
+ }
206
+
207
+ parseResponse(text) {
208
+ try {
209
+ const start = text.indexOf('{');
210
+ const end = text.lastIndexOf('}');
211
+ if (start === -1 || end === -1) throw new Error('No JSON object found');
212
+ return JSON.parse(text.substring(start, end + 1));
213
+ } catch (e) {
214
+ throw new Error(`Invalid AI JSON format: ${e.message}`);
215
+ }
216
+ }
217
+ }
package/lib/utils/ui.js CHANGED
@@ -27,12 +27,13 @@ export function showAlert(analysis) {
27
27
  ? threats.map((t) => {
28
28
  const loc = t.file ? chalk.yellow(`[${t.file}${t.line ? `:${t.line}` : ''}] `) : '';
29
29
  const tag = t.threat ? chalk.red(`(${t.threat})`) : '';
30
- return `• ${loc}${tag}\n ${chalk.dim(t.reason || t)}`;
30
+ const reason = t.reason || (typeof t === 'string' ? t : 'Potential threat detected without specific details.');
31
+ return `• ${loc}${tag}\n ${chalk.dim(reason)}`;
31
32
  })
32
33
  : [chalk.dim('• No targeted threats identified.')]),
33
34
  '',
34
35
  chalk.bold('📝 Auditor Summary:'),
35
- summary,
36
+ typeof summary === 'string' ? summary : JSON.stringify(summary, null, 2),
36
37
  ].join('\n');
37
38
 
38
39
  console.log(
@@ -0,0 +1,41 @@
1
+ import fetch from 'node-fetch';
2
+ import chalk from 'chalk';
3
+ import boxen from 'boxen';
4
+
5
+ export async function checkForUpdates(currentVersion) {
6
+ try {
7
+ // We use a short timeout to prevent hanging the CLI if the network is slow
8
+ const response = await fetch('https://registry.npmjs.org/vanguard-cli/latest', {
9
+ timeout: 1500,
10
+ });
11
+
12
+ if (!response.ok) return;
13
+
14
+ const data = await response.json();
15
+ const latestVersion = data.version;
16
+
17
+ if (latestVersion && latestVersion !== currentVersion) {
18
+ const message = [
19
+ chalk.bold(`Update Available!`),
20
+ `${chalk.dim('Current:')} ${chalk.red(currentVersion)}`,
21
+ `${chalk.dim('Latest: ')} ${chalk.green(latestVersion)}`,
22
+ '',
23
+ chalk.cyan('Run the following to upgrade:'),
24
+ chalk.white.bgBlack(` npm install -g vanguard-cli@latest `),
25
+ ].join('\n');
26
+
27
+ console.log(
28
+ boxen(message, {
29
+ padding: 1,
30
+ margin: 1,
31
+ borderStyle: 'round',
32
+ borderColor: 'yellow',
33
+ title: '🚀 Vanguard Upgrade',
34
+ titleAlignment: 'center',
35
+ })
36
+ );
37
+ }
38
+ } catch (error) {
39
+ // Silently ignore update check failures to maintain UX
40
+ }
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanguard-cli",
3
- "version": "3.1.18",
3
+ "version": "3.1.20",
4
4
  "description": "AI-Powered Supply Chain Firewall for Git",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,79 @@
1
+ __ __ _ _ _ ____ _ _ _ ____ ____
2
+ \ \ / / / \ | \ | | / ___| | | | | / \ | _ \ | _ \
3
+ \ \ / / / _ \ | \| | | | _ | | | | / _ \ | |_) | | | | |
4
+ \ V / / ___ \ | |\ | | |_| | | |_| | / ___ \ | _ < | |_| |
5
+ \_/ /_/ \_\ |_| \_| \____| \___/ /_/ \_\ |_| \_\ |____/
6
+
7
+ 🛡️ Enterprise AI Supply Chain Firewall v3.1.19
8
+
9
+ ? 🛡️ Vanguard Enterprise Edition detected a CLONE. Select protection level: (Use
10
+ arrow keys)
11
+ > ΓÜí Quick Audit (Config & Entry Files)
12
+ 🛡️ Deep Audit (Full behavioral scanning)
13
+ ⚠️ Skip (Direct execution - RISKY) ? 🛡️ Vanguard Enterprise Edition detected a CLONE. Select protection level: ⚡
14
+ Quick Audit (Config & Entry Files)
15
+ - ⬇️ Isolating repository in sandbox...
16
+  ⚠️ Using Local Rules Only (Offline Fallback)
17
+
18
+ 📦 OSV.dev Dependency Audit:
19
+ ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
20
+ Γöé Dependency Γöé Status Γöé Vulnerabilities Γöé
21
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
22
+ Γöé @babel/core Γöé 200 Γöé 0 Γöé
23
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
24
+ │ @logux/eslint-con… │ 200 │ 0 │
25
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
26
+ Γöé @lukeed/uuid Γöé 200 Γöé 0 Γöé
27
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
28
+ Γöé @napi-rs/uuid Γöé 200 Γöé 0 Γöé
29
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
30
+ │ @originjs/vite-pl… │ 200 │ 0 │
31
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
32
+ Γöé @size-limit/file Γöé 200 Γöé 0 Γöé
33
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
34
+ │ @size-limit/webpa… │ 200 │ 0 │
35
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
36
+ Γöé @types/node Γöé 200 Γöé 0 Γöé
37
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
38
+ Γöé actions-up Γöé 200 Γöé 0 Γöé
39
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
40
+ Γöé better-node-test Γöé 200 Γöé 0 Γöé
41
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
42
+ Γöé c8 Γöé 200 Γöé 0 Γöé
43
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
44
+ Γöé clean-publish Γöé 200 Γöé 0 Γöé
45
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
46
+ Γöé eslint Γöé 200 Γöé 0 Γöé
47
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
48
+ Γöé rndm Γöé 200 Γöé 0 Γöé
49
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
50
+ │ secure-random-str… │ 200 │ 0 │
51
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
52
+ Γöé shortid Γöé 200 Γöé 0 Γöé
53
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
54
+ Γöé size-limit Γöé 200 Γöé 0 Γöé
55
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
56
+ Γöé terser Γöé 200 Γöé 0 Γöé
57
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
58
+ Γöé tinybench Γöé 200 Γöé 0 Γöé
59
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
60
+ Γöé uid Γöé 200 Γöé 0 Γöé
61
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
62
+ Γöé uid-safe Γöé 200 Γöé 0 Γöé
63
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
64
+ Γöé uuid Γöé 200 Γöé 0 Γöé
65
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
66
+ Γöé vite Γöé 200 Γöé 1 Γöé
67
+ ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
68
+
69
+ ΓÜí Quick Audit: 5/34 critical files targeted.
70
+ - 🧠 Auditing 5 files...
71
+ Γ£ö Audit Complete: 5 scanned (4 cached, 0 skipped).
72
+
73
+ ✅ Quick Audit Passed. Finalizing clone to test-nanoid-log...
74
+
75
+ ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
76
+ 🤝 Contribute: https://github.com/bazobehram/vanguard
77
+ Γÿò Support: https://www.buymeacoffee.com/bazobehram
78
+ ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
79
+
@@ -0,0 +1,107 @@
1
+ __ __ _ _ _ ____ _ _ _ ____ ____
2
+ \ \ / / / \ | \ | | / ___| | | | | / \ | _ \ | _ \
3
+ \ \ / / / _ \ | \| | | | _ | | | | / _ \ | |_) | | | | |
4
+ \ V / / ___ \ | |\ | | |_| | | |_| | / ___ \ | _ < | |_| |
5
+ \_/ /_/ \_\ |_| \_| \____| \___/ /_/ \_\ |_| \_\ |____/
6
+
7
+ 🛡️ Enterprise AI Supply Chain Firewall v3.1.19
8
+
9
+ ? 🛡️ Vanguard Enterprise Edition detected a CLONE. Select protection level: (Use
10
+ arrow keys)
11
+ > ΓÜí Quick Audit (Config & Entry Files)
12
+ 🛡️ Deep Audit (Full behavioral scanning)
13
+ ⚠️ Skip (Direct execution - RISKY) ? 🛡️ Vanguard Enterprise Edition detected a CLONE. Select protection level: ⚡
14
+ Quick Audit (Config & Entry Files)
15
+ - ⬇️ Isolating repository in sandbox...
16
+  ⚠️ Using Local Rules Only (Offline Fallback)
17
+
18
+ 📦 OSV.dev Dependency Audit:
19
+ ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
20
+ Γöé Dependency Γöé Status Γöé Vulnerabilities Γöé
21
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
22
+ │ @google/generativ… │ 200 │ 0 │
23
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
24
+ Γöé boxen Γöé 200 Γöé 0 Γöé
25
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
26
+ Γöé chalk Γöé 200 Γöé 0 Γöé
27
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
28
+ Γöé cli-table3 Γöé 200 Γöé 0 Γöé
29
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
30
+ Γöé commander Γöé 200 Γöé 0 Γöé
31
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
32
+ Γöé conf Γöé 200 Γöé 0 Γöé
33
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
34
+ Γöé crypto-js Γöé 200 Γöé 0 Γöé
35
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
36
+ Γöé figlet Γöé 200 Γöé 0 Γöé
37
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
38
+ Γöé fs-extra Γöé 200 Γöé 0 Γöé
39
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
40
+ Γöé inquirer Γöé 200 Γöé 0 Γöé
41
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
42
+ Γöé nanoid Γöé 200 Γöé 0 Γöé
43
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
44
+ Γöé node-fetch Γöé 200 Γöé 0 Γöé
45
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
46
+ Γöé ora Γöé 200 Γöé 0 Γöé
47
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
48
+ Γöé p-limit Γöé 200 Γöé 0 Γöé
49
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
50
+ Γöé simple-git Γöé 200 Γöé 0 Γöé
51
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
52
+ Γöé @eslint/js Γöé 200 Γöé 0 Γöé
53
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
54
+ │ @release-it/conve… │ 200 │ 0 │
55
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
56
+ Γöé eslint Γöé 200 Γöé 0 Γöé
57
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
58
+ │ eslint-config-pre… │ 200 │ 0 │
59
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
60
+ Γöé eslint-plugin-node Γöé 200 Γöé 0 Γöé
61
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
62
+ Γöé globals Γöé 200 Γöé 0 Γöé
63
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
64
+ Γöé nock Γöé 200 Γöé 0 Γöé
65
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
66
+ Γöé prettier Γöé 200 Γöé 0 Γöé
67
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
68
+ Γöé release-it Γöé 200 Γöé 0 Γöé
69
+ Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
70
+ Γöé vitest Γöé 200 Γöé 0 Γöé
71
+ ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
72
+
73
+ ΓÜí Quick Audit: 2/68 critical files targeted.
74
+ - 🧠 Auditing 2 files...
75
+ Γ£ö Audit Complete: 2 scanned (1 cached, 0 skipped).
76
+
77
+ ╔═══════════════════════════ ⚠️ HIGH RISK DETECTED ════════════════════════════╗
78
+ Γòæ Γòæ
79
+ Γòæ Verdict: BLOCK Γòæ
80
+ Γòæ Risk Score: 99/100 Γòæ
81
+ Γòæ Γòæ
82
+ ║ 🚨 Detailed Threat Audit: ║
83
+ Γòæ ΓÇó [test\fixtures\malicious\index.js:7] (Secret Exfiltration) Γòæ
84
+ Γòæ Reading and exfiltrating the contents of .env file to an external Γòæ
85
+ Γòæ endpoint. Γòæ
86
+ Γòæ ΓÇó [test\fixtures\malicious\index.js:14] (Obfuscation & Evasion) Γòæ
87
+ Γòæ Using Base64 encoding followed by eval() to execute obfuscated code. Γòæ
88
+ Γòæ ΓÇó [test\fixtures\malicious\index.js:23] (Spyware/Harvesting) Γòæ
89
+ Γòæ Collecting system and network information, which could be used for Γòæ
90
+ Γòæ unauthorized surveillance. Γòæ
91
+ Γòæ ΓÇó [test\fixtures\malicious\index.js:30] (Web3/Wallet Draining) Γòæ
92
+ Γòæ Simulating a fraudulent transaction request to drain cryptocurrency Γòæ
93
+ Γòæ wallets. Γòæ
94
+ Γòæ Γòæ
95
+ ║ 📝 Auditor Summary: ║
96
+ Γòæ Enterprise QUICK Audit identified multiple high-risk vectors. Γòæ
97
+ Γòæ Γòæ
98
+ ΓòÜΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓò¥
99
+
100
+
101
+ 🛡️ Malware Detected! Clone aborted. Your disk is safe.
102
+
103
+ ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
104
+ 🤝 Contribute: https://github.com/bazobehram/vanguard
105
+ Γÿò Support: https://www.buymeacoffee.com/bazobehram
106
+ ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
107
+