@7nsane/zift 1.0.4 → 1.0.6

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 (3) hide show
  1. package/README.md +5 -4
  2. package/bin/zift.js +102 -79
  3. package/package.json +3 -1
package/README.md CHANGED
@@ -19,14 +19,15 @@ npm install -g @7nsane/zift
19
19
  ## Usage
20
20
 
21
21
  ```bash
22
+ # NEW: Secure Installer Mode (Scan + Install)
23
+ @7nsane/zift install <package-name>
24
+ @7nsane/zift i <package-name>
25
+
22
26
  # Scan current directory
23
27
  @7nsane/zift .
24
28
 
25
- # Scan a specific package or directory
29
+ # Scan a specific folder
26
30
  @7nsane/zift ./node_modules/example-pkg
27
-
28
- # Output result in JSON format for CI/CD pipelines
29
- @7nsane/zift . --format json
30
31
  ```
31
32
 
32
33
  ## Rule Transparency
package/bin/zift.js CHANGED
@@ -13,162 +13,185 @@ async function main() {
13
13
  let format = 'text';
14
14
  let isInstallMode = false;
15
15
 
16
- // Verb detection: zift install <pkg> or zift i <pkg>
16
+ // 1. Setup Command
17
+ if (args[0] === 'setup') {
18
+ await runSetup();
19
+ return;
20
+ }
21
+
22
+ // 2. Installation Verbs
17
23
  if (args[0] === 'install' || args[0] === 'i') {
18
24
  isInstallMode = true;
19
- target = args[1] || '.';
25
+ target = args.find((a, i) => i > 0 && !a.startsWith('-')) || '.';
20
26
  }
21
27
 
22
- // Flag detection: zift <pkg> --zift (for future-proofing/aliases)
23
28
  if (args.includes('--zift')) {
24
29
  isInstallMode = true;
30
+ target = args.find(a => !a.startsWith('-') && !['install', 'i', 'npm'].includes(a)) || '.';
25
31
  }
26
32
 
27
- // Basic arg parsing for other flags
33
+ // 3. Flags
28
34
  for (let i = 0; i < args.length; i++) {
29
35
  if (args[i] === '--format' && args[i + 1]) {
30
36
  format = args[i + 1];
31
37
  i++;
32
- } else if (!args[i].startsWith('-') && !['install', 'i'].includes(args[i])) {
33
- target = args[i];
34
38
  }
35
39
  }
36
40
 
37
- // Determine if target is a local folder
41
+ // 4. No Args? Show Help
42
+ if (args.length === 0) {
43
+ showHelp();
44
+ return;
45
+ }
46
+
47
+ // 5. Execution
38
48
  const isLocal = fs.existsSync(target) && fs.lstatSync(target).isDirectory();
39
49
 
40
50
  if (isLocal) {
41
51
  await runLocalScan(target, format);
42
52
  } else {
43
- // Treat as remote package name
44
53
  await runRemoteAudit(target, format, isInstallMode);
45
54
  }
46
55
  }
47
56
 
57
+ async function runSetup() {
58
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
59
+ console.log(chalk.blue.bold('\n🛡️ Zift Secure Alias Setup'));
60
+ console.log(chalk.gray('Configure `npm install --zift` for automatic security audits.\n'));
61
+
62
+ const question = chalk.white('Add the Zift secure wrapper to your shell profile? (y/n): ');
63
+
64
+ rl.question(question, (answer) => {
65
+ rl.close();
66
+ if (['y', 'yes'].includes(answer.toLowerCase())) {
67
+ try {
68
+ if (os.platform() === 'win32') {
69
+ setupWindows();
70
+ } else {
71
+ setupUnix();
72
+ }
73
+ console.log(chalk.green('\n✅ Setup complete! Please RESTART your terminal.'));
74
+ console.log(chalk.gray('Try: npm install <pkg> --zift'));
75
+ } catch (e) {
76
+ console.error(chalk.red('\n❌ Setup failed: ') + e.message);
77
+ }
78
+ } else {
79
+ console.log(chalk.yellow('\nSetup cancelled.'));
80
+ }
81
+ });
82
+ }
83
+
84
+ function setupWindows() {
85
+ const psFunction = `
86
+ # Zift Secure Alias
87
+ function npm {
88
+ if ($args -contains "--zift") {
89
+ $pkg = $args | Where-Object { $_ -ne "install" -and $_ -ne "i" -and $_ -ne "--zift" } | Select-Object -First 1
90
+ Write-Host "\n🛡️ Zift: Intercepting installation for audit...\n" -ForegroundColor Green
91
+ npx @7nsane/zift@latest install $pkg
92
+ } else {
93
+ & (Get-Command npm.cmd).Definition @args
94
+ }
95
+ }
96
+ `;
97
+ const profilePath = cp.execSync('powershell -NoProfile -Command "echo $PROFILE"').toString().trim();
98
+ const profileDir = path.dirname(profilePath);
99
+ if (!fs.existsSync(profileDir)) fs.mkdirSync(profileDir, { recursive: true });
100
+ fs.appendFileSync(profilePath, psFunction);
101
+ }
102
+
103
+ function setupUnix() {
104
+ const bashFunction = `
105
+ # Zift Secure Alias
106
+ npm() {
107
+ if [[ "$*" == *"--zift"* ]]; then
108
+ pkg=$(echo "$@" | sed 's/install//g; s/ i //g; s/--zift//g' | xargs)
109
+ npx @7nsane/zift@latest install $pkg
110
+ else
111
+ command npm "$@"
112
+ fi
113
+ }
114
+ `;
115
+ const home = os.homedir();
116
+ const profiles = [path.join(home, '.bashrc'), path.join(home, '.zshrc')];
117
+ profiles.forEach(p => { if (fs.existsSync(p)) fs.appendFileSync(p, bashFunction); });
118
+ }
119
+
48
120
  async function runLocalScan(targetDir, format) {
49
121
  const scanner = new PackageScanner(targetDir);
50
- if (format === 'text') {
51
- console.log(chalk.blue(`\n🔍 Scanning local directory: ${path.resolve(targetDir)}`));
52
- }
53
-
122
+ if (format === 'text') console.log(chalk.blue(`\n🔍 Scanning local directory: ${path.resolve(targetDir)}`));
54
123
  try {
55
124
  const findings = await scanner.scan();
56
125
  handleFindings(findings, format, targetDir);
57
- } catch (err) {
58
- handleError(err, format);
59
- }
126
+ } catch (err) { handleError(err, format); }
60
127
  }
61
128
 
62
- async function runRemoteAudit(packageName, format, forceInstall = false) {
63
- if (format === 'text') {
64
- console.log(chalk.blue(`\n🌍 Remote Audit: Pre-scanning package '${packageName}'...`));
65
- }
66
-
129
+ async function runRemoteAudit(packageName, format, installOnSuccess) {
130
+ if (format === 'text') console.log(chalk.blue(`\n🌍 Remote Audit: Pre-scanning package '${packageName}'...`));
67
131
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'zift-audit-'));
68
-
69
132
  try {
70
133
  cp.execSync(`npm pack ${packageName}`, { cwd: tmpDir, stdio: 'ignore' });
71
134
  const tarball = fs.readdirSync(tmpDir).find(f => f.endsWith('.tgz'));
72
135
  cp.execSync(`tar -xzf ${tarball}`, { cwd: tmpDir });
73
-
74
136
  const scanPath = path.join(tmpDir, 'package');
75
137
  const scanner = new PackageScanner(scanPath);
76
138
  const findings = await scanner.scan();
77
-
78
- if (format === 'text') {
79
- const s = getSummary(findings);
80
- const statusText = s.Critical > 0 ? chalk.red.bold('CRITICAL RISK') :
81
- s.High > 0 ? chalk.red('HIGH RISK') :
82
- s.Medium > 0 ? chalk.yellow('WARNING') : chalk.green('SECURE');
83
-
84
- console.log(chalk.green(`✅ Audit of '${packageName}' complete. Status: ${statusText}`));
85
- }
86
-
87
139
  handleFindings(findings, format, scanPath, true);
88
140
 
89
141
  if (format === 'text') {
90
142
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
91
143
  const promptText = findings.length > 0
92
144
  ? chalk.yellow(`\n⚠️ Suspicious patterns found. Still install '${packageName}'? (y/n): `)
93
- : chalk.blue(`\nProceed with installation of '${packageName}'? (y/n): `);
145
+ : chalk.blue(`\nAudit passed. Proceed with installation of '${packageName}'? (y/n): `);
94
146
 
95
147
  rl.question(promptText, (answer) => {
96
148
  rl.close();
97
- const confirmed = ['y', 'yes'].includes(answer.toLowerCase());
98
-
99
- if (confirmed) {
149
+ if (['y', 'yes'].includes(answer.toLowerCase())) {
100
150
  console.log(chalk.blue(`\n📦 Installing ${packageName}...`));
101
- try {
102
- cp.execSync(`npm install ${packageName}`, { stdio: 'inherit' });
103
- console.log(chalk.green(`\n✅ ${packageName} installed successfully.`));
104
- } catch (e) { console.error(chalk.red(`\n❌ Installation failed.`)); }
105
- } else {
106
- console.log(chalk.red(`\n❌ Installation aborted by user.`));
151
+ cp.execSync(`npm install ${packageName}`, { stdio: 'inherit' });
152
+ console.log(chalk.green(`\n✅ ${packageName} installed successfully.`));
107
153
  }
108
154
  cleanupAndExit(tmpDir, 0);
109
155
  });
110
- } else {
111
- cleanupAndExit(tmpDir, 0);
112
- }
113
- } catch (err) {
114
- console.error(chalk.red(`\n❌ Audit failed: Ensure '${packageName}' is a valid npm package.`));
115
- cleanupAndExit(tmpDir, 1);
116
- }
156
+ } else { cleanupAndExit(tmpDir, 0); }
157
+ } catch (err) { cleanupAndExit(tmpDir, 1); }
117
158
  }
118
159
 
119
160
  function handleFindings(findings, format, targetDir, skipExit = false) {
120
161
  if (format === 'json') {
121
- process.stdout.write(JSON.stringify({ target: targetDir, findings, summary: getSummary(findings) }, null, 2));
162
+ process.stdout.write(JSON.stringify({ target: targetDir, findings, summary: { Critical: findings.filter(f => f.classification === 'Critical').length, High: findings.filter(f => f.classification === 'High').length, Medium: findings.filter(f => f.classification === 'Medium').length, Low: findings.filter(f => f.classification === 'Low').length } }, null, 2));
122
163
  if (!skipExit) process.exit(findings.some(f => f.score >= 90) ? 1 : 0);
123
164
  return;
124
165
  }
125
-
126
166
  if (findings.length === 0) {
127
- if (!skipExit) {
128
- console.log(chalk.green('\n✅ No suspicious patterns detected. All modules safe.'));
129
- process.exit(0);
130
- }
167
+ if (!skipExit) { console.log(chalk.green('\n✅ No suspicious patterns detected. All modules safe.')); process.exit(0); }
131
168
  return;
132
169
  }
133
-
134
- console.log(chalk.yellow(`\n⚠️ Suspicious patterns found:\n`));
135
170
  findings.forEach(f => {
136
171
  const color = { 'Critical': chalk.red.bold, 'High': chalk.red, 'Medium': chalk.yellow, 'Low': chalk.blue }[f.classification];
137
172
  console.log(color(`[${f.classification}] ${f.id} ${f.name} (Score: ${f.score})`));
138
173
  f.triggers.forEach(t => console.log(chalk.white(` - ${t.type} in ${t.file}:${t.line} [${t.context}]`)));
139
174
  console.log('');
140
175
  });
176
+ if (!skipExit) process.exit(findings[0].score >= 90 ? 1 : 0);
177
+ }
141
178
 
142
- printSummary(findings);
143
-
144
- if (!skipExit) {
145
- process.exit(findings[0].score >= 90 ? 1 : 0);
146
- }
179
+ function showHelp() {
180
+ console.log(chalk.blue.bold('\n🛡️ Zift - The Elite Security Scanner\n'));
181
+ console.log('Usage:');
182
+ console.log(' zift setup Configure secure npm wrapper');
183
+ console.log(' zift install <pkg> Audit and install package');
184
+ console.log(' zift . Scan local directory');
147
185
  }
148
186
 
149
187
  function cleanupAndExit(dir, code) {
150
188
  if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true });
151
- if (code !== undefined) process.exit(code);
189
+ process.exit(code);
152
190
  }
153
191
 
154
192
  function handleError(err, format) {
155
- if (format === 'json') console.error(JSON.stringify({ error: err.message }));
156
- else console.error(chalk.red(`\n❌ Fatal Error: ${err.message}`));
193
+ console.error(chalk.red(`\n❌ Error: ${err.message}`));
157
194
  process.exit(1);
158
195
  }
159
196
 
160
- function getSummary(findings) {
161
- const s = { Critical: 0, High: 0, Medium: 0, Low: 0 };
162
- findings.forEach(f => s[f.classification]++);
163
- return s;
164
- }
165
-
166
- function printSummary(findings) {
167
- const s = getSummary(findings);
168
- console.log(chalk.bold('Severity Summary:'));
169
- console.log(chalk.red(` Critical: ${s.Critical}\n High: ${s.High}`));
170
- console.log(chalk.yellow(` Medium: ${s.Medium}`));
171
- console.log(chalk.blue(` Low: ${s.Low}`));
172
- }
173
-
174
197
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@7nsane/zift",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "A high-performance, deterministic security scanner for npm packages.",
5
5
  "main": "src/scanner.js",
6
6
  "bin": {
@@ -24,7 +24,9 @@
24
24
  "dependencies": {
25
25
  "acorn": "^8.16.0",
26
26
  "acorn-walk": "^8.3.5",
27
+ "axios": "^1.13.6",
27
28
  "chalk": "^4.1.2",
29
+ "express": "^5.2.1",
28
30
  "glob": "^13.0.6"
29
31
  }
30
32
  }