vanguard-cli 3.1.6 → 3.1.10

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/bin/vanguard.js CHANGED
@@ -3,6 +3,7 @@
3
3
  import { Command } from 'commander';
4
4
  import inquirer from 'inquirer';
5
5
  import { runConfigWizard } from '../lib/commands/config.js';
6
+ import { runIntegrate } from '../lib/commands/integrate.js';
6
7
  import { handlePull, handleClone } from '../lib/commands/scan.js';
7
8
  import { showBanner, showFooter } from '../lib/utils/ui.js';
8
9
  import config from '../lib/utils/config.js';
@@ -10,6 +11,21 @@ import config from '../lib/utils/config.js';
10
11
  const program = new Command();
11
12
 
12
13
  async function handleAction(actionName, logicFn) {
14
+ const isConfigured =
15
+ (config.get('AI_PROVIDER') === 'gemini' && config.get('GEMINI_KEY')) ||
16
+ (config.get('AI_PROVIDER') === 'ollama');
17
+
18
+ if (!isConfigured) {
19
+ showBanner();
20
+ console.log(chalk.yellow(`šŸ›”ļø Vanguard V3 is not fully configured. Starting setup...\n`));
21
+ await runConfigWizard();
22
+ // After wizard, check again
23
+ if (!config.get('GEMINI_KEY') && config.get('AI_PROVIDER') === 'gemini') {
24
+ console.log(chalk.red('\nāŒ Configuration incomplete. Please provide a Gemini API key.'));
25
+ return;
26
+ }
27
+ }
28
+
13
29
  showBanner();
14
30
  const options = program.opts();
15
31
  config.set('VERBOSE', !!options.verbose);
@@ -28,11 +44,6 @@ async function handleAction(actionName, logicFn) {
28
44
  return;
29
45
  }
30
46
 
31
- if (config.get('AI_PROVIDER') === 'gemini' && !config.get('GEMINI_KEY')) {
32
- console.log('āŒ Gemini not configured.');
33
- await runConfigWizard();
34
- }
35
-
36
47
  await logicFn();
37
48
  showFooter();
38
49
  }
@@ -46,6 +57,8 @@ program
46
57
  .option('--clear-cache', 'Flush local audit cache');
47
58
 
48
59
  program.command('config').description('Configure AI providers').action(runConfigWizard);
60
+ program.command('integrate').description('Install "Invisible Protection" shell wrapper').action(runIntegrate);
61
+
49
62
 
50
63
  program
51
64
  .command('pull')
@@ -65,3 +78,18 @@ program
65
78
  });
66
79
 
67
80
  program.parse(process.argv);
81
+
82
+ // Default action: if no command provided, check config
83
+ if (!process.argv.slice(2).length) {
84
+ const isConfigured =
85
+ (config.get('AI_PROVIDER') === 'gemini' && config.get('GEMINI_KEY')) ||
86
+ (config.get('AI_PROVIDER') === 'ollama');
87
+
88
+ if (!isConfigured) {
89
+ showBanner();
90
+ console.log(chalk.yellow(`šŸ›”ļø Vanguard V3 is not fully configured. Starting setup...\n`));
91
+ await runConfigWizard();
92
+ } else {
93
+ program.help();
94
+ }
95
+ }
@@ -28,6 +28,8 @@ export async function runConfigWizard() {
28
28
  {
29
29
  type: 'password',
30
30
  name: 'apiKey',
31
+ message: 'Enter your Gemini API Key:',
32
+ default: config.get('GEMINI_KEY'),
31
33
  validate: (val) => {
32
34
  if (val.length === 0) return 'Key is required';
33
35
  if (val.startsWith('sk-')) return 'āš ļø That looks like an OpenAI key. Gemini keys usually start with "AIza"';
@@ -0,0 +1,139 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import chalk from 'chalk';
5
+ import { execSync } from 'child_process';
6
+ import { createSpinner } from '../utils/spinner.js';
7
+
8
+ const BASH_ZSH_TEMPLATE = `
9
+ # --- VANGUARD PROTECTION START ---
10
+ git() {
11
+ if [ "$1" = "clone" ] && [ -z "$VANGUARD_INTERNAL" ]; then
12
+ export VANGUARD_INTERNAL=1
13
+ vanguard clone "\${@:2}"
14
+ unset VANGUARD_INTERNAL
15
+ else
16
+ command git "$@"
17
+ fi
18
+ }
19
+ # --- VANGUARD PROTECTION END ---
20
+ `;
21
+
22
+ const FISH_TEMPLATE = `
23
+ # --- VANGUARD PROTECTION START ---
24
+ function git
25
+ if test "$argv[1]" = "clone"
26
+ vanguard clone $argv[2..-1]
27
+ else
28
+ command git $argv
29
+ end
30
+ end
31
+ # --- VANGUARD PROTECTION END ---
32
+ `;
33
+
34
+ const POWERSHELL_TEMPLATE = `
35
+ # --- VANGUARD PROTECTION START ---
36
+ function git {
37
+ if ($args[0] -eq "clone" -and !$env:VANGUARD_INTERNAL) {
38
+ $env:VANGUARD_INTERNAL = "1"
39
+ try {
40
+ vanguard clone @(if ($args.Length -gt 1) { $args[1..($args.Length-1)] })
41
+ } finally {
42
+ $env:VANGUARD_INTERNAL = $null
43
+ }
44
+ } else {
45
+ & (Get-Command git -CommandType Application) @args
46
+ }
47
+ }
48
+ # --- VANGUARD PROTECTION END ---
49
+ `;
50
+
51
+ const SCRIPTS = {
52
+ bash: { file: '.bashrc', template: BASH_ZSH_TEMPLATE },
53
+ zsh: { file: '.zshrc', template: BASH_ZSH_TEMPLATE },
54
+ fish: { file: '.config/fish/config.fish', template: FISH_TEMPLATE },
55
+ powershell: { file: 'Documents/PowerShell/Microsoft.PowerShell_profile.ps1', template: POWERSHELL_TEMPLATE }
56
+ };
57
+
58
+ export async function runIntegrate() {
59
+ console.log(chalk.bold('🐚 Vanguard Shell Integrator\n'));
60
+ const spinner = createSpinner('šŸ” Detecting shells and profiles...').start();
61
+
62
+ const home = os.homedir();
63
+ let detected = [];
64
+
65
+ // Check Unix shells
66
+ for (const [shell, info] of Object.entries(SCRIPTS)) {
67
+ if (shell === 'powershell') continue;
68
+ const profilePath = path.join(home, info.file);
69
+ if (await fs.pathExists(profilePath)) {
70
+ detected.push({ name: shell, path: profilePath, template: info.template });
71
+ }
72
+ }
73
+
74
+ // Check PowerShell with robust detection
75
+ const psProfiles = [];
76
+ if (os.platform() === 'win32') {
77
+ const documentsPath = path.join(home, 'Documents');
78
+ const oneDriveDocumentsPath = path.join(home, 'OneDrive', 'Documents');
79
+ const oneDriveBelgelerPath = path.join(home, 'OneDrive', 'Belgeler'); // User specific Turkish path
80
+
81
+ const possibleRoots = [documentsPath, oneDriveDocumentsPath, oneDriveBelgelerPath, home];
82
+ const psFolders = ['PowerShell', 'WindowsPowerShell'];
83
+
84
+ for (const root of possibleRoots) {
85
+ for (const folder of psFolders) {
86
+ const profile = path.join(root, folder, 'Microsoft.PowerShell_profile.ps1');
87
+ psProfiles.push(profile);
88
+ }
89
+ }
90
+ } else {
91
+ psProfiles.push(path.join(home, SCRIPTS.powershell.file));
92
+ }
93
+
94
+ for (const profilePath of psProfiles) {
95
+ if (await fs.pathExists(profilePath) || (os.platform() === 'win32' && profilePath.includes('PowerShell'))) {
96
+ if (!detected.some(d => d.path === profilePath)) {
97
+ detected.push({ name: 'powershell', path: profilePath, template: POWERSHELL_TEMPLATE });
98
+ }
99
+ }
100
+ }
101
+
102
+ if (detected.length === 0) {
103
+ spinner.fail('No supported shell profiles detected.');
104
+ return;
105
+ }
106
+
107
+ spinner.succeed(`Detected ${detected.length} shell profiles.`);
108
+
109
+ for (const shell of detected) {
110
+ const profilePath = shell.path;
111
+ const backupPath = `${profilePath}.backup`;
112
+
113
+ try {
114
+ // Backup
115
+ if (await fs.pathExists(profilePath)) {
116
+ await fs.copy(profilePath, backupPath);
117
+ console.log(chalk.dim(`šŸ’¾ Backup created: ${backupPath}`));
118
+ } else {
119
+ await fs.ensureDir(path.dirname(profilePath));
120
+ await fs.writeFile(profilePath, '');
121
+ }
122
+
123
+ const content = await fs.readFile(profilePath, 'utf-8');
124
+ if (content.includes('VANGUARD PROTECTION START')) {
125
+ // Update existing integration if template differs significantly
126
+ console.log(chalk.yellow(`ā© ${shell.name} already integrated. Updating wrapper...`));
127
+ const stripped = content.replace(/# --- VANGUARD PROTECTION START ---[\s\S]*?# --- VANGUARD PROTECTION END ---/g, '').trim();
128
+ await fs.writeFile(profilePath, stripped + '\n' + shell.template);
129
+ } else {
130
+ await fs.appendFile(profilePath, '\n' + shell.template);
131
+ }
132
+ console.log(chalk.green(`āœ… Integrated with ${shell.name} (${profilePath})`));
133
+ } catch (err) {
134
+ console.log(chalk.red(`āŒ Failed to integrate with ${shell.name}: ${err.message}`));
135
+ }
136
+ }
137
+
138
+ console.log(chalk.bold('\n✨ Invisible Protection Active! Restart your terminal to apply. šŸ›”ļø'));
139
+ }
@@ -142,6 +142,7 @@ export async function handleClone(url, directory, programOptions) {
142
142
  summary: 'Enterprise Deep Audit identified multiple high-risk vectors.',
143
143
  });
144
144
  await cleanupSandbox(tempPath);
145
+ console.log(chalk.red.bold('\nšŸ›”ļø Malware Detected! Clone aborted. Your disk is safe.'));
145
146
  } else {
146
147
  console.log(chalk.green(`\nāœ… Deep Audit Passed. Finalizing clone to ${targetPath}...`));
147
148
  await finalizeClone(tempPath, targetPath);
@@ -1,6 +1,8 @@
1
1
  import simpleGit from 'simple-git';
2
2
  import path from 'path';
3
- import fs from 'fs/promises';
3
+ import fs from 'fs-extra';
4
+ import os from 'os';
5
+ import { nanoid } from 'nanoid';
4
6
 
5
7
 
6
8
  export const git = simpleGit();
@@ -28,14 +30,15 @@ export async function mergeUpstream() {
28
30
  }
29
31
 
30
32
  /**
31
- * Clones into a temporary sandbox.
33
+ * Clones into a temporary sandbox in system temp directory.
32
34
  */
33
35
  export async function createSandboxClone(url) {
34
- const tempPath = path.join(process.cwd(), '.vanguard_temp');
36
+ const randomId = nanoid(8);
37
+ const tempPath = path.join(os.tmpdir(), `vanguard_${randomId}`);
35
38
 
36
- // Cleanup if exists
37
- await fs.rm(tempPath, { recursive: true, force: true }).catch(() => { });
38
- await fs.mkdir(tempPath, { recursive: true });
39
+ // Ensure fresh directory
40
+ await fs.remove(tempPath);
41
+ await fs.ensureDir(tempPath);
39
42
 
40
43
  const tempGit = simpleGit(tempPath);
41
44
  await tempGit.clone(url, '.');
@@ -44,23 +47,23 @@ export async function createSandboxClone(url) {
44
47
 
45
48
  /**
46
49
  * Moves files from sandbox to target and cleans up.
47
- * Improved for Windows EPERM stability.
50
+ * Uses fs-extra to handle cross-partition moves safely.
48
51
  */
49
52
  export async function finalizeClone(tempPath, targetPath) {
50
53
  const absTemp = path.resolve(tempPath);
51
54
  const absTarget = path.resolve(targetPath);
52
55
 
53
56
  try {
54
- await fs.rename(absTemp, absTarget);
57
+ // Ensure parent directory of target exists
58
+ await fs.ensureDir(path.dirname(absTarget));
59
+
60
+ // Use fs-extra move which handles cross-device moves automatically
61
+ await fs.move(absTemp, absTarget, { overwrite: true });
55
62
  } catch (e) {
56
- if (e.code === 'EPERM' || e.code === 'EXDEV' || e.code === 'EACCES') {
57
- // Fallback: Force purge target if it exists, then copy
58
- await fs.rm(absTarget, { recursive: true, force: true }).catch(() => { });
59
- await fs.cp(absTemp, absTarget, { recursive: true, force: true });
60
- await fs.rm(absTemp, { recursive: true, force: true });
61
- } else {
62
- throw e;
63
- }
63
+ throw new Error(`Failed to finalize clone: ${e.message}`);
64
+ } finally {
65
+ // Final cleanup attempt
66
+ await cleanupSandbox(tempPath);
64
67
  }
65
68
  }
66
69
 
@@ -68,5 +71,5 @@ export async function finalizeClone(tempPath, targetPath) {
68
71
  * Completely removes the sandbox.
69
72
  */
70
73
  export async function cleanupSandbox(tempPath) {
71
- await fs.rm(tempPath, { recursive: true, force: true }).catch(() => { });
74
+ await fs.remove(tempPath).catch(() => { });
72
75
  }
@@ -32,7 +32,7 @@ export class IntelligenceEngine {
32
32
  )
33
33
  );
34
34
  } catch {
35
- console.log(chalk.yellow(' āš ļø Using Local Rules Only (Offline Fallback)'));
35
+ console.log(chalk.yellow(' āš ļø Using Local Rules Only (Offline Fallback)'));
36
36
  }
37
37
  }
38
38
 
package/lib/utils/ui.js CHANGED
@@ -53,13 +53,13 @@ export function showFooter() {
53
53
  chalk.dim('\n----------------------------------------------------------------------')
54
54
  );
55
55
  console.log(
56
- chalk.yellow('⭐ Star on GitHub: ') + chalk.underline('https://github.com/bazob/vanguard')
56
+ chalk.yellow('⭐ Star on GitHub: ') + chalk.underline('https://github.com/bazobehram/vanguard')
57
57
  );
58
58
  console.log(
59
- chalk.green('šŸ¤ Contribute: ') + chalk.underline('https://github.com/bazob/vanguard/pulls')
59
+ chalk.green('šŸ¤ Contribute: ') + chalk.underline('https://github.com/bazobehram/vanguard/pulls')
60
60
  );
61
61
  console.log(
62
- chalk.cyan('ā˜• Support the project: ') +
62
+ chalk.cyan('ā˜• Support the project: ') +
63
63
  chalk.underline('https://www.buymeacoffee.com/bazobehram')
64
64
  );
65
65
  console.log(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanguard-cli",
3
- "version": "3.1.6",
3
+ "version": "3.1.10",
4
4
  "description": "AI-Powered Supply Chain Firewall for Git",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,7 @@
19
19
  "license": "MIT",
20
20
  "repository": {
21
21
  "type": "git",
22
- "url": "git+https://github.com/bazob/vanguard.git"
22
+ "url": "git+https://github.com/bazobehram/vanguard.git"
23
23
  },
24
24
  "dependencies": {
25
25
  "@google/generative-ai": "^0.21.0",
@@ -30,7 +30,9 @@
30
30
  "conf": "^12.0.0",
31
31
  "crypto-js": "^4.2.0",
32
32
  "figlet": "^1.8.0",
33
+ "fs-extra": "^11.3.3",
33
34
  "inquirer": "^8.2.4",
35
+ "nanoid": "^5.1.6",
34
36
  "node-fetch": "^2.7.0",
35
37
  "ora": "^8.1.1",
36
38
  "p-limit": "^7.2.0",
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 🚨 THREAT VECTOR: PROJECT_OMEGA (MOCK)
3
+ * This file contains the mock threat signature we added to lib/threats.json.
4
+ */
5
+
6
+ function initializeSystem() {
7
+ console.log("System initializing...");
8
+ }
9
+
10
+ // ALERT: PROJECT_OMEGA sequence initiated
11
+ const secretKey = "SG0uLi4uIHRoaXMgaXMgYSBzZWNyZXQ=";
12
+ initializeSystem();
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "vanguard-malware-lab",
3
+ "version": "1.0.0",
4
+ "description": "Educational test repository for supply chain security auditing.",
5
+ "scripts": {
6
+ "postinstall": "node ./scripts/postinstall.js"
7
+ },
8
+ "dependencies": {
9
+ "axios": "^1.6.0"
10
+ }
11
+ }