@moontra/moonui-pro 2.37.0 → 2.37.2
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/cdn/index.global.js +149 -149
- package/dist/cdn/index.global.js.map +1 -1
- package/dist/index.d.ts +51 -22
- package/dist/index.mjs +856 -638
- package/package.json +1 -1
- package/templates/validate-pro-route.ts +240 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moontra/moonui-pro",
|
|
3
|
-
"version": "2.37.
|
|
3
|
+
"version": "2.37.2",
|
|
4
4
|
"description": "Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MoonUI Pro - Server-side License Validation Route
|
|
2
|
+
* MoonUI Pro - Server-side License Validation Route with Enhanced Security
|
|
3
3
|
*
|
|
4
4
|
* This route handles license validation on the server to prevent browser API calls.
|
|
5
5
|
* Copy this file to: app/api/moonui/validate-pro/route.ts (App Router)
|
|
@@ -8,11 +8,23 @@
|
|
|
8
8
|
* Required environment variables:
|
|
9
9
|
* - MOONUI_LICENSE_KEY: Your MoonUI Pro license key
|
|
10
10
|
* - MOONUI_ENCRYPTION_KEY (optional): Custom encryption key for cookies
|
|
11
|
+
* - MOONUI_SECURITY_HASH (auto-generated): Security checksum for validation
|
|
12
|
+
*
|
|
13
|
+
* SECURITY WARNING: Do not modify the validation logic.
|
|
14
|
+
* Any tampering will be detected and reported.
|
|
11
15
|
*/
|
|
12
16
|
|
|
13
17
|
import { NextRequest, NextResponse } from 'next/server';
|
|
14
18
|
import { cookies, headers } from 'next/headers';
|
|
15
19
|
import crypto from 'crypto';
|
|
20
|
+
import os from 'os';
|
|
21
|
+
import { execSync } from 'child_process';
|
|
22
|
+
|
|
23
|
+
// Security checksum - DO NOT MODIFY
|
|
24
|
+
const SECURITY_CHECKSUM = process.env.MOONUI_SECURITY_HASH ||
|
|
25
|
+
crypto.createHash('sha256')
|
|
26
|
+
.update(`moonui-pro-${process.env.MOONUI_LICENSE_KEY || 'default'}-validation`)
|
|
27
|
+
.digest('hex');
|
|
16
28
|
|
|
17
29
|
// Cache configuration
|
|
18
30
|
const CACHE_DURATION = process.env.NODE_ENV === 'production'
|
|
@@ -30,21 +42,107 @@ const validationCache = new Map<string, {
|
|
|
30
42
|
expiresAt: number;
|
|
31
43
|
}>();
|
|
32
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Get MAC address for hardware fingerprinting
|
|
47
|
+
*/
|
|
48
|
+
function getMacAddress(): string {
|
|
49
|
+
try {
|
|
50
|
+
const platform = os.platform();
|
|
51
|
+
let command: string;
|
|
52
|
+
|
|
53
|
+
switch (platform) {
|
|
54
|
+
case 'darwin':
|
|
55
|
+
command = "ifconfig | grep ether | head -1 | awk '{print $2}'";
|
|
56
|
+
break;
|
|
57
|
+
case 'linux':
|
|
58
|
+
command = "ip link show | grep ether | head -1 | awk '{print $2}'";
|
|
59
|
+
break;
|
|
60
|
+
case 'win32':
|
|
61
|
+
command = 'getmac /NH /FO csv | findstr /r "^"';
|
|
62
|
+
break;
|
|
63
|
+
default:
|
|
64
|
+
return 'unknown-mac';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const result = execSync(command, { encoding: 'utf8' }).trim();
|
|
68
|
+
const match = result.match(/([a-f0-9:]+)/i);
|
|
69
|
+
|
|
70
|
+
if (match && match[1]) {
|
|
71
|
+
return match[1].replace(/[:-]/g, '').toLowerCase();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Fallback to network interfaces
|
|
75
|
+
const interfaces = os.networkInterfaces();
|
|
76
|
+
for (const name in interfaces) {
|
|
77
|
+
const iface = interfaces[name];
|
|
78
|
+
if (iface) {
|
|
79
|
+
for (const entry of iface) {
|
|
80
|
+
if (entry.mac && entry.mac !== '00:00:00:00:00:00') {
|
|
81
|
+
return entry.mac.replace(/[:-]/g, '').toLowerCase();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return 'fallback-mac';
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error('[Hardware] Error getting MAC:', error);
|
|
89
|
+
return 'error-mac';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generate hardware-based device fingerprint
|
|
95
|
+
* Format: hw-{platform}-{macHash}-{cpuHash}
|
|
96
|
+
*/
|
|
97
|
+
function generateHardwareFingerprint(): string {
|
|
98
|
+
const platform = os.platform();
|
|
99
|
+
const macAddress = getMacAddress();
|
|
100
|
+
const cpuCount = os.cpus().length;
|
|
101
|
+
const totalMem = Math.round(os.totalmem() / (1024 * 1024 * 1024)); // GB
|
|
102
|
+
const hostname = os.hostname();
|
|
103
|
+
|
|
104
|
+
// Create hashes
|
|
105
|
+
const macHash = crypto.createHash('sha256')
|
|
106
|
+
.update(macAddress)
|
|
107
|
+
.digest('hex')
|
|
108
|
+
.substring(0, 8);
|
|
109
|
+
|
|
110
|
+
const systemHash = crypto.createHash('sha256')
|
|
111
|
+
.update(`${cpuCount}:${totalMem}:${hostname}`)
|
|
112
|
+
.digest('hex')
|
|
113
|
+
.substring(0, 6);
|
|
114
|
+
|
|
115
|
+
return `hw-${platform}-${macHash}-${systemHash}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
33
118
|
/**
|
|
34
119
|
* Generate a device fingerprint from request headers
|
|
120
|
+
* Combines hardware fingerprint with browser characteristics
|
|
35
121
|
*/
|
|
36
122
|
async function getDeviceFingerprint(request: NextRequest): Promise<string> {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
123
|
+
// In development with CLI auth, generate hardware-based fingerprint
|
|
124
|
+
if (process.env.NODE_ENV === 'development') {
|
|
125
|
+
// Use hardware fingerprint for maximum security
|
|
126
|
+
const hardwareId = generateHardwareFingerprint();
|
|
127
|
+
|
|
128
|
+
// If CLI device ID is set, validate it matches hardware
|
|
129
|
+
if (process.env.NEXT_PUBLIC_MOONUI_DEVICE_ID) {
|
|
130
|
+
const cliDeviceId = process.env.NEXT_PUBLIC_MOONUI_DEVICE_ID;
|
|
131
|
+
|
|
132
|
+
// For backward compatibility, accept both old and new format
|
|
133
|
+
if (cliDeviceId.startsWith('hw-')) {
|
|
134
|
+
return cliDeviceId; // New hardware-based format
|
|
135
|
+
} else {
|
|
136
|
+
console.log('[MoonUI] Migrating to hardware-based device ID');
|
|
137
|
+
return hardwareId; // Use new hardware ID
|
|
138
|
+
}
|
|
139
|
+
}
|
|
43
140
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
141
|
+
return hardwareId;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// In production, use hardware fingerprint
|
|
145
|
+
return generateHardwareFingerprint();
|
|
48
146
|
}
|
|
49
147
|
|
|
50
148
|
/**
|
|
@@ -83,20 +181,56 @@ async function validateWithMoonUIServer(
|
|
|
83
181
|
const devToken = process.env.NEXT_PUBLIC_MOONUI_DEV_TOKEN;
|
|
84
182
|
const devDeviceId = process.env.NEXT_PUBLIC_MOONUI_DEVICE_ID;
|
|
85
183
|
|
|
86
|
-
|
|
184
|
+
console.log('[MoonUI Dev Auth] Checking CLI authentication:');
|
|
185
|
+
console.log('[MoonUI Dev Auth] Device IDs match:', devDeviceId === deviceId);
|
|
186
|
+
|
|
187
|
+
// STRICT DEVICE ID VALIDATION - Prevent token sharing
|
|
188
|
+
if (devToken && devDeviceId && devDeviceId === deviceId) {
|
|
87
189
|
try {
|
|
88
190
|
// Decode the dev token
|
|
89
191
|
const decoded = JSON.parse(Buffer.from(devToken, 'base64').toString());
|
|
192
|
+
|
|
193
|
+
// Verify token structure and session
|
|
194
|
+
if (!decoded.deviceId || !decoded.session || !decoded.security) {
|
|
195
|
+
console.error('[MoonUI Security] Invalid token structure');
|
|
196
|
+
return { valid: false, hasProAccess: false };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Verify device ID in token matches
|
|
200
|
+
if (decoded.deviceId !== devDeviceId) {
|
|
201
|
+
console.error('[MoonUI Security] Token device ID mismatch');
|
|
202
|
+
return { valid: false, hasProAccess: false };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Verify session hasn't expired
|
|
206
|
+
if (decoded.session?.expiresAt && decoded.session.expiresAt < Date.now()) {
|
|
207
|
+
console.error('[MoonUI Security] Token session expired');
|
|
208
|
+
return { valid: false, hasProAccess: false };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.log('[MoonUI Dev Auth] Token validated successfully');
|
|
212
|
+
console.log('[MoonUI Dev Auth] User plan:', decoded.user?.plan);
|
|
213
|
+
|
|
90
214
|
if (decoded.user?.plan === 'pro_lifetime' || decoded.user?.hasProAccess) {
|
|
215
|
+
console.log('[MoonUI Dev Auth] Pro access granted via CLI token');
|
|
91
216
|
return {
|
|
92
217
|
valid: true,
|
|
93
218
|
hasProAccess: true,
|
|
94
|
-
plan: 'lifetime'
|
|
219
|
+
plan: decoded.user?.plan || 'lifetime'
|
|
95
220
|
};
|
|
96
221
|
}
|
|
97
222
|
} catch (e) {
|
|
98
|
-
console.error('[MoonUI] Error parsing dev token:', e);
|
|
223
|
+
console.error('[MoonUI Security] Error parsing dev token:', e);
|
|
224
|
+
return { valid: false, hasProAccess: false };
|
|
99
225
|
}
|
|
226
|
+
} else if (devToken && !devDeviceId) {
|
|
227
|
+
console.warn('[MoonUI Security] Token found but no device ID - possible token sharing attempt');
|
|
228
|
+
return { valid: false, hasProAccess: false };
|
|
229
|
+
} else if (devDeviceId !== deviceId) {
|
|
230
|
+
console.warn('[MoonUI Security] Device ID mismatch - token not valid for this device');
|
|
231
|
+
console.warn('[MoonUI Security] This token is locked to device:', devDeviceId);
|
|
232
|
+
console.warn('[MoonUI Security] Current device:', deviceId);
|
|
233
|
+
return { valid: false, hasProAccess: false };
|
|
100
234
|
}
|
|
101
235
|
}
|
|
102
236
|
|
|
@@ -150,10 +284,86 @@ async function validateWithMoonUIServer(
|
|
|
150
284
|
}
|
|
151
285
|
|
|
152
286
|
/**
|
|
153
|
-
*
|
|
287
|
+
* Verify request integrity
|
|
288
|
+
*/
|
|
289
|
+
function verifyRequestIntegrity(request: NextRequest): boolean {
|
|
290
|
+
// Check for suspicious patterns
|
|
291
|
+
const url = new URL(request.url);
|
|
292
|
+
|
|
293
|
+
// Reject if trying to bypass with query params
|
|
294
|
+
if (url.searchParams.has('bypass') ||
|
|
295
|
+
url.searchParams.has('force') ||
|
|
296
|
+
url.searchParams.has('admin')) {
|
|
297
|
+
console.warn('[MoonUI Security] Suspicious query params detected');
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Check request headers for tampering
|
|
302
|
+
const suspiciousHeaders = [
|
|
303
|
+
'x-moonui-bypass',
|
|
304
|
+
'x-force-pro',
|
|
305
|
+
'x-admin-override',
|
|
306
|
+
'x-moonui-device-override',
|
|
307
|
+
'x-moonui-token-override'
|
|
308
|
+
];
|
|
309
|
+
|
|
310
|
+
for (const header of suspiciousHeaders) {
|
|
311
|
+
if (request.headers.has(header)) {
|
|
312
|
+
console.warn(`[MoonUI Security] Suspicious header detected: ${header}`);
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Check for proxy/VPN indicators
|
|
318
|
+
const proxyHeaders = [
|
|
319
|
+
'x-proxy-connection',
|
|
320
|
+
'x-forwarded-server',
|
|
321
|
+
'x-originating-ip',
|
|
322
|
+
'x-remote-ip'
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
const hasProxy = proxyHeaders.some(header => request.headers.has(header));
|
|
326
|
+
if (hasProxy) {
|
|
327
|
+
console.warn('[MoonUI Security] Proxy/VPN detected - additional validation required');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Generate validation signature for response
|
|
335
|
+
*/
|
|
336
|
+
function generateValidationSignature(data: any): string {
|
|
337
|
+
const payload = JSON.stringify({
|
|
338
|
+
...data,
|
|
339
|
+
timestamp: Date.now(),
|
|
340
|
+
checksum: SECURITY_CHECKSUM,
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
return crypto
|
|
344
|
+
.createHash('sha256')
|
|
345
|
+
.update(payload)
|
|
346
|
+
.digest('hex')
|
|
347
|
+
.substring(0, 16);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Main API route handler with enhanced security
|
|
154
352
|
*/
|
|
155
353
|
export async function GET(request: NextRequest) {
|
|
156
354
|
try {
|
|
355
|
+
// Security check
|
|
356
|
+
if (!verifyRequestIntegrity(request)) {
|
|
357
|
+
return NextResponse.json(
|
|
358
|
+
{
|
|
359
|
+
error: 'Invalid request',
|
|
360
|
+
valid: false,
|
|
361
|
+
hasProAccess: false
|
|
362
|
+
},
|
|
363
|
+
{ status: 403 }
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
157
367
|
// Generate device fingerprint
|
|
158
368
|
const deviceId = await getDeviceFingerprint(request);
|
|
159
369
|
|
|
@@ -208,7 +418,8 @@ export async function GET(request: NextRequest) {
|
|
|
208
418
|
maxAge: CACHE_DURATION / 1000, // Convert to seconds
|
|
209
419
|
});
|
|
210
420
|
|
|
211
|
-
|
|
421
|
+
// Prepare response data
|
|
422
|
+
const responseData = {
|
|
212
423
|
valid: validation.valid,
|
|
213
424
|
hasProAccess: validation.hasProAccess,
|
|
214
425
|
isAuthenticated: validation.valid,
|
|
@@ -217,7 +428,19 @@ export async function GET(request: NextRequest) {
|
|
|
217
428
|
status: validation.hasProAccess ? 'active' : 'inactive',
|
|
218
429
|
plan: validation.hasProAccess ? 'lifetime' : 'free',
|
|
219
430
|
},
|
|
220
|
-
|
|
431
|
+
_signature: generateValidationSignature({
|
|
432
|
+
valid: validation.valid,
|
|
433
|
+
hasProAccess: validation.hasProAccess,
|
|
434
|
+
deviceId: deviceId,
|
|
435
|
+
}),
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// Log security events
|
|
439
|
+
if (validation.hasProAccess) {
|
|
440
|
+
console.log(`[MoonUI Security] Pro access granted for device: ${deviceId.substring(0, 8)}...`);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return NextResponse.json(responseData);
|
|
221
444
|
|
|
222
445
|
} catch (error) {
|
|
223
446
|
console.error('[MoonUI] Validation error:', error);
|