@7nsane/zift 1.0.3 → 1.0.5

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 +132 -109
  3. package/package.json +1 -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
@@ -11,190 +11,213 @@ async function main() {
11
11
  const args = process.argv.slice(2);
12
12
  let target = '.';
13
13
  let format = 'text';
14
+ let isInstallMode = false;
14
15
 
15
- // Basic arg parsing
16
+ // 1. Setup Command
17
+ if (args[0] === 'setup') {
18
+ await runSetup();
19
+ return;
20
+ }
21
+
22
+ // 2. Installation Verbs
23
+ if (args[0] === 'install' || args[0] === 'i') {
24
+ isInstallMode = true;
25
+ target = args.find((a, i) => i > 0 && !a.startsWith('-')) || '.';
26
+ }
27
+
28
+ if (args.includes('--zift')) {
29
+ isInstallMode = true;
30
+ target = args.find(a => !a.startsWith('-') && !['install', 'i'].includes(a)) || '.';
31
+ }
32
+
33
+ // 3. Flags
16
34
  for (let i = 0; i < args.length; i++) {
17
35
  if (args[i] === '--format' && args[i + 1]) {
18
36
  format = args[i + 1];
19
37
  i++;
20
- } else if (!args[i].startsWith('-')) {
21
- target = args[i];
22
38
  }
23
39
  }
24
40
 
25
- // Determine if target is local path or remote package
41
+ // 4. No Args? Show Help or offer Setup
42
+ if (args.length === 0) {
43
+ showHelp();
44
+ return;
45
+ }
46
+
47
+ // 5. Execution
26
48
  const isLocal = fs.existsSync(target) && fs.lstatSync(target).isDirectory();
27
49
 
28
50
  if (isLocal) {
29
51
  await runLocalScan(target, format);
30
52
  } else {
31
- // Treat as remote package name
32
- await runRemoteAudit(target, format);
53
+ await runRemoteAudit(target, format, isInstallMode);
33
54
  }
34
55
  }
35
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('This will allow you to use `npm install <pkg> --zift` for secure audits.\n'));
61
+
62
+ const question = chalk.white('Would you like to add the Zift secure alias 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 to use the new command.'));
74
+ } catch (e) {
75
+ console.error(chalk.red('\n❌ Setup failed: ') + e.message);
76
+ }
77
+ } else {
78
+ console.log(chalk.yellow('\nSetup cancelled. You can always run `zift setup` later.'));
79
+ }
80
+ });
81
+ }
82
+
83
+ function setupWindows() {
84
+ // Define PowerShell function
85
+ const psFunction = `
86
+ # Zift Secure Alias
87
+ function npm-secure {
88
+ if ($args -contains "--zift") {
89
+ $pkg = $args | Where-Object { $_ -ne "install" -and $_ -ne "i" -and $_ -ne "--zift" } | Select-Object -First 1
90
+ npx @7nsane/zift install $pkg
91
+ } else {
92
+ npm.cmd @args
93
+ }
94
+ }
95
+ if (!(Test-Path alias:npm)) { Set-Alias npm npm-secure -Force -Scope Global }
96
+ `;
97
+
98
+ // Get PS Profile path
99
+ const profilePath = cp.execSync('powershell -NoProfile -Command "echo $PROFILE"').toString().trim();
100
+ const profileDir = path.dirname(profilePath);
101
+
102
+ if (!fs.existsSync(profileDir)) fs.mkdirSync(profileDir, { recursive: true });
103
+ fs.appendFileSync(profilePath, psFunction);
104
+ }
105
+
106
+ function setupUnix() {
107
+ const bashFunction = `
108
+ # Zift Secure Alias
109
+ npm() {
110
+ if [[ "$*" == *"--zift"* ]]; then
111
+ pkg=$(echo "$@" | sed 's/install//g; s/ i //g; s/--zift//g' | xargs)
112
+ npx @7nsane/zift install $pkg
113
+ else
114
+ command npm "$@"
115
+ fi
116
+ }
117
+ `;
118
+ const home = os.homedir();
119
+ const profiles = [path.join(home, '.bashrc'), path.join(home, '.zshrc')];
120
+
121
+ profiles.forEach(p => {
122
+ if (fs.existsSync(p)) {
123
+ fs.appendFileSync(p, bashFunction);
124
+ }
125
+ });
126
+ }
127
+
36
128
  async function runLocalScan(targetDir, format) {
37
129
  const scanner = new PackageScanner(targetDir);
38
- if (format === 'text') {
39
- process.stdout.write(chalk.blue(`\n🔍 Scanning local directory at ${path.resolve(targetDir)}...\n`));
40
- }
130
+ if (format === 'text') console.log(chalk.blue(`\n🔍 Scanning local directory: ${path.resolve(targetDir)}`));
41
131
 
42
132
  try {
43
133
  const findings = await scanner.scan();
44
134
  handleFindings(findings, format, targetDir);
45
- } catch (err) {
46
- handleError(err, format);
47
- }
135
+ } catch (err) { handleError(err, format); }
48
136
  }
49
137
 
50
- async function runRemoteAudit(packageName, format) {
51
- if (format === 'text') {
52
- console.log(chalk.blue(`\n🌍 Remote Audit: Attempting to pre-scan package '${packageName}'...`));
53
- }
54
-
138
+ async function runRemoteAudit(packageName, format, installOnSuccess) {
139
+ if (format === 'text') console.log(chalk.blue(`\n🌍 Remote Audit: Pre-scanning package '${packageName}'...`));
55
140
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'zift-audit-'));
56
141
 
57
142
  try {
58
- // Download tarball
59
143
  cp.execSync(`npm pack ${packageName}`, { cwd: tmpDir, stdio: 'ignore' });
60
144
  const tarball = fs.readdirSync(tmpDir).find(f => f.endsWith('.tgz'));
61
-
62
- // Extract
63
145
  cp.execSync(`tar -xzf ${tarball}`, { cwd: tmpDir });
64
- const scanPath = path.join(tmpDir, 'package');
65
146
 
147
+ const scanPath = path.join(tmpDir, 'package');
66
148
  const scanner = new PackageScanner(scanPath);
67
149
  const findings = await scanner.scan();
68
-
69
- // Custom reporting for remote audit
70
- if (format === 'text') {
71
- console.log(chalk.green(`✅ Pre-scan of '${packageName}' complete.`));
72
- const s = getSummary(findings);
73
- console.log(chalk.bold('Risk Profile: ') +
74
- (s.Critical > 0 ? chalk.red.bold('CRITICAL') :
75
- s.High > 0 ? chalk.red('HIGH') :
76
- s.Medium > 0 ? chalk.yellow('MEDIUM') : chalk.green('SECURE')));
77
- }
78
-
79
150
  handleFindings(findings, format, scanPath, true);
80
151
 
81
- // Interactive Prompt
82
152
  if (format === 'text') {
83
153
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
84
- const question = findings.length > 0
85
- ? chalk.yellow(`\n⚠️ Suspicious patterns found. Still install '${packageName}'? (yes/no): `)
86
- : chalk.blue(`\nProceed with installation of '${packageName}'? (yes/no): `);
154
+ const promptText = findings.length > 0
155
+ ? chalk.yellow(`\n⚠️ Suspicious patterns found. Still install '${packageName}'? (y/n): `)
156
+ : chalk.blue(`\nAudit passed. Proceed with installation of '${packageName}'? (y/n): `);
87
157
 
88
- rl.question(question, (answer) => {
158
+ rl.question(promptText, (answer) => {
89
159
  rl.close();
90
- if (answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y') {
160
+ if (['y', 'yes'].includes(answer.toLowerCase())) {
91
161
  console.log(chalk.blue(`\n📦 Installing ${packageName}...`));
92
- try {
93
- cp.execSync(`npm install ${packageName}`, { stdio: 'inherit' });
94
- console.log(chalk.green(`\n✅ ${packageName} installed successfully.`));
95
- } catch (e) {
96
- console.error(chalk.red(`\n❌ Installation failed.`));
97
- }
98
- cleanupAndExit(tmpDir, 0);
99
- } else {
100
- console.log(chalk.red(`\n❌ Installation aborted by user.`));
101
- cleanupAndExit(tmpDir, 0);
162
+ cp.execSync(`npm install ${packageName}`, { stdio: 'inherit' });
163
+ console.log(chalk.green(`\n✅ ${packageName} installed successfully.`));
102
164
  }
165
+ cleanupAndExit(tmpDir, 0);
103
166
  });
104
- } else {
105
- cleanupAndExit(tmpDir, 0);
106
- }
107
-
167
+ } else { cleanupAndExit(tmpDir, 0); }
108
168
  } catch (err) {
109
- console.error(chalk.red(`\n❌ Remote Audit failed: Ensure '${packageName}' exists on npm.`));
110
169
  cleanupAndExit(tmpDir, 1);
111
170
  }
112
171
  }
113
172
 
114
173
  function handleFindings(findings, format, targetDir, skipExit = false) {
115
174
  if (format === 'json') {
116
- console.log(JSON.stringify({
117
- target: targetDir,
118
- timestamp: new Date().toISOString(),
119
- findings: findings,
120
- summary: getSummary(findings)
121
- }, null, 2));
175
+ process.stdout.write(JSON.stringify({ target: targetDir, findings, summary: getSummary(findings) }, null, 2));
122
176
  if (!skipExit) process.exit(findings.some(f => f.score >= 90) ? 1 : 0);
123
177
  return;
124
178
  }
125
-
126
179
  if (findings.length === 0) {
127
- if (!skipExit) {
128
- console.log(chalk.green('\n✅ No suspicious patterns detected. All modules within safety thresholds.'));
129
- process.exit(0);
130
- }
180
+ if (!skipExit) { console.log(chalk.green('\n✅ No suspicious patterns detected. All modules safe.')); process.exit(0); }
131
181
  return;
132
182
  }
133
-
134
- console.log(chalk.yellow(`\n⚠️ Found ${findings.length} suspicious patterns.\n`));
135
-
136
- findings.forEach(finding => {
137
- const colorMap = { 'Critical': chalk.red.bold, 'High': chalk.red, 'Medium': chalk.yellow, 'Low': chalk.blue };
138
- const theme = colorMap[finding.classification] || chalk.white;
139
-
140
- console.log(theme(`[${finding.classification}] ${finding.id} ${finding.name} (Risk Score: ${finding.score})`));
141
- console.log(chalk.gray(`Description: ${finding.description}`));
142
-
143
- finding.triggers.forEach(t => {
144
- console.log(chalk.white(` - ${t.type} in ${t.file}:${t.line} [${t.context}]`));
145
- });
146
-
147
- if (finding.isLifecycle) {
148
- console.log(chalk.magenta(` Context: Multiplier applied due to execution in lifecycle script.`));
149
- }
183
+ findings.forEach(f => {
184
+ const color = { 'Critical': chalk.red.bold, 'High': chalk.red, 'Medium': chalk.yellow, 'Low': chalk.blue }[f.classification];
185
+ console.log(color(`[${f.classification}] ${f.id} ${f.name} (Score: ${f.score})`));
186
+ f.triggers.forEach(t => console.log(chalk.white(` - ${t.type} in ${t.file}:${t.line} [${t.context}]`)));
150
187
  console.log('');
151
188
  });
152
-
153
189
  printSummary(findings);
190
+ if (!skipExit) process.exit(findings[0].score >= 90 ? 1 : 0);
191
+ }
154
192
 
155
- if (!skipExit) {
156
- const highestScore = findings.length > 0 ? findings[0].score : 0;
157
- if (highestScore >= 90) {
158
- console.log(chalk.red.bold(`\n❌ FAILED SAFETY CHECK: Critical risk detected (Score: ${highestScore})\n`));
159
- process.exit(1);
160
- } else {
161
- console.log(chalk.green(`\n✔ Safety check completed with minor warnings.\n`));
162
- process.exit(0);
163
- }
164
- }
193
+ function showHelp() {
194
+ console.log(chalk.blue.bold('\n🛡️ Zift - The Elite Security Scanner\n'));
195
+ console.log('Usage:');
196
+ console.log(' zift setup Configure secure npm aliases');
197
+ console.log(' zift install <pkg> Scan and prompt before installing');
198
+ console.log(' zift . Scan current directory');
165
199
  }
166
200
 
167
201
  function cleanupAndExit(dir, code) {
168
202
  if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true });
169
- if (code !== undefined) process.exit(code);
203
+ process.exit(code);
170
204
  }
171
205
 
172
206
  function handleError(err, format) {
173
- if (format === 'json') {
174
- console.error(JSON.stringify({ error: err.message }));
175
- } else {
176
- console.error(chalk.red(`\n❌ Fatal Error: ${err.message}`));
177
- }
207
+ console.error(chalk.red(`\n❌ Error: ${err.message}`));
178
208
  process.exit(1);
179
209
  }
180
210
 
181
211
  function getSummary(findings) {
182
- const summary = { Critical: 0, High: 0, Medium: 0, Low: 0 };
183
- findings.forEach(f => {
184
- if (summary[f.classification] !== undefined) {
185
- summary[f.classification]++;
186
- }
187
- });
188
- return summary;
212
+ const s = { Critical: 0, High: 0, Medium: 0, Low: 0 };
213
+ findings.forEach(f => s[f.classification]++);
214
+ return s;
189
215
  }
190
216
 
191
217
  function printSummary(findings) {
192
218
  const s = getSummary(findings);
193
219
  console.log(chalk.bold('Severity Summary:'));
194
- console.log(chalk.red(` Critical: ${s.Critical}`));
195
- console.log(chalk.red(` High: ${s.High}`));
196
- console.log(chalk.yellow(` Medium: ${s.Medium}`));
197
- console.log(chalk.blue(` Low: ${s.Low}`));
220
+ console.log(chalk.red(` Critical: ${s.Critical}\n High: ${s.High}`));
198
221
  }
199
222
 
200
223
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@7nsane/zift",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "A high-performance, deterministic security scanner for npm packages.",
5
5
  "main": "src/scanner.js",
6
6
  "bin": {