@7nsane/zift 1.0.4 ā 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.
- package/README.md +5 -4
- package/bin/zift.js +108 -59
- 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
|
|
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,57 +13,130 @@ 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'].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 or offer Setup
|
|
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('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
|
+
|
|
48
128
|
async function runLocalScan(targetDir, format) {
|
|
49
129
|
const scanner = new PackageScanner(targetDir);
|
|
50
|
-
if (format === 'text') {
|
|
51
|
-
console.log(chalk.blue(`\nš Scanning local directory: ${path.resolve(targetDir)}`));
|
|
52
|
-
}
|
|
130
|
+
if (format === 'text') console.log(chalk.blue(`\nš Scanning local directory: ${path.resolve(targetDir)}`));
|
|
53
131
|
|
|
54
132
|
try {
|
|
55
133
|
const findings = await scanner.scan();
|
|
56
134
|
handleFindings(findings, format, targetDir);
|
|
57
|
-
} catch (err) {
|
|
58
|
-
handleError(err, format);
|
|
59
|
-
}
|
|
135
|
+
} catch (err) { handleError(err, format); }
|
|
60
136
|
}
|
|
61
137
|
|
|
62
|
-
async function runRemoteAudit(packageName, format,
|
|
63
|
-
if (format === 'text') {
|
|
64
|
-
console.log(chalk.blue(`\nš Remote Audit: Pre-scanning package '${packageName}'...`));
|
|
65
|
-
}
|
|
66
|
-
|
|
138
|
+
async function runRemoteAudit(packageName, format, installOnSuccess) {
|
|
139
|
+
if (format === 'text') console.log(chalk.blue(`\nš Remote Audit: Pre-scanning package '${packageName}'...`));
|
|
67
140
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'zift-audit-'));
|
|
68
141
|
|
|
69
142
|
try {
|
|
@@ -74,44 +147,25 @@ async function runRemoteAudit(packageName, format, forceInstall = false) {
|
|
|
74
147
|
const scanPath = path.join(tmpDir, 'package');
|
|
75
148
|
const scanner = new PackageScanner(scanPath);
|
|
76
149
|
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
150
|
handleFindings(findings, format, scanPath, true);
|
|
88
151
|
|
|
89
152
|
if (format === 'text') {
|
|
90
153
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
91
154
|
const promptText = findings.length > 0
|
|
92
155
|
? chalk.yellow(`\nā ļø Suspicious patterns found. Still install '${packageName}'? (y/n): `)
|
|
93
|
-
: chalk.blue(`\
|
|
156
|
+
: chalk.blue(`\nAudit passed. Proceed with installation of '${packageName}'? (y/n): `);
|
|
94
157
|
|
|
95
158
|
rl.question(promptText, (answer) => {
|
|
96
159
|
rl.close();
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (confirmed) {
|
|
160
|
+
if (['y', 'yes'].includes(answer.toLowerCase())) {
|
|
100
161
|
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.`));
|
|
162
|
+
cp.execSync(`npm install ${packageName}`, { stdio: 'inherit' });
|
|
163
|
+
console.log(chalk.green(`\nā
${packageName} installed successfully.`));
|
|
107
164
|
}
|
|
108
165
|
cleanupAndExit(tmpDir, 0);
|
|
109
166
|
});
|
|
110
|
-
} else {
|
|
111
|
-
cleanupAndExit(tmpDir, 0);
|
|
112
|
-
}
|
|
167
|
+
} else { cleanupAndExit(tmpDir, 0); }
|
|
113
168
|
} catch (err) {
|
|
114
|
-
console.error(chalk.red(`\nā Audit failed: Ensure '${packageName}' is a valid npm package.`));
|
|
115
169
|
cleanupAndExit(tmpDir, 1);
|
|
116
170
|
}
|
|
117
171
|
}
|
|
@@ -122,38 +176,35 @@ function handleFindings(findings, format, targetDir, skipExit = false) {
|
|
|
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 safe.'));
|
|
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ā ļø Suspicious patterns found:\n`));
|
|
135
183
|
findings.forEach(f => {
|
|
136
184
|
const color = { 'Critical': chalk.red.bold, 'High': chalk.red, 'Medium': chalk.yellow, 'Low': chalk.blue }[f.classification];
|
|
137
185
|
console.log(color(`[${f.classification}] ${f.id} ${f.name} (Score: ${f.score})`));
|
|
138
186
|
f.triggers.forEach(t => console.log(chalk.white(` - ${t.type} in ${t.file}:${t.line} [${t.context}]`)));
|
|
139
187
|
console.log('');
|
|
140
188
|
});
|
|
141
|
-
|
|
142
189
|
printSummary(findings);
|
|
190
|
+
if (!skipExit) process.exit(findings[0].score >= 90 ? 1 : 0);
|
|
191
|
+
}
|
|
143
192
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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');
|
|
147
199
|
}
|
|
148
200
|
|
|
149
201
|
function cleanupAndExit(dir, code) {
|
|
150
202
|
if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true });
|
|
151
|
-
|
|
203
|
+
process.exit(code);
|
|
152
204
|
}
|
|
153
205
|
|
|
154
206
|
function handleError(err, format) {
|
|
155
|
-
|
|
156
|
-
else console.error(chalk.red(`\nā Fatal Error: ${err.message}`));
|
|
207
|
+
console.error(chalk.red(`\nā Error: ${err.message}`));
|
|
157
208
|
process.exit(1);
|
|
158
209
|
}
|
|
159
210
|
|
|
@@ -167,8 +218,6 @@ function printSummary(findings) {
|
|
|
167
218
|
const s = getSummary(findings);
|
|
168
219
|
console.log(chalk.bold('Severity Summary:'));
|
|
169
220
|
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
221
|
}
|
|
173
222
|
|
|
174
223
|
main();
|