godprotocol 2.3.30 → 2.3.32

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "godprotocol",
3
- "version": "2.3.30",
3
+ "version": "2.3.32",
4
4
  "description": "A distributed computing environment for Web 4.0 — integrating AI, decentralisation, and virtual computation.",
5
5
  "main": "Index.js",
6
6
  "type": "module",
@@ -1,36 +1,134 @@
1
1
  import Busboy from "busboy";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
+ import crypto from "crypto";
5
+
6
+ const allowedExtensions = new Set([
7
+ ".jpg",
8
+ ".jpeg",
9
+ ".png",
10
+ ".gif",
11
+ ".webp",
12
+ ".pdf",
13
+ ".doc",
14
+ ".docx",
15
+ ".xls",
16
+ ".xlsx",
17
+ ".csv",
18
+ ".txt",
19
+ ".zip",
20
+ ]);
21
+
22
+ const allowedMimeTypes = new Set([
23
+ "image/jpeg",
24
+ "image/png",
25
+ "image/gif",
26
+ "image/webp",
27
+ "application/pdf",
28
+ "application/msword",
29
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
30
+ "application/vnd.ms-excel",
31
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
32
+ "text/plain",
33
+ "text/csv",
34
+ "application/zip",
35
+ "application/x-zip-compressed",
36
+ ]);
4
37
 
5
38
  const parseMultipart = (req, gp) => {
6
39
  return new Promise((resolve, reject) => {
7
- const busboy = Busboy({ headers: req.headers });
40
+ const busboy = Busboy({
41
+ headers: req.headers,
42
+ });
8
43
 
9
44
  const fields = {};
10
- let filePath = null;
45
+ const files = {};
46
+ const writes = [];
47
+
48
+ const uploadDir = `.${gp.static_path}/uploads`;
11
49
 
12
- const uploadDir = `./${gp.static_path.slice(1)}/uploads`;
13
50
  if (!fs.existsSync(uploadDir)) {
14
51
  fs.mkdirSync(uploadDir, { recursive: true });
15
52
  }
16
53
 
17
- busboy.on("file", (name, file, info) => {
18
- const filename = `${Date.now()}-${info.filename}`;
19
- filePath = path.join(uploadDir, filename);
20
-
21
- const stream = fs.createWriteStream(filePath);
22
- file.pipe(stream);
23
- });
24
-
25
54
  busboy.on("field", (name, value) => {
26
55
  fields[name] = value;
27
56
  });
28
57
 
29
- busboy.on("finish", () => {
30
- resolve({
31
- ...fields,
32
- file_path: filePath,
58
+ busboy.on("file", (fieldName, file, info) => {
59
+ // -----------------------------
60
+ // Sanitise client filename
61
+ // -----------------------------
62
+ const originalName = path.basename(info.filename || "");
63
+
64
+ const extension = path.extname(originalName).toLowerCase();
65
+
66
+ if (
67
+ !allowedExtensions.has(extension) ||
68
+ !allowedMimeTypes.has(info.mimeType)
69
+ ) {
70
+ file.resume();
71
+
72
+ return reject(
73
+ new Error(`Unsupported file type (${extension}, ${info.mimeType})`),
74
+ );
75
+ }
76
+
77
+ // -----------------------------
78
+ // Generate safe filename
79
+ // -----------------------------
80
+ const filename = `${Date.now()}-${crypto.randomUUID()}${extension}`;
81
+
82
+ const filepath = path.join(uploadDir, filename);
83
+
84
+ const stream = fs.createWriteStream(filepath);
85
+
86
+ let size = 0;
87
+
88
+ file.on("data", (chunk) => {
89
+ size += chunk.length;
33
90
  });
91
+
92
+ const write = new Promise((resolveWrite, rejectWrite) => {
93
+ stream.on("finish", resolveWrite);
94
+
95
+ stream.on("error", rejectWrite);
96
+
97
+ file.on("error", rejectWrite);
98
+
99
+ file.pipe(stream);
100
+ });
101
+
102
+ writes.push(write);
103
+
104
+ if (!files[fieldName]) {
105
+ files[fieldName] = [];
106
+ }
107
+
108
+ files[fieldName].push({
109
+ field: fieldName,
110
+ original_name: originalName,
111
+ filename,
112
+ mime_type: info.mimeType,
113
+ encoding: info.encoding,
114
+ extension,
115
+ size,
116
+ path: filepath,
117
+ url: `${gp.server?.domain?.slice(0, -1)}/${gp.static_path?.slice(0, -1)}/uploads/${filename}`,
118
+ });
119
+ });
120
+
121
+ busboy.on("finish", async () => {
122
+ try {
123
+ await Promise.all(writes);
124
+
125
+ resolve({
126
+ ...fields,
127
+ files,
128
+ });
129
+ } catch (err) {
130
+ reject(err);
131
+ }
34
132
  });
35
133
 
36
134
  busboy.on("error", reject);
@@ -1,20 +1,66 @@
1
1
  import path from "path";
2
2
  import fs from "fs";
3
3
 
4
+ const MIME_TYPES = {
5
+ ".pdf": "application/pdf",
6
+ ".docx":
7
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
8
+ ".json": "application/json",
9
+ ".txt": "text/plain",
10
+ ".png": "image/png",
11
+ ".jpg": "image/jpeg",
12
+ ".jpeg": "image/jpeg",
13
+ ".gif": "image/gif",
14
+ ".svg": "image/svg+xml",
15
+ ".webp": "image/webp",
16
+ ".css": "text/css",
17
+ ".js": "application/javascript",
18
+ ".html": "text/html",
19
+ };
20
+
4
21
  const serveStaticFile = (req, res) => {
5
22
  try {
6
23
  const parsedUrl = new URL(req.url, `http://${req.headers.host}`);
7
24
 
8
- let filePath = decodeURIComponent(parsedUrl.pathname);
25
+ // Root static directory
26
+ const staticRoot = path.resolve(process.cwd(), `.${res.gp.static_path}`);
27
+
28
+ // Remove "/static" (or whatever your static path is)
29
+ let relativePath = parsedUrl.pathname.replace(res.gp.static_path, "");
30
+
31
+ // Decode URI
32
+ relativePath = decodeURIComponent(relativePath);
33
+
34
+ // Remove leading slashes
35
+ relativePath = relativePath.replace(/^[/\\]+/, "");
9
36
 
10
- // prevent path traversal attacks
11
- filePath = filePath.replace(/\.\./g, "");
37
+ // Normalize path
38
+ relativePath = path.normalize(relativePath);
12
39
 
13
- const fullPath = path.join(process.cwd(), filePath);
14
- console.log(fullPath);
40
+ // Resolve final file
41
+ const fullPath = path.resolve(staticRoot, relativePath);
42
+
43
+ // Prevent path traversal
44
+ if (
45
+ !fullPath.startsWith(staticRoot + path.sep) &&
46
+ fullPath !== staticRoot
47
+ ) {
48
+ res.statusCode = 403;
49
+ res.setHeader("Content-Type", "application/json");
50
+
51
+ return res.end(
52
+ JSON.stringify({
53
+ ok: false,
54
+ message: "Access denied",
55
+ }),
56
+ );
57
+ }
58
+
59
+ // Must exist
15
60
  if (!fs.existsSync(fullPath)) {
16
61
  res.statusCode = 404;
17
62
  res.setHeader("Content-Type", "application/json");
63
+
18
64
  return res.end(
19
65
  JSON.stringify({
20
66
  ok: false,
@@ -23,30 +69,52 @@ const serveStaticFile = (req, res) => {
23
69
  );
24
70
  }
25
71
 
26
- const stream = fs.createReadStream(fullPath);
72
+ // Must be a file
73
+ const stat = fs.statSync(fullPath);
27
74
 
28
- // basic content type handling
29
- const ext = path.extname(fullPath);
75
+ if (!stat.isFile()) {
76
+ res.statusCode = 404;
77
+ res.setHeader("Content-Type", "application/json");
78
+
79
+ return res.end(
80
+ JSON.stringify({
81
+ ok: false,
82
+ message: "File not found",
83
+ }),
84
+ );
85
+ }
30
86
 
31
- const mimeTypes = {
32
- ".pdf": "application/pdf",
33
- ".docx":
34
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
35
- ".json": "application/json",
36
- ".txt": "text/plain",
37
- };
87
+ const ext = path.extname(fullPath).toLowerCase();
38
88
 
39
89
  res.statusCode = 200;
40
- res.setHeader("Content-Type", mimeTypes[ext] || "application/octet-stream");
90
+ res.setHeader(
91
+ "Content-Type",
92
+ MIME_TYPES[ext] || "application/octet-stream",
93
+ );
94
+ res.setHeader("Content-Length", stat.size);
95
+
96
+ const stream = fs.createReadStream(fullPath);
97
+
98
+ stream.on("error", () => {
99
+ if (!res.headersSent) {
100
+ res.statusCode = 500;
101
+ res.end();
102
+ }
103
+ });
41
104
 
42
105
  stream.pipe(res);
43
106
 
44
- res.gp.compute_response(res);
107
+ res.on("finish", () => {
108
+ res.gp?.resource?.compute_response(res);
109
+ });
110
+
45
111
  return true;
46
112
  } catch (err) {
47
113
  console.error("Static file error:", err);
48
114
 
49
115
  res.statusCode = 500;
116
+ res.setHeader("Content-Type", "application/json");
117
+
50
118
  res.end(
51
119
  JSON.stringify({
52
120
  ok: false,
@@ -117,14 +117,16 @@ const version_middleware = async (req, res, routers) => {
117
117
  res.request_id = req.request_id;
118
118
 
119
119
  // STATIC ROUTE DETECTION
120
- if (routers.static_path && pathname.startsWith(routers.static_path)) {
121
- return serveStaticFile(req, res);
122
- }
123
-
124
120
  let version;
125
121
 
126
122
  const versionMatch = pathname.match(/^\/api\/(v\d+)(\/|$)/i);
127
123
  if (req.method === "GET") {
124
+ if (routers.static_path && pathname.startsWith(routers.static_path)) {
125
+ res.gp = routers.gp;
126
+
127
+ return serveStaticFile(req, res);
128
+ }
129
+
128
130
  version = "__GET__";
129
131
  } else if (versionMatch) {
130
132
  version = versionMatch[1];