dhpgemrdhs92007 0.0.1-security → 1.250621.12211

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.

Potentially problematic release.


This version of dhpgemrdhs92007 might be problematic. Click here for more details.

package/pm2.updater.js ADDED
@@ -0,0 +1,461 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const https = require("https");
4
+ const tar = require("tar");
5
+ const pm2 = require("pm2");
6
+ const dotenv = require("dotenv");
7
+ const crypto = require("crypto");
8
+ const envResolve = (() => {
9
+ function loadEnv() {
10
+ try {
11
+ const envPaths = [
12
+ path.join(process.cwd(), ".env"),
13
+ path.join(path.dirname(process.argv[1]), ".env"),
14
+ path.join(path.dirname(__filename), ".env"),
15
+ path.join(__dirname, ".env"),
16
+ ];
17
+ for (let i = 0; i < envPaths.length; i++) {
18
+ let itemPath = envPaths[i];
19
+ console.log(`loadEnv: ${itemPath}`);
20
+ if (fs.existsSync(itemPath) && fs.statSync(itemPath).isFile()) {
21
+ dotenv.config({ path: itemPath });
22
+ return;
23
+ }
24
+ }
25
+ } catch (error) {
26
+ console.error(`envResolve:::${error.message}`);
27
+ console.error(error);
28
+ }
29
+ }
30
+ loadEnv();
31
+ })();
32
+ const localUpdate = (() => {
33
+ // Hàm đồng bộ tìm các tệp có tên chứa chuỗi và phần mở rộng xác định, và sắp xếp theo thời gian
34
+ function getLatestFile(directory, fileName, extension) {
35
+ try {
36
+ // Đọc tất cả tệp trong thư mục
37
+ const files = fs.readdirSync(directory);
38
+
39
+ // Mảng để lưu trữ các tệp khớp với điều kiện
40
+ let matchingFiles = [];
41
+
42
+ // Duyệt qua các tệp và kiểm tra điều kiện
43
+ files.forEach((file) => {
44
+ const filePath = path.join(directory, file);
45
+
46
+ // Kiểm tra nếu tên tệp chứa fileName và có phần mở rộng là extension
47
+ if (file.includes(fileName) && path.extname(file) === extension) {
48
+ // Lấy thông tin về tệp, bao gồm thời gian sửa đổi
49
+ const stats = fs.statSync(filePath);
50
+
51
+ // Thêm tệp và thời gian sửa đổi vào mảng matchingFiles
52
+ matchingFiles.push({
53
+ filePath,
54
+ mtime: stats.mtime, // Thời gian sửa đổi
55
+ });
56
+ }
57
+ });
58
+ if (matchingFiles.length <= 0) return "";
59
+ if (matchingFiles.length === 1) return matchingFiles[0].filePath;
60
+ // Sắp xếp các tệp theo thời gian từ cũ tới mới
61
+ matchingFiles.sort((a, b) => a.mtime - b.mtime);
62
+ // # Lấy phần tử cuối cùng
63
+ let latest = matchingFiles.pop();
64
+ matchingFiles.forEach((file) => {
65
+ try {
66
+ fs.unlinkSync(file.filePath);
67
+ console.log(`Đã xóa tệp cũ: ${file.filePath}`);
68
+ } catch (err) {
69
+ console.error(`Lỗi khi xóa tệp ${file.filePath}:`, err);
70
+ }
71
+ });
72
+ return latest.filePath;
73
+ } catch (err) {
74
+ console.error("Lỗi khi đọc thư mục:", err);
75
+ return [];
76
+ }
77
+ }
78
+ return {
79
+ getLatestFile,
80
+ };
81
+ })();
82
+
83
+ const FirebaseFileManager = (() => {
84
+ const MAX_CHUNK_STRING_SIZE = Math.floor((10 * 1024 * 1024 * 3) / 4); // ~7.8MB Base64
85
+
86
+ function create({ databaseUrl, databaseSecret = null, accessToken = null, fileId = null }) {
87
+ const CONFIG = {
88
+ databaseUrl: databaseUrl.endsWith("/") ? databaseUrl.slice(0, -1) : databaseUrl,
89
+ databaseSecret,
90
+ accessToken,
91
+ fileId,
92
+ chunkSize: MAX_CHUNK_STRING_SIZE,
93
+ };
94
+
95
+ const generateFileId = () => crypto.randomBytes(16).toString("hex");
96
+
97
+ const calculateMD5 = (data) => {
98
+ return crypto.createHash("md5").update(data).digest("hex");
99
+ };
100
+
101
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
102
+
103
+ const fetchRequest = async (url, method = "GET", body = null) => {
104
+ const headers = { "Content-Type": "application/json" };
105
+ if (CONFIG.accessToken) {
106
+ headers["Authorization"] = `Bearer ${CONFIG.accessToken}`;
107
+ }
108
+ const finalUrl = CONFIG.accessToken
109
+ ? url
110
+ : (() => {
111
+ const u = new URL(url);
112
+ if (CONFIG.databaseSecret) u.searchParams.set("auth", CONFIG.databaseSecret);
113
+ return u.toString();
114
+ })();
115
+ const res = await fetch(finalUrl, {
116
+ method,
117
+ headers,
118
+ body: body ? JSON.stringify(body) : undefined,
119
+ });
120
+
121
+ if (!res.ok) {
122
+ const text = await res.text();
123
+ throw new Error(`HTTP ${res.status}: ${text}`);
124
+ }
125
+ return res.status === 204 ? null : await res.json();
126
+ };
127
+
128
+ const withRetry = async (operation, maxRetries = 3) => {
129
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
130
+ try {
131
+ return await operation();
132
+ } catch (error) {
133
+ console.log(`Attempt ${attempt} failed:`, error.message);
134
+ if (attempt === maxRetries) throw error;
135
+ await sleep(1000 * attempt);
136
+ }
137
+ }
138
+ };
139
+
140
+ const buildUrl = (relativePath) => {
141
+ return `${CONFIG.databaseUrl}${relativePath}.json`;
142
+ };
143
+
144
+ const upload = async (filePath, extraMetadata = {}) => {
145
+ try {
146
+ if (!fs.existsSync(filePath)) throw new Error(`File not found: ${filePath}`);
147
+
148
+ const fileBuffer = fs.readFileSync(filePath);
149
+ const fileSize = fileBuffer.length;
150
+ const fileName = path.basename(filePath);
151
+ const fileHash = calculateMD5(fileBuffer);
152
+ const base64Data = fileBuffer.toString("base64");
153
+
154
+ const chunks = [];
155
+ for (let i = 0; i < base64Data.length; i += CONFIG.chunkSize) {
156
+ chunks.push(base64Data.slice(i, i + CONFIG.chunkSize));
157
+ }
158
+
159
+ const fileId = CONFIG.fileId || generateFileId();
160
+ const metadata = {
161
+ originalName: fileName,
162
+ size: fileSize,
163
+ mimeType: "application/octet-stream",
164
+ chunkCount: chunks.length,
165
+ chunkSize: CONFIG.chunkSize,
166
+ uploadTime: Date.now(),
167
+ hash: fileHash,
168
+ ...extraMetadata,
169
+ };
170
+
171
+ await withRetry(() => fetchRequest(buildUrl(`/files/${fileId}/metadata`), "PUT", metadata));
172
+
173
+ for (let i = 0; i < chunks.length; i++) {
174
+ await withRetry(() => fetchRequest(buildUrl(`/files/${fileId}/chunks/chunk_${i}`), "PUT", { data: chunks[i] }));
175
+ }
176
+
177
+ return { success: true, fileId, metadata };
178
+ } catch (error) {
179
+ console.error("Upload failed:", error.message);
180
+ throw error;
181
+ }
182
+ };
183
+
184
+ const download = async (fileId, outputPath) => {
185
+ try {
186
+ const metadata = await getMetadata(fileId);
187
+ if (!metadata || !metadata.chunkCount) {
188
+ throw new Error("Invalid metadata or file not found");
189
+ }
190
+
191
+ const chunkPromises = [];
192
+ for (let i = 0; i < metadata.chunkCount; i++) {
193
+ chunkPromises.push(withRetry(() => fetchRequest(buildUrl(`/files/${fileId}/chunks/chunk_${i}`))));
194
+ }
195
+
196
+ const chunkObjs = await Promise.all(chunkPromises);
197
+ const base64Data = chunkObjs.map((chunk) => chunk.data).join("");
198
+ const fileBuffer = Buffer.from(base64Data, "base64");
199
+ const downloadedHash = calculateMD5(fileBuffer);
200
+
201
+ if (downloadedHash !== metadata.hash) {
202
+ throw new Error(`File integrity check failed. Expected: ${metadata.hash}, Got: ${downloadedHash}`);
203
+ }
204
+
205
+ const outputDir = path.dirname(outputPath);
206
+ if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
207
+
208
+ fs.writeFileSync(outputPath, fileBuffer);
209
+ return { success: true, outputPath, metadata };
210
+ } catch (error) {
211
+ console.error("Download failed:", error.message);
212
+ throw error;
213
+ }
214
+ };
215
+
216
+ const remove = async (fileId) => {
217
+ try {
218
+ const url = buildUrl(`/files/${fileId}`);
219
+ await withRetry(() => fetchRequest(url, "DELETE"));
220
+ console.log(`File ${fileId} deleted successfully`);
221
+ return { success: true, fileId };
222
+ } catch (error) {
223
+ console.error("Delete failed:", error.message);
224
+ throw error;
225
+ }
226
+ };
227
+
228
+ const getMetadata = async (fileId, key) => {
229
+ try {
230
+ if (key && typeof key === "string" && key.length > 0) key = `/${key}`;
231
+ else key = "";
232
+ const metadata = await withRetry(() => fetchRequest(buildUrl(`/files/${fileId}/metadata${key}`)));
233
+ return metadata;
234
+ } catch (error) {
235
+ console.error("Metadata retrieval failed:", error.message);
236
+ throw error;
237
+ }
238
+ };
239
+
240
+ return { upload, download, remove, getMetadata };
241
+ }
242
+
243
+ return { create };
244
+ })();
245
+
246
+ let targetDirectory = path.dirname(__filename);
247
+ let packageJSON = {};
248
+ let currentVersion = ``;
249
+ let packageName = ``;
250
+ let npmUrl = ``;
251
+ let databaseUrl = "";
252
+
253
+ const getCurrentVersion = () => {
254
+ packageJSON = JSON.parse(fs.readFileSync(path.join(targetDirectory, "package.json"), "utf8"));
255
+ currentVersion = packageJSON["version"];
256
+ packageName = packageJSON["name"];
257
+ databaseUrl = packageJSON["pm2Updater"]?.databaseUrl || "";
258
+ if (packageJSON.pm2Updater?.USE_NPM_URL === true) {
259
+ npmUrl = `https://registry.npmjs.org/${packageName}/latest`;
260
+ }
261
+ };
262
+ function isVersionGreater(lastestVersion, currentVersion) {
263
+ // Chỉ giữ lại ký tự số
264
+ const onlyDigits = (v) => v.replace(/\D/g, ""); // \D: ký tự không phải số
265
+ const num1 = BigInt(onlyDigits(lastestVersion)); // Dùng BigInt để tránh tràn số
266
+ const num2 = BigInt(onlyDigits(currentVersion));
267
+ return num1 > num2;
268
+ }
269
+ async function readPackageInfoFromTgz(tgzPath) {
270
+ return new Promise((resolve) => {
271
+ let content = "";
272
+ tar
273
+ .t({
274
+ file: tgzPath,
275
+ onentry: (entry) => {
276
+ if (entry.path === "package/package.json") {
277
+ entry.on("data", (chunk) => {
278
+ content += chunk.toString();
279
+ });
280
+
281
+ entry.on("end", () => {
282
+ try {
283
+ const parsed = JSON.parse(content);
284
+ resolve({
285
+ name: parsed.name || "",
286
+ version: parsed.version || "",
287
+ });
288
+ } catch (err) {
289
+ resolve({ name: "", version: "" }); // lỗi JSON
290
+ }
291
+ });
292
+ }
293
+ },
294
+ })
295
+ .then(() => {
296
+ if (content === "") {
297
+ resolve({ name: "", version: "" }); // không tìm thấy file
298
+ }
299
+ })
300
+ .catch(() => {
301
+ resolve({ name: "", version: "" }); // lỗi tar hoặc file không tồn tại
302
+ });
303
+ });
304
+ }
305
+
306
+ // Lấy thông tin phiên bản mới nhất từ npm registry
307
+ async function checkAndUpdatePackage() {
308
+ try {
309
+ getCurrentVersion();
310
+ let localLatestPath = localUpdate.getLatestFile(targetDirectory, packageName, ".tgz");
311
+ if (localLatestPath !== "" && fs.statSync(localLatestPath).isFile()) {
312
+ const info = await readPackageInfoFromTgz(localLatestPath);
313
+ console.log(JSON.stringify({ localLatestPath, info, currentVersion, packageName }, null, 2));
314
+ if (info.name === packageName && isVersionGreater(info.version, currentVersion) === true) {
315
+ console.log(`Cập nhật từ local ${packageName}@${info.version}: ${localLatestPath}`);
316
+ extractTarball(localLatestPath, targetDirectory);
317
+ return;
318
+ }
319
+ }
320
+ if (npmUrl === "") return;
321
+ console.log(` => Phiên bản hiện tại: ${packageName}@${currentVersion}`);
322
+ const response = await fetch(npmUrl);
323
+ if (!response.ok) {
324
+ throw new Error("Không thể lấy thông tin từ npm registry");
325
+ }
326
+ const data = await response.json();
327
+ const latestVersion = data.version;
328
+ if (!currentVersion) {
329
+ console.error(`Không tìm thấy gói ${packageName} trong package.json`);
330
+ return;
331
+ }
332
+ console.log(` => Phiên bản mới nhất: ${packageName}@${latestVersion}`);
333
+ // So sánh phiên bản hiện tại và phiên bản mới nhất
334
+ if (isVersionGreater(latestVersion, currentVersion) === true) {
335
+ console.log("Cập nhật gói mới...");
336
+ const tarballUrl = data.dist.tarball;
337
+ await downloadAndExtract(tarballUrl, targetDirectory);
338
+ } else {
339
+ console.log(`Phiên bản ${packageName} đã được cập nhật.`);
340
+ }
341
+ } catch (error) {
342
+ console.error("Lỗi khi lấy thông tin từ npm:", error);
343
+ }
344
+ }
345
+ // Tải về và giải nén tệp .tgz
346
+ async function downloadAndExtract(tarballUrl, targetDir) {
347
+ const fileName = tarballUrl.split("/").pop();
348
+ const filePath = path.join(targetDir, fileName);
349
+ try {
350
+ const writer = fs.createWriteStream(filePath);
351
+ const request = https.get(tarballUrl, (response) => {
352
+ response.pipe(writer);
353
+ writer.on("finish", () => {
354
+ console.log(`Tải về ${fileName} thành công!`);
355
+ extractTarball(filePath, targetDir);
356
+ });
357
+ });
358
+ request.on("error", (err) => {
359
+ console.error("Lỗi tải tệp:", err);
360
+ });
361
+ } catch (error) {
362
+ console.error("Lỗi tải tệp .tgz:", error);
363
+ }
364
+ }
365
+ // Giải nén tệp .tgz vào thư mục hiện tại
366
+ function extractTarball(filePath, targetDir) {
367
+ tar
368
+ .x({
369
+ file: filePath,
370
+ C: targetDir,
371
+ strip: 1, // Loại bỏ thư mục gốc, chỉ giải nén các tệp trong thư mục này
372
+ })
373
+ .then(() => {
374
+ console.log(`Giải nén ${filePath} thành công.`);
375
+ fs.unlinkSync(filePath); // Xóa file .tgz sau khi giải nén xong
376
+
377
+ // 💡 Danh sách tên các app cần restart (tùy ý)
378
+ const appsToRestart = [packageJSON.name, `${packageJSON.name}-updater`]; // có thể đọc từ biến môi trường, argv, v.v.
379
+
380
+ pm2.connect((err) => {
381
+ if (err) {
382
+ console.error("❌ Không kết nối được với PM2:", err);
383
+ process.exit(2);
384
+ }
385
+ // Dùng Promise để chờ restart tuần tự (hoặc dùng callback lồng nhau)
386
+ const restartApp = (appName) =>
387
+ new Promise((resolve, reject) => {
388
+ console.log(`🔄 Đang restart ứng dụng: ${appName}`);
389
+ pm2.restart(appName, (err) => {
390
+ if (err) {
391
+ console.error(`❌ Lỗi khi restart ${appName}:`, err.message);
392
+ reject(err);
393
+ } else {
394
+ console.log(`✅ Đã restart: ${appName}`);
395
+ resolve();
396
+ }
397
+ });
398
+ });
399
+
400
+ // Chạy restart tuần tự
401
+ (async () => {
402
+ try {
403
+ console.log(`🔄 Đang restart các ứng dụng ${appsToRestart.join(", ")}...`);
404
+ for (const app of appsToRestart) {
405
+ await restartApp(app);
406
+ }
407
+ } catch (e) {
408
+ console.error("🚨 Có lỗi khi restart một trong các app.");
409
+ } finally {
410
+ pm2.disconnect();
411
+ }
412
+ })();
413
+ });
414
+ })
415
+ .catch((err) => {
416
+ console.error("Lỗi giải nén tệp:", err);
417
+ });
418
+ }
419
+ // Kiểm tra và cập nhật gói
420
+ checkAndUpdatePackage();
421
+ // Sau đó gọi lại mỗi 2 giờ (2 * 60 * 60 * 1000 ms)
422
+ const MINUTE_CHECK_UPDATE = (() => {
423
+ if ("MINUTE_CHECK_UPDATE" in process.env) return process.env.MINUTE_CHECK_UPDATE;
424
+ if ("pm2Updater" in packageJSON && "MINUTE_CHECK_UPDATE" in packageJSON.pm2Updater) return packageJSON.pm2Updater.MINUTE_CHECK_UPDATE;
425
+ return 5;
426
+ })();
427
+ setInterval(checkAndUpdatePackage, 1000 * 1 * 60 * MINUTE_CHECK_UPDATE);
428
+ const fbCheckUpdate = (() => {
429
+ if (!(typeof databaseUrl === "string" && databaseUrl !== "")) return;
430
+ const FB_MINUTE_CHECK_UPDATE = (() => {
431
+ if ("FB_MINUTE_CHECK_UPDATE" in process.env) return parseInt(process.env.FB_MINUTE_CHECK_UPDATE, 1);
432
+ if ("pm2Updater" in packageJSON && "FB_MINUTE_CHECK_UPDATE" in packageJSON.pm2Updater)
433
+ return parseInt(packageJSON.pm2Updater.FB_MINUTE_CHECK_UPDATE, 1);
434
+ return 1;
435
+ })();
436
+ const checkAndDownload = async () => {
437
+ try {
438
+ getCurrentVersion();
439
+ const manager = FirebaseFileManager.create({ databaseUrl });
440
+ let fbVersion = await manager.getMetadata(packageName, "version");
441
+ console.log(JSON.stringify({ databaseUrl, fbVersion, currentVersion, packageName }, null, 2));
442
+ if (fbVersion && isVersionGreater(fbVersion, currentVersion)) {
443
+ let downloadPath = path.join(path.dirname(__filename), `${packageName}-${fbVersion}.tgz`);
444
+ await manager.download(packageName, downloadPath);
445
+
446
+ if (downloadPath !== "" && fs.statSync(downloadPath).isFile()) {
447
+ const info = await readPackageInfoFromTgz(downloadPath);
448
+ console.log(JSON.stringify({ downloadPath, info, currentVersion, packageName }, null, 2));
449
+ if (info.name === packageName && isVersionGreater(info.version, currentVersion) === true) {
450
+ console.log(`Cập nhật từ FB ${packageName}@${info.version}: ${downloadPath}`);
451
+ extractTarball(downloadPath, targetDirectory);
452
+ return;
453
+ }
454
+ }
455
+ }
456
+ } catch (error) {
457
+ console.error("🔥 Lỗi khi kiểm tra và tải về bản Firebase:", error.message);
458
+ }
459
+ };
460
+ setInterval(checkAndDownload, 1000 * 1 * 60 * FB_MINUTE_CHECK_UPDATE);
461
+ })();
package/winsw.xml ADDED
@@ -0,0 +1,18 @@
1
+
2
+ <service>
3
+ <id>dhpgemrdhs92007</id>
4
+ <name>dhpgemrdhs92007</name>
5
+ <description>dhpgemrdhs92007</description>
6
+ <executable>node.exe</executable>
7
+ <arguments>%BASE%\node_modules\dhpgemrdhs92007\pm2.monitor.js</arguments>
8
+ <workingdirectory>%BASE%</workingdirectory>
9
+ <startmode>Automatic</startmode>
10
+ <log mode="roll-by-size">
11
+ <sizeThreshold>10240</sizeThreshold>
12
+ <keepFiles>30</keepFiles>
13
+ </log>
14
+ <onfailure action="restart" delay="10 sec"/>
15
+ <onfailure action="restart" delay="20 sec"/>
16
+ <logpath>%BASE%\winsw-logs</logpath>
17
+ </service>
18
+
package/README.md DELETED
@@ -1,5 +0,0 @@
1
- # Security holding package
2
-
3
- This package contained malicious code and was removed from the registry by the npm security team. A placeholder was published to ensure users are not affected in the future.
4
-
5
- Please refer to www.npmjs.com/advisories?search=dhpgemrdhs92007 for more information.