@veestack-tools/cli 3.0.1 → 3.0.3
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/dist/index.js +450 -0
- package/package.json +8 -4
- package/.env.example +0 -14
- package/src/commands/login.ts +0 -60
- package/src/commands/scan.ts +0 -219
- package/src/commands/upload.ts +0 -241
- package/src/index.ts +0 -36
- package/src/wasm/license.ts +0 -181
- package/src/wasm/loader.ts +0 -409
- package/tsconfig.json +0 -19
- package/tsup.config.ts +0 -11
package/src/wasm/loader.ts
DELETED
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import crypto from 'crypto';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
-
|
|
9
|
-
export interface WasmEngineConfig {
|
|
10
|
-
apiUrl: string;
|
|
11
|
-
apiKey: string;
|
|
12
|
-
licenseKey?: string;
|
|
13
|
-
sessionId: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface RemoteRulesResponse {
|
|
17
|
-
success: boolean;
|
|
18
|
-
rules_version: string;
|
|
19
|
-
rules_count: number;
|
|
20
|
-
encrypted_rules: string;
|
|
21
|
-
encrypted_session_key: string;
|
|
22
|
-
expires_at: string;
|
|
23
|
-
licensed: boolean;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface WasmAnalysisResult {
|
|
27
|
-
findings: WasmFinding[];
|
|
28
|
-
metrics: WasmMetrics;
|
|
29
|
-
execution_time_ms: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface WasmFinding {
|
|
33
|
-
rule_id: string;
|
|
34
|
-
file_path: string;
|
|
35
|
-
line_number: number;
|
|
36
|
-
severity: 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
37
|
-
message: string;
|
|
38
|
-
context_hash: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface WasmMetrics {
|
|
42
|
-
files_scanned: number;
|
|
43
|
-
rules_applied: number;
|
|
44
|
-
patterns_matched: number;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface WasmSnapshot {
|
|
48
|
-
version: string;
|
|
49
|
-
project_id: string;
|
|
50
|
-
files: WasmFileInfo[];
|
|
51
|
-
dependencies: WasmDependency[];
|
|
52
|
-
metadata: WasmMetadata;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface WasmFileInfo {
|
|
56
|
-
path: string;
|
|
57
|
-
path_hash: string;
|
|
58
|
-
content_hash: string;
|
|
59
|
-
extension: string;
|
|
60
|
-
size_bytes: number;
|
|
61
|
-
estimated_lines: number;
|
|
62
|
-
depth: number;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface WasmDependency {
|
|
66
|
-
name_hash: string;
|
|
67
|
-
version_major: number;
|
|
68
|
-
version_minor: number;
|
|
69
|
-
category: 'dev' | 'prod' | 'peer';
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface WasmMetadata {
|
|
73
|
-
total_files: number;
|
|
74
|
-
total_dependencies: number;
|
|
75
|
-
framework: string;
|
|
76
|
-
language: string;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export class WasmEngineLoader {
|
|
80
|
-
private config: WasmEngineConfig;
|
|
81
|
-
private wasmModule: WebAssembly.Module | null = null;
|
|
82
|
-
private wasmInstance: WebAssembly.Instance | null = null;
|
|
83
|
-
private sessionKey: string = '';
|
|
84
|
-
private rules: any[] = [];
|
|
85
|
-
|
|
86
|
-
constructor(config: WasmEngineConfig) {
|
|
87
|
-
this.config = config;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Load and initialize the WASM engine
|
|
92
|
-
*/
|
|
93
|
-
async initialize(): Promise<boolean> {
|
|
94
|
-
try {
|
|
95
|
-
// Find WASM file path
|
|
96
|
-
const wasmPath = this.findWasmFile();
|
|
97
|
-
|
|
98
|
-
if (!wasmPath) {
|
|
99
|
-
console.error('❌ WASM engine not found');
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Read WASM file
|
|
104
|
-
const wasmBuffer = fs.readFileSync(wasmPath);
|
|
105
|
-
|
|
106
|
-
// Compile WASM module
|
|
107
|
-
this.wasmModule = await WebAssembly.compile(wasmBuffer);
|
|
108
|
-
|
|
109
|
-
// Create import object with required imports
|
|
110
|
-
const importObject = {
|
|
111
|
-
env: {
|
|
112
|
-
memory: new WebAssembly.Memory({ initial: 256, maximum: 512 }),
|
|
113
|
-
__memory_base: 0,
|
|
114
|
-
__table_base: 0,
|
|
115
|
-
abort: (msg: number, file: number, line: number, column: number) => {
|
|
116
|
-
console.error(`WASM abort: ${msg} at ${file}:${line}:${column}`);
|
|
117
|
-
},
|
|
118
|
-
// Add console.log for debugging
|
|
119
|
-
log: (ptr: number, len: number) => {
|
|
120
|
-
const memory = (this.wasmInstance?.exports.memory as WebAssembly.Memory);
|
|
121
|
-
if (memory) {
|
|
122
|
-
const bytes = new Uint8Array(memory.buffer, ptr, len);
|
|
123
|
-
const message = new TextDecoder().decode(bytes);
|
|
124
|
-
console.log(`[WASM] ${message}`);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
console: {
|
|
129
|
-
log: (message: string) => {
|
|
130
|
-
console.log(`[WASM Engine] ${message}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
// Instantiate WASM module
|
|
136
|
-
this.wasmInstance = await WebAssembly.instantiate(this.wasmModule, importObject);
|
|
137
|
-
|
|
138
|
-
console.log('✅ WASM engine initialized successfully');
|
|
139
|
-
return true;
|
|
140
|
-
} catch (error) {
|
|
141
|
-
console.error('❌ Failed to initialize WASM engine:', error);
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Fetch encrypted rules from remote server
|
|
148
|
-
*/
|
|
149
|
-
async fetchRemoteRules(framework: string, language: string): Promise<boolean> {
|
|
150
|
-
try {
|
|
151
|
-
const response = await fetch(`${this.config.apiUrl}/functions/v1/rules`, {
|
|
152
|
-
method: 'POST',
|
|
153
|
-
headers: {
|
|
154
|
-
'Content-Type': 'application/json',
|
|
155
|
-
'Authorization': `Bearer ${this.config.apiKey}`
|
|
156
|
-
},
|
|
157
|
-
body: JSON.stringify({
|
|
158
|
-
framework,
|
|
159
|
-
language,
|
|
160
|
-
version: '1.0.0',
|
|
161
|
-
session_id: this.config.sessionId,
|
|
162
|
-
license_key: this.config.licenseKey
|
|
163
|
-
})
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
if (!response.ok) {
|
|
167
|
-
const error = await response.text();
|
|
168
|
-
console.error('❌ Failed to fetch rules:', error);
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const data: RemoteRulesResponse = await response.json();
|
|
173
|
-
|
|
174
|
-
if (!data.success) {
|
|
175
|
-
console.error('❌ Rules API returned unsuccessful response');
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Decrypt session key
|
|
180
|
-
this.sessionKey = this.decryptSessionKey(data.encrypted_session_key);
|
|
181
|
-
|
|
182
|
-
// Decrypt rules
|
|
183
|
-
const decryptedRules = this.decryptRules(data.encrypted_rules);
|
|
184
|
-
this.rules = JSON.parse(decryptedRules);
|
|
185
|
-
|
|
186
|
-
console.log(`✅ Loaded ${this.rules.length} security rules (v${data.rules_version})`);
|
|
187
|
-
|
|
188
|
-
return true;
|
|
189
|
-
} catch (error) {
|
|
190
|
-
console.error('❌ Error fetching remote rules:', error);
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Analyze a snapshot using WASM engine
|
|
197
|
-
*/
|
|
198
|
-
async analyzeSnapshot(
|
|
199
|
-
snapshot: WasmSnapshot,
|
|
200
|
-
fileContents: Map<string, string>
|
|
201
|
-
): Promise<WasmAnalysisResult | null> {
|
|
202
|
-
if (!this.wasmInstance) {
|
|
203
|
-
console.error('❌ WASM engine not initialized');
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
try {
|
|
208
|
-
const exports = this.wasmInstance.exports as any;
|
|
209
|
-
|
|
210
|
-
// Initialize session in WASM
|
|
211
|
-
const encryptedKey = this.encryptString(this.sessionKey);
|
|
212
|
-
|
|
213
|
-
// Convert strings to WASM memory
|
|
214
|
-
const snapshotJson = JSON.stringify(snapshot);
|
|
215
|
-
const contentsJson = JSON.stringify(Object.fromEntries(fileContents));
|
|
216
|
-
|
|
217
|
-
// Allocate memory for input
|
|
218
|
-
const snapshotPtr = this.allocateString(snapshotJson);
|
|
219
|
-
const contentsPtr = this.allocateString(contentsJson);
|
|
220
|
-
const sessionPtr = this.allocateString(encryptedKey);
|
|
221
|
-
|
|
222
|
-
// Call WASM functions
|
|
223
|
-
const initResult = exports.initialize_session(sessionPtr);
|
|
224
|
-
if (!initResult) {
|
|
225
|
-
console.error('❌ Failed to initialize WASM session');
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Load encrypted rules into WASM
|
|
230
|
-
const rulesJson = JSON.stringify(this.rules);
|
|
231
|
-
const encryptedRules = this.encryptString(rulesJson);
|
|
232
|
-
const rulesPtr = this.allocateString(encryptedRules);
|
|
233
|
-
|
|
234
|
-
const loadResult = exports.load_rules(rulesPtr);
|
|
235
|
-
if (loadResult !== 0) {
|
|
236
|
-
console.error('❌ Failed to load rules into WASM');
|
|
237
|
-
return null;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Run analysis
|
|
241
|
-
const resultPtr = exports.analyze_snapshot(snapshotPtr, contentsPtr);
|
|
242
|
-
|
|
243
|
-
// Read result from WASM memory
|
|
244
|
-
const resultJson = this.readString(resultPtr);
|
|
245
|
-
|
|
246
|
-
// Free allocated memory (if WASM exports free function)
|
|
247
|
-
if (exports.free) {
|
|
248
|
-
exports.free(snapshotPtr);
|
|
249
|
-
exports.free(contentsPtr);
|
|
250
|
-
exports.free(sessionPtr);
|
|
251
|
-
exports.free(rulesPtr);
|
|
252
|
-
exports.free(resultPtr);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Parse result
|
|
256
|
-
const result: WasmAnalysisResult = JSON.parse(resultJson);
|
|
257
|
-
|
|
258
|
-
return result;
|
|
259
|
-
} catch (error) {
|
|
260
|
-
console.error('❌ Analysis error:', error);
|
|
261
|
-
return null;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Validate license key
|
|
267
|
-
*/
|
|
268
|
-
validateLicense(hardwareId: string): boolean {
|
|
269
|
-
if (!this.wasmInstance) return false;
|
|
270
|
-
|
|
271
|
-
try {
|
|
272
|
-
const exports = this.wasmInstance.exports as any;
|
|
273
|
-
const licensePtr = this.allocateString(this.config.licenseKey || '');
|
|
274
|
-
const hardwarePtr = this.allocateString(hardwareId);
|
|
275
|
-
|
|
276
|
-
const result = exports.validate_license(licensePtr, hardwarePtr);
|
|
277
|
-
|
|
278
|
-
return result === 1;
|
|
279
|
-
} catch (error) {
|
|
280
|
-
console.error('❌ License validation error:', error);
|
|
281
|
-
return false;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Generate content fingerprint
|
|
287
|
-
*/
|
|
288
|
-
generateFingerprint(content: string): string {
|
|
289
|
-
if (!this.wasmInstance) {
|
|
290
|
-
// Fallback to Node.js crypto
|
|
291
|
-
return crypto.createHash('sha256').update(content).digest('hex');
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
try {
|
|
295
|
-
const exports = this.wasmInstance.exports as any;
|
|
296
|
-
const contentPtr = this.allocateString(content);
|
|
297
|
-
const resultPtr = exports.generate_fingerprint(contentPtr);
|
|
298
|
-
return this.readString(resultPtr);
|
|
299
|
-
} catch {
|
|
300
|
-
return crypto.createHash('sha256').update(content).digest('hex');
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Generate unique session ID
|
|
306
|
-
*/
|
|
307
|
-
static generateSessionId(): string {
|
|
308
|
-
return crypto.randomUUID();
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Generate hardware fingerprint
|
|
313
|
-
*/
|
|
314
|
-
static generateHardwareId(): string {
|
|
315
|
-
const components = [
|
|
316
|
-
process.platform,
|
|
317
|
-
process.arch,
|
|
318
|
-
process.version,
|
|
319
|
-
require('os').hostname(),
|
|
320
|
-
'veestack'
|
|
321
|
-
];
|
|
322
|
-
|
|
323
|
-
const combined = components.join('|');
|
|
324
|
-
return crypto.createHash('sha256').update(combined).digest('hex');
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Private helper methods
|
|
328
|
-
|
|
329
|
-
private findWasmFile(): string | null {
|
|
330
|
-
// Search for WASM file in multiple locations
|
|
331
|
-
const possiblePaths = [
|
|
332
|
-
path.join(__dirname, '../../wasm-engine/target/wasm32-unknown-unknown/release/veestack_wasm_engine.wasm'),
|
|
333
|
-
path.join(__dirname, '../engine.wasm'),
|
|
334
|
-
path.join(process.cwd(), 'node_modules/@veestack-tools/wasm-engine/engine.wasm'),
|
|
335
|
-
path.join(__dirname, '../../../wasm-engine/pkg/veestack_wasm_engine_bg.wasm'),
|
|
336
|
-
];
|
|
337
|
-
|
|
338
|
-
for (const wasmPath of possiblePaths) {
|
|
339
|
-
if (fs.existsSync(wasmPath)) {
|
|
340
|
-
return wasmPath;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return null;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
private allocateString(str: string): number {
|
|
348
|
-
if (!this.wasmInstance) return 0;
|
|
349
|
-
|
|
350
|
-
const encoder = new TextEncoder();
|
|
351
|
-
const bytes = encoder.encode(str + '\0');
|
|
352
|
-
|
|
353
|
-
const exports = this.wasmInstance.exports as any;
|
|
354
|
-
const ptr = exports.malloc ? exports.malloc(bytes.length) : 0;
|
|
355
|
-
|
|
356
|
-
if (ptr === 0) {
|
|
357
|
-
// Fallback: use memory directly
|
|
358
|
-
const memory = exports.memory as WebAssembly.Memory;
|
|
359
|
-
const view = new Uint8Array(memory.buffer);
|
|
360
|
-
const offset = 1024; // Use a fixed offset
|
|
361
|
-
view.set(bytes, offset);
|
|
362
|
-
return offset;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
const memory = exports.memory as WebAssembly.Memory;
|
|
366
|
-
const view = new Uint8Array(memory.buffer);
|
|
367
|
-
view.set(bytes, ptr);
|
|
368
|
-
|
|
369
|
-
return ptr;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
private readString(ptr: number): string {
|
|
373
|
-
if (!this.wasmInstance) return '';
|
|
374
|
-
|
|
375
|
-
const exports = this.wasmInstance.exports as any;
|
|
376
|
-
const memory = exports.memory as WebAssembly.Memory;
|
|
377
|
-
const view = new Uint8Array(memory.buffer);
|
|
378
|
-
|
|
379
|
-
// Find null terminator
|
|
380
|
-
let end = ptr;
|
|
381
|
-
while (view[end] !== 0 && end < view.length) {
|
|
382
|
-
end++;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
const bytes = view.slice(ptr, end);
|
|
386
|
-
return new TextDecoder().decode(bytes);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
private decryptSessionKey(encrypted: string): string {
|
|
390
|
-
const decoded = Buffer.from(encrypted, 'base64');
|
|
391
|
-
return decoded.toString('utf-8');
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
private decryptRules(encrypted: string): string {
|
|
395
|
-
const decoded = Buffer.from(encrypted, 'base64');
|
|
396
|
-
const key = Buffer.from(this.sessionKey);
|
|
397
|
-
|
|
398
|
-
const decrypted = decoded.map((byte, i) => byte ^ key[i % key.length]);
|
|
399
|
-
return decrypted.toString('utf-8');
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
private encryptString(str: string): string {
|
|
403
|
-
const key = Buffer.from('veestack_session_key_2024');
|
|
404
|
-
const data = Buffer.from(str);
|
|
405
|
-
|
|
406
|
-
const encrypted = data.map((byte, i) => byte ^ key[i % key.length]);
|
|
407
|
-
return encrypted.toString('base64');
|
|
408
|
-
}
|
|
409
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"lib": ["ES2022"],
|
|
6
|
-
"moduleResolution": "bundler",
|
|
7
|
-
"outDir": "./dist",
|
|
8
|
-
"rootDir": "./src",
|
|
9
|
-
"strict": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"declaration": true,
|
|
14
|
-
"declarationMap": true,
|
|
15
|
-
"types": ["node"]
|
|
16
|
-
},
|
|
17
|
-
"include": ["src/**/*"],
|
|
18
|
-
"exclude": ["node_modules", "dist"]
|
|
19
|
-
}
|
package/tsup.config.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'tsup';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
entry: ['src/index.ts'],
|
|
5
|
-
format: ['esm'],
|
|
6
|
-
dts: false,
|
|
7
|
-
clean: true,
|
|
8
|
-
external: ['@veestack/types', '@veestack/engine', '@veestack/utils'],
|
|
9
|
-
splitting: false,
|
|
10
|
-
// No banner here - we'll add shebang in package.json bin
|
|
11
|
-
});
|