@7nsane/zift 2.0.0 → 2.2.0
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 +4 -0
- package/package.json +1 -1
- package/src/collector.js +63 -18
- package/src/engine.js +17 -16
- package/src/rules/definitions.js +68 -5
- package/src/scanner.js +13 -1
- package/v22_output.txt +12 -0
- package/v22_output_fixed.txt +16 -0
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# 🛡️ Zift (v2.0.0)
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@7nsane/zift)
|
|
4
|
+
[](https://www.npmjs.com/package/@7nsane/zift)
|
|
5
|
+
[](https://github.com/7nsane/zift)
|
|
6
|
+
|
|
3
7
|
**The Deterministic Pre-install Security Gate for JavaScript Projects.** By using deterministic AST analysis and lightweight variable propagation, Zift identifies potential credential exfiltration, malicious persistence, and obfuscated execution with extreme precision.
|
|
4
8
|
|
|
5
9
|
## Installation
|
package/package.json
CHANGED
package/src/collector.js
CHANGED
|
@@ -12,16 +12,23 @@ class ASTCollector {
|
|
|
12
12
|
collect(code, filePath) {
|
|
13
13
|
const facts = {
|
|
14
14
|
ENV_READ: [],
|
|
15
|
+
MASS_ENV_ACCESS: [],
|
|
15
16
|
FILE_READ_SENSITIVE: [],
|
|
16
17
|
NETWORK_SINK: [],
|
|
18
|
+
DNS_SINK: [],
|
|
19
|
+
RAW_SOCKET_SINK: [],
|
|
17
20
|
DYNAMIC_EXECUTION: [],
|
|
21
|
+
DYNAMIC_REQUIRE: [],
|
|
18
22
|
OBFUSCATION: [],
|
|
19
23
|
FILE_WRITE_STARTUP: [],
|
|
20
24
|
SHELL_EXECUTION: [],
|
|
21
|
-
ENCODER_USE: []
|
|
25
|
+
ENCODER_USE: [],
|
|
26
|
+
REMOTE_FETCH_SIGNAL: [],
|
|
27
|
+
PIPE_TO_SHELL_SIGNAL: []
|
|
22
28
|
};
|
|
23
29
|
const flows = [];
|
|
24
30
|
const sourceCode = code;
|
|
31
|
+
let envAccessCount = 0;
|
|
25
32
|
|
|
26
33
|
let ast;
|
|
27
34
|
try {
|
|
@@ -60,16 +67,16 @@ class ASTCollector {
|
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
if (calleeCode === 'require' && node.arguments.length > 0 && node.arguments[0].type !== 'Literal') {
|
|
63
|
-
facts.
|
|
70
|
+
facts.DYNAMIC_REQUIRE.push({
|
|
64
71
|
file: filePath,
|
|
65
72
|
line: node.loc.start.line,
|
|
66
|
-
type: 'dynamic_require',
|
|
67
73
|
variable: sourceCode.substring(node.arguments[0].start, node.arguments[0].end)
|
|
68
74
|
});
|
|
69
75
|
}
|
|
70
76
|
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
const netType = this.getNetworkType(calleeCode);
|
|
78
|
+
if (netType) {
|
|
79
|
+
facts[netType].push({
|
|
73
80
|
file: filePath,
|
|
74
81
|
line: node.loc.start.line,
|
|
75
82
|
callee: calleeCode
|
|
@@ -82,6 +89,19 @@ class ASTCollector {
|
|
|
82
89
|
line: node.loc.start.line,
|
|
83
90
|
callee: calleeCode
|
|
84
91
|
});
|
|
92
|
+
|
|
93
|
+
// Signal Analysis: Dropper Patterns
|
|
94
|
+
node.arguments.forEach(arg => {
|
|
95
|
+
if (arg.type === 'Literal' && typeof arg.value === 'string') {
|
|
96
|
+
const val = arg.value.toLowerCase();
|
|
97
|
+
if ((val.includes('curl') || val.includes('wget') || val.includes('fetch')) && (val.includes('http') || val.includes('//'))) {
|
|
98
|
+
facts.REMOTE_FETCH_SIGNAL.push({ file: filePath, line: node.loc.start.line, context: val });
|
|
99
|
+
}
|
|
100
|
+
if (val.includes('| sh') || val.includes('| bash') || val.includes('| cmd') || val.includes('| pwsh')) {
|
|
101
|
+
facts.PIPE_TO_SHELL_SIGNAL.push({ file: filePath, line: node.loc.start.line, context: val });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
85
105
|
}
|
|
86
106
|
|
|
87
107
|
if (this.isEncoder(calleeCode)) {
|
|
@@ -100,9 +120,16 @@ class ASTCollector {
|
|
|
100
120
|
});
|
|
101
121
|
}
|
|
102
122
|
|
|
123
|
+
if (this.isStartupFileWrite(calleeCode, node, sourceCode)) {
|
|
124
|
+
facts.FILE_WRITE_STARTUP.push({
|
|
125
|
+
file: filePath,
|
|
126
|
+
line: node.loc.start.line,
|
|
127
|
+
path: node.arguments[0] ? sourceCode.substring(node.arguments[0].start, node.arguments[0].end) : 'unknown'
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
103
131
|
node.arguments.forEach((arg, index) => {
|
|
104
132
|
const argCode = sourceCode.substring(arg.start, arg.end);
|
|
105
|
-
// Improved check: Does the expression contain any variable we know is tainted?
|
|
106
133
|
const isArgTainted = argCode.includes('process.env') || flows.some(f => {
|
|
107
134
|
const regex = new RegExp(`\\b${f.toVar}\\b`);
|
|
108
135
|
return regex.test(argCode);
|
|
@@ -129,11 +156,16 @@ class ASTCollector {
|
|
|
129
156
|
const whitelist = ['NODE_ENV', 'TIMING', 'DEBUG', 'VERBOSE', 'CI', 'APPDATA', 'HOME', 'USERPROFILE', 'PATH', 'PWD'];
|
|
130
157
|
if (whitelist.includes(property)) return;
|
|
131
158
|
|
|
159
|
+
envAccessCount++;
|
|
132
160
|
facts.ENV_READ.push({
|
|
133
161
|
file: filePath,
|
|
134
162
|
line: node.loc.start.line,
|
|
135
163
|
variable: property ? `process.env.${property}` : 'process.env'
|
|
136
164
|
});
|
|
165
|
+
|
|
166
|
+
if (envAccessCount > 5) {
|
|
167
|
+
facts.MASS_ENV_ACCESS.push({ file: filePath, line: node.loc.start.line, count: envAccessCount });
|
|
168
|
+
}
|
|
137
169
|
}
|
|
138
170
|
},
|
|
139
171
|
VariableDeclarator: (node) => {
|
|
@@ -183,20 +215,21 @@ class ASTCollector {
|
|
|
183
215
|
return { facts, flows };
|
|
184
216
|
}
|
|
185
217
|
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
218
|
+
getNetworkType(calleeCode) {
|
|
219
|
+
const dnsSinks = ['dns.lookup', 'dns.resolve', 'dns.resolve4', 'dns.resolve6'];
|
|
220
|
+
const rawSocketSinks = ['net.connect', 'net.createConnection'];
|
|
221
|
+
const networkSinks = ['http.request', 'https.request', 'http.get', 'https.get', 'fetch', 'axios', 'request'];
|
|
222
|
+
|
|
223
|
+
if (dnsSinks.some(sink => calleeCode === sink || calleeCode.endsWith('.' + sink))) return 'DNS_SINK';
|
|
224
|
+
if (rawSocketSinks.some(sink => calleeCode === sink || calleeCode.endsWith('.' + sink))) return 'RAW_SOCKET_SINK';
|
|
225
|
+
if (networkSinks.some(sink => {
|
|
194
226
|
if (calleeCode === sink) return true;
|
|
195
227
|
if (calleeCode.endsWith('.' + sink)) return true;
|
|
196
|
-
// Catch cases like require('https').get
|
|
197
228
|
if (sink.includes('.') && calleeCode.endsWith(sink.split('.')[1]) && calleeCode.includes(sink.split('.')[0])) return true;
|
|
198
229
|
return false;
|
|
199
|
-
})
|
|
230
|
+
})) return 'NETWORK_SINK';
|
|
231
|
+
|
|
232
|
+
return null;
|
|
200
233
|
}
|
|
201
234
|
|
|
202
235
|
isShellSink(calleeCode) {
|
|
@@ -210,7 +243,7 @@ class ASTCollector {
|
|
|
210
243
|
}
|
|
211
244
|
|
|
212
245
|
isEncoder(calleeCode) {
|
|
213
|
-
const encoders = ['Buffer.from', 'btoa', 'atob'];
|
|
246
|
+
const encoders = ['Buffer.from', 'btoa', 'atob', 'zlib.deflate', 'zlib.gzip', 'crypto.createCipheriv'];
|
|
214
247
|
return encoders.some(enc => calleeCode === enc || calleeCode.endsWith('.' + enc));
|
|
215
248
|
}
|
|
216
249
|
|
|
@@ -235,12 +268,24 @@ class ASTCollector {
|
|
|
235
268
|
|
|
236
269
|
if (node.arguments.length > 0 && node.arguments[0].type === 'Literal') {
|
|
237
270
|
const pathValue = String(node.arguments[0].value);
|
|
238
|
-
const sensitive = ['.ssh', '.env', 'shadow', 'passwd', 'credentials', 'token'];
|
|
271
|
+
const sensitive = ['.ssh', '.env', 'shadow', 'passwd', 'credentials', 'token', '_netrc', 'aws_access_key'];
|
|
239
272
|
return sensitive.some((s) => pathValue.toLowerCase().includes(s));
|
|
240
273
|
}
|
|
241
274
|
return false;
|
|
242
275
|
}
|
|
243
276
|
|
|
277
|
+
isStartupFileWrite(calleeCode, node, sourceCode) {
|
|
278
|
+
if (!calleeCode.includes('fs.writeFile') && !calleeCode.includes('fs.writeFileSync') &&
|
|
279
|
+
!calleeCode.includes('fs.appendFile')) return false;
|
|
280
|
+
|
|
281
|
+
if (node.arguments.length > 0 && node.arguments[0].type === 'Literal') {
|
|
282
|
+
const pathValue = String(node.arguments[0].value);
|
|
283
|
+
const startup = ['package.json', '.npmrc', '.bashrc', '.zshrc', 'crontab', 'init.d', 'systemd'];
|
|
284
|
+
return startup.some((s) => pathValue.toLowerCase().includes(s));
|
|
285
|
+
}
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
244
289
|
getSourceCode(node) {
|
|
245
290
|
return this.sourceCode.substring(node.start, node.end);
|
|
246
291
|
}
|
package/src/engine.js
CHANGED
|
@@ -6,7 +6,7 @@ class SafetyEngine {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
evaluate(packageFacts, lifecycleFiles) {
|
|
9
|
-
|
|
9
|
+
let findings = [];
|
|
10
10
|
|
|
11
11
|
// Process each rule
|
|
12
12
|
for (const rule of RULES) {
|
|
@@ -16,6 +16,9 @@ class SafetyEngine {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
// Sort by score (desc) and then by priority (desc)
|
|
20
|
+
findings.sort((a, b) => (b.score - a.score) || (b.priority - a.priority));
|
|
21
|
+
|
|
19
22
|
return findings;
|
|
20
23
|
}
|
|
21
24
|
|
|
@@ -29,12 +32,9 @@ class SafetyEngine {
|
|
|
29
32
|
for (const req of rule.requires) {
|
|
30
33
|
let matchedFacts = facts[req] || [];
|
|
31
34
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
} else if (req === 'DYNAMIC_EXECUTION') {
|
|
36
|
-
matchedFacts = matchedFacts.filter(f => f.type !== 'dynamic_require');
|
|
37
|
-
}
|
|
35
|
+
// Specialist Rule: Startup Mod (ZFT-012) requires specific file paths (now explicit in definitions but engine may still help)
|
|
36
|
+
// But per review, we should aim for explicit facts.
|
|
37
|
+
// ZFT-012 now just requires FILE_WRITE_STARTUP. Simple.
|
|
38
38
|
|
|
39
39
|
if (matchedFacts.length === 0) return null; // Rule not matched
|
|
40
40
|
triggers.push(...matchedFacts.map(f => ({ ...f, type: req })));
|
|
@@ -51,10 +51,10 @@ class SafetyEngine {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
//
|
|
55
|
-
const isInLifecycle = triggers.some(t => lifecycleFiles.has(t.file));
|
|
54
|
+
// Check for Lifecycle Context Fact (Virtual or Actual)
|
|
55
|
+
const isInLifecycle = triggers.some(t => lifecycleFiles.has(t.file)) || (facts['LIFECYCLE_CONTEXT'] && facts['LIFECYCLE_CONTEXT'].length > 0);
|
|
56
56
|
if (isInLifecycle) {
|
|
57
|
-
multiplier
|
|
57
|
+
multiplier *= 2.0;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// Encoder Multiplier (1.5x)
|
|
@@ -64,18 +64,18 @@ class SafetyEngine {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// Cluster Bonus: Source + Sink
|
|
67
|
-
const hasSource = triggers.some(t => t.type.includes('READ'));
|
|
68
|
-
const hasSink = triggers.some(t => t.type.includes('SINK') || t.type === 'DYNAMIC_EXECUTION' || t.type === 'SHELL_EXECUTION');
|
|
67
|
+
const hasSource = triggers.some(t => t.type.includes('READ') || t.type.includes('ACCESS'));
|
|
68
|
+
const hasSink = triggers.some(t => t.type.includes('SINK') || t.type === 'DYNAMIC_EXECUTION' || t.type === 'SHELL_EXECUTION' || t.type === 'DYNAMIC_REQUIRE');
|
|
69
69
|
if (hasSource && hasSink) {
|
|
70
70
|
baseScore += 40;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
let finalScore = baseScore * multiplier;
|
|
74
74
|
|
|
75
|
-
// Severe Cluster:
|
|
76
|
-
const
|
|
77
|
-
const isDangerousSink = triggers.some(t => t.type === 'NETWORK_SINK' || t.type === 'SHELL_EXECUTION');
|
|
78
|
-
if (
|
|
75
|
+
// Severe Cluster: SENSITIVE_READ + Dangerous Sink + lifecycleContext = Critical (100)
|
|
76
|
+
const isSensitiveRead = triggers.some(t => t.type === 'ENV_READ' || t.type === 'FILE_READ_SENSITIVE');
|
|
77
|
+
const isDangerousSink = triggers.some(t => t.type === 'NETWORK_SINK' || t.type === 'DNS_SINK' || t.type === 'RAW_SOCKET_SINK' || t.type === 'SHELL_EXECUTION');
|
|
78
|
+
if (isSensitiveRead && isDangerousSink && isInLifecycle) {
|
|
79
79
|
finalScore = 100;
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -83,6 +83,7 @@ class SafetyEngine {
|
|
|
83
83
|
id: rule.id,
|
|
84
84
|
alias: rule.alias,
|
|
85
85
|
name: rule.name,
|
|
86
|
+
priority: rule.priority || 1,
|
|
86
87
|
score: Math.min(finalScore, 100),
|
|
87
88
|
triggers: triggers,
|
|
88
89
|
description: rule.description,
|
package/src/rules/definitions.js
CHANGED
|
@@ -5,6 +5,7 @@ const RULES = [
|
|
|
5
5
|
name: 'Environment Variable Exfiltration',
|
|
6
6
|
requires: ['ENV_READ', 'NETWORK_SINK'],
|
|
7
7
|
optional: ['OBFUSCATION'],
|
|
8
|
+
priority: 1,
|
|
8
9
|
baseScore: 40,
|
|
9
10
|
description: 'Detection of environment variables being read and sent over the network.'
|
|
10
11
|
},
|
|
@@ -13,6 +14,7 @@ const RULES = [
|
|
|
13
14
|
alias: 'SENSITIVE_FILE_EXFILTRATION',
|
|
14
15
|
name: 'Sensitive File Exfiltration',
|
|
15
16
|
requires: ['FILE_READ_SENSITIVE', 'NETWORK_SINK'],
|
|
17
|
+
priority: 1,
|
|
16
18
|
baseScore: 50,
|
|
17
19
|
description: 'Detection of sensitive files (e.g., .ssh, .env) being read and sent over the network.'
|
|
18
20
|
},
|
|
@@ -21,6 +23,7 @@ const RULES = [
|
|
|
21
23
|
alias: 'PERSISTENCE_ATTEMPT',
|
|
22
24
|
name: 'Persistence Attempt',
|
|
23
25
|
requires: ['FILE_WRITE_STARTUP'],
|
|
26
|
+
priority: 2,
|
|
24
27
|
baseScore: 60,
|
|
25
28
|
description: 'Detection of attempts to write to system startup directories.'
|
|
26
29
|
},
|
|
@@ -29,6 +32,7 @@ const RULES = [
|
|
|
29
32
|
alias: 'OBFUSCATED_EXECUTION',
|
|
30
33
|
name: 'Obfuscated Execution',
|
|
31
34
|
requires: ['OBFUSCATION', 'DYNAMIC_EXECUTION'],
|
|
35
|
+
priority: 2,
|
|
32
36
|
baseScore: 40,
|
|
33
37
|
description: 'Detection of high-entropy strings being executed via eval or Function constructor.'
|
|
34
38
|
},
|
|
@@ -38,6 +42,7 @@ const RULES = [
|
|
|
38
42
|
name: 'Shell Command Execution',
|
|
39
43
|
requires: ['SHELL_EXECUTION'],
|
|
40
44
|
optional: ['ENV_READ', 'FILE_READ_SENSITIVE'],
|
|
45
|
+
priority: 1,
|
|
41
46
|
baseScore: 50,
|
|
42
47
|
description: 'Detection of shell command execution (child_process).'
|
|
43
48
|
},
|
|
@@ -45,17 +50,75 @@ const RULES = [
|
|
|
45
50
|
id: 'ZFT-006',
|
|
46
51
|
alias: 'DYNAMIC_REQUIRE_DEPENDENCY',
|
|
47
52
|
name: 'Dynamic Require Dependency',
|
|
48
|
-
requires: ['
|
|
53
|
+
requires: ['DYNAMIC_REQUIRE'],
|
|
54
|
+
priority: 1,
|
|
49
55
|
baseScore: 30,
|
|
50
56
|
description: 'Detection of dynamic require calls where the dependency name is a variable.'
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'ZFT-007',
|
|
60
|
+
alias: 'DNS_EXFILTRATION',
|
|
61
|
+
name: 'DNS-Based Exfiltration',
|
|
62
|
+
requires: ['ENV_READ', 'DNS_SINK'],
|
|
63
|
+
priority: 2,
|
|
64
|
+
baseScore: 45,
|
|
65
|
+
description: 'Stealthy environment variable exfiltration via DNS lookups.'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'ZFT-008',
|
|
69
|
+
alias: 'SUSPICIOUS_COLLECTION',
|
|
70
|
+
name: 'Suspicious Information Collection',
|
|
71
|
+
requires: ['MASS_ENV_ACCESS'],
|
|
72
|
+
optional: ['FILE_READ_SENSITIVE'],
|
|
73
|
+
priority: 1,
|
|
74
|
+
baseScore: 20,
|
|
75
|
+
description: 'Massive environment reading without immediate network activity (potential harvesting).'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'ZFT-009',
|
|
79
|
+
alias: 'REMOTE_DROPPER_PATTERN',
|
|
80
|
+
name: 'Remote Script Dropper',
|
|
81
|
+
requires: ['SHELL_EXECUTION', 'REMOTE_FETCH_SIGNAL'],
|
|
82
|
+
optional: ['OBFUSCATION', 'PIPE_TO_SHELL_SIGNAL'],
|
|
83
|
+
priority: 3,
|
|
84
|
+
baseScore: 55,
|
|
85
|
+
description: 'Detection of remote script download and execution (curl | sh) patterns.'
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'ZFT-010',
|
|
89
|
+
alias: 'ENCODED_EXFILTRATION',
|
|
90
|
+
name: 'Encoded Data Exfiltration',
|
|
91
|
+
requires: ['ENV_READ', 'NETWORK_SINK', 'ENCODER_USE'],
|
|
92
|
+
priority: 3,
|
|
93
|
+
baseScore: 70,
|
|
94
|
+
description: 'Sensitive data encoded before transmission to evade detection.'
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'ZFT-011',
|
|
98
|
+
alias: 'RAW_SOCKET_TUNNEL',
|
|
99
|
+
name: 'Raw Socket Tunneling',
|
|
100
|
+
requires: ['RAW_SOCKET_SINK'],
|
|
101
|
+
priority: 2,
|
|
102
|
+
baseScore: 45,
|
|
103
|
+
description: 'Use of raw network sockets instead of http/dns, often used for reverse shells.'
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'ZFT-012',
|
|
107
|
+
alias: 'STARTUP_SCRIPT_MOD',
|
|
108
|
+
name: 'Startup Script Modification',
|
|
109
|
+
requires: ['FILE_WRITE_STARTUP'],
|
|
110
|
+
priority: 2,
|
|
111
|
+
baseScore: 60,
|
|
112
|
+
description: 'Detection of attempts to modify package.json scripts or npm configuration.'
|
|
51
113
|
}
|
|
52
114
|
];
|
|
53
115
|
|
|
54
116
|
const CATEGORIES = {
|
|
55
|
-
SOURCES: ['ENV_READ', 'FILE_READ_SENSITIVE'],
|
|
56
|
-
SINKS: ['NETWORK_SINK', 'DYNAMIC_EXECUTION', 'SHELL_EXECUTION'],
|
|
57
|
-
SIGNALS: ['OBFUSCATION', 'ENCODER_USE'],
|
|
58
|
-
PERSISTENCE: ['FILE_WRITE_STARTUP']
|
|
117
|
+
SOURCES: ['ENV_READ', 'FILE_READ_SENSITIVE', 'MASS_ENV_ACCESS'],
|
|
118
|
+
SINKS: ['NETWORK_SINK', 'DNS_SINK', 'RAW_SOCKET_SINK', 'DYNAMIC_EXECUTION', 'SHELL_EXECUTION', 'DYNAMIC_REQUIRE'],
|
|
119
|
+
SIGNALS: ['OBFUSCATION', 'ENCODER_USE', 'REMOTE_FETCH_SIGNAL', 'PIPE_TO_SHELL_SIGNAL'],
|
|
120
|
+
PERSISTENCE: ['FILE_WRITE_STARTUP'],
|
|
121
|
+
CONTEXT: ['LIFECYCLE_CONTEXT']
|
|
59
122
|
};
|
|
60
123
|
|
|
61
124
|
module.exports = { RULES, CATEGORIES };
|
package/src/scanner.js
CHANGED
|
@@ -27,13 +27,20 @@ class PackageScanner {
|
|
|
27
27
|
let allFacts = {
|
|
28
28
|
facts: {
|
|
29
29
|
ENV_READ: [],
|
|
30
|
+
MASS_ENV_ACCESS: [],
|
|
30
31
|
FILE_READ_SENSITIVE: [],
|
|
31
32
|
NETWORK_SINK: [],
|
|
33
|
+
DNS_SINK: [],
|
|
34
|
+
RAW_SOCKET_SINK: [],
|
|
32
35
|
DYNAMIC_EXECUTION: [],
|
|
36
|
+
DYNAMIC_REQUIRE: [],
|
|
33
37
|
OBFUSCATION: [],
|
|
34
38
|
FILE_WRITE_STARTUP: [],
|
|
35
39
|
SHELL_EXECUTION: [],
|
|
36
|
-
ENCODER_USE: []
|
|
40
|
+
ENCODER_USE: [],
|
|
41
|
+
REMOTE_FETCH_SIGNAL: [],
|
|
42
|
+
PIPE_TO_SHELL_SIGNAL: [],
|
|
43
|
+
LIFECYCLE_CONTEXT: []
|
|
37
44
|
},
|
|
38
45
|
flows: []
|
|
39
46
|
};
|
|
@@ -78,6 +85,11 @@ class PackageScanner {
|
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
// Merge facts (Synchronized)
|
|
88
|
+
if (lifecycleFiles.has(file)) {
|
|
89
|
+
facts.LIFECYCLE_CONTEXT = facts.LIFECYCLE_CONTEXT || [];
|
|
90
|
+
facts.LIFECYCLE_CONTEXT.push({ file, reason: 'Lifecycle script context detected' });
|
|
91
|
+
}
|
|
92
|
+
|
|
81
93
|
for (const category in facts) {
|
|
82
94
|
if (allFacts.facts[category]) {
|
|
83
95
|
allFacts.facts[category].push(...facts[category]);
|
package/v22_output.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
🛡️ Verifying Zift v2.2.0 Deterministic Architecture...
|
|
2
|
+
📊 Audit Results:
|
|
3
|
+
- [Priority: 1, Score: 100] ZFT-005: Shell Command Execution
|
|
4
|
+
- [Priority: 1, Score: 80] ZFT-001: Environment Variable Exfiltration
|
|
5
|
+
|
|
6
|
+
🔍 Structural Verification:
|
|
7
|
+
- DNS Exfil detected: false
|
|
8
|
+
- Env Exfil detected: true
|
|
9
|
+
- Remote Dropper detected: false
|
|
10
|
+
- Mass Env Access detected: false
|
|
11
|
+
- Dynamic Require detected: false
|
|
12
|
+
❌ v2.2.0 Verification incomplete. Some rules did not trigger as expected.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
🛡️ Verifying Zift v2.2.0 Deterministic Architecture...
|
|
2
|
+
📊 Audit Results:
|
|
3
|
+
- [Priority: 1, Score: 100] ZFT-005: Shell Command Execution
|
|
4
|
+
- [Priority: 2, Score: 85] ZFT-007: DNS-Based Exfiltration
|
|
5
|
+
- [Priority: 1, Score: 80] ZFT-001: Environment Variable Exfiltration
|
|
6
|
+
- [Priority: 3, Score: 75] ZFT-009: Remote Script Dropper
|
|
7
|
+
- [Priority: 1, Score: 30] ZFT-006: Dynamic Require Dependency
|
|
8
|
+
- [Priority: 1, Score: 20] ZFT-008: Suspicious Information Collection
|
|
9
|
+
|
|
10
|
+
🔍 Structural Verification:
|
|
11
|
+
- DNS Exfil detected: true
|
|
12
|
+
- Env Exfil detected: true
|
|
13
|
+
- Remote Dropper detected: true
|
|
14
|
+
- Mass Env Access detected: true
|
|
15
|
+
- Dynamic Require detected: true
|
|
16
|
+
✅ v2.2.0 Architecture Verified Successfully!
|