@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.
- package/README.md +5 -4
- package/bin/zift.js +102 -79
- 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
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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,
|
|
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(`\
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
if (confirmed) {
|
|
149
|
+
if (['y', 'yes'].includes(answer.toLowerCase())) {
|
|
100
150
|
console.log(chalk.blue(`\n📦 Installing ${packageName}...`));
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
189
|
+
process.exit(code);
|
|
152
190
|
}
|
|
153
191
|
|
|
154
192
|
function handleError(err, format) {
|
|
155
|
-
|
|
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.
|
|
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
|
}
|