Skip to content

Develop -> Master (28 Oct)#2528

Merged
gorkem-bwl merged 505 commits into
masterfrom
develop
Oct 28, 2025
Merged

Develop -> Master (28 Oct)#2528
gorkem-bwl merged 505 commits into
masterfrom
develop

Conversation

@HarshP4585
Copy link
Copy Markdown
Collaborator

Describe your changes

  • Merge develop into master (28 Oct)

Write your issue number after "Fixes "

Enter the corresponding issue number after "Fixes #"

Please ensure all items are checked off before requesting a review:

  • I deployed the code locally.
  • I have performed a self-review of my code.
  • I have included the issue # in the PR.
  • I have labelled the PR correctly.
  • The issue I am working on is assigned to me.
  • I have avoided using hardcoded values to ensure scalability and maintain consistency across the application.
  • I have ensured that font sizes, color choices, and other UI elements are referenced from the theme.
  • My pull request is focused and addresses a single, specific feature.
  • If there are UI changes, I have attached a screenshot or video to this PR.

MuhammadKhalilzadeh and others added 30 commits October 21, 2025 10:42
Add right padding (pr: 14 / 112px) to main content area to match the left side gap. This prevents content from sticking to the browser's right border when the window is narrowed.
…content-area

fix: Add right padding to prevent content sticking to edge
"rel",
],
ALLOWED_URI_REGEXP:
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i,

Check warning

Code scanning / CodeQL

Overly permissive regular expression range Medium

Suspicious character range that is equivalent to [./0-9:].

Copilot Autofix

AI 7 months ago

To fix this issue, we need to rewrite the regular expression to make the character class explicit and unambiguous. Specifically, in [a-z+.-], the dash (-) should be either escaped (\-) or placed at the beginning or end of the character class to remove ambiguity; likewise, for [a-z+.-:], every character meant to be matched individually should be listed explicitly (not as part of a range).

In file Clients/src/presentation/components/PlateEditor.tsx, change line 96 so that ALLOWED_URI_REGEXP uses an explicit character class for +, ., and -. The correct fix is to replace [a-z+.-] with [a-z+.\-] and similar in all relevant places in the pattern, so that only the intended characters are matched. No new imports or methods are needed.


Suggested changeset 1
Clients/src/presentation/components/PlateEditor.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Clients/src/presentation/components/PlateEditor.tsx b/Clients/src/presentation/components/PlateEditor.tsx
--- a/Clients/src/presentation/components/PlateEditor.tsx
+++ b/Clients/src/presentation/components/PlateEditor.tsx
@@ -93,7 +93,7 @@
           "rel",
         ],
         ALLOWED_URI_REGEXP:
-          /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i,
+          /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
         ADD_ATTR: ["target"],
         FORBID_TAGS: [
           "script",
EOF
@@ -93,7 +93,7 @@
"rel",
],
ALLOWED_URI_REGEXP:
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i,
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
ADD_ATTR: ["target"],
FORBID_TAGS: [
"script",
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
"rel",
],
ALLOWED_URI_REGEXP:
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i,

Check warning

Code scanning / CodeQL

Overly permissive regular expression range Medium

Suspicious character range that is equivalent to [./0-9:].

Copilot Autofix

AI 6 months ago

To fix the problem, the regular expression should only allow the explicitly intended characters (., +, -, and :) rather than the range .-:, which includes many unintended characters. Specifically, replace [a-z+.-:] with [a-z.+-:] to individually enumerate only the characters allowed.

Detailed steps:

  • Edit file Clients/src/presentation/components/Policies/PolicyDetailsModal.tsx.
  • At line 452, change a-z+.-: to a-z.+-:.
  • No new imports are needed; just modify this string in-place.
  • No changes are needed elsewhere.

Suggested changeset 1
Clients/src/presentation/components/Policies/PolicyDetailsModal.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Clients/src/presentation/components/Policies/PolicyDetailsModal.tsx b/Clients/src/presentation/components/Policies/PolicyDetailsModal.tsx
--- a/Clients/src/presentation/components/Policies/PolicyDetailsModal.tsx
+++ b/Clients/src/presentation/components/Policies/PolicyDetailsModal.tsx
@@ -449,7 +449,7 @@
                     "rel",
                   ],
                   ALLOWED_URI_REGEXP:
-                    /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i,
+                    /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z.+-]+(?:[^a-z.+-:]|$))/i,
                   ADD_ATTR: ["target"],
                   FORBID_TAGS: [
                     "script",
EOF
@@ -449,7 +449,7 @@
"rel",
],
ALLOWED_URI_REGEXP:
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i,
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z.+-]+(?:[^a-z.+-:]|$))/i,
ADD_ATTR: ["target"],
FORBID_TAGS: [
"script",
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
"rel",
],
ALLOWED_URI_REGEXP:
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i,

Check warning

Code scanning / CodeQL

Overly permissive regular expression range Medium test

Suspicious character range that is equivalent to [./0-9:].

Copilot Autofix

AI 7 months ago

To make the regular expression character class unambiguous and safe, each of the special characters intended to be allowed (such as +, ., and -) should be listed explicitly and not as a range. Specifically:

  • In [a-z+.-], the dash (-) should be either escaped (\-) or moved to the start or end of the character class so it is not interpreted as a range. This avoids creating the unintended .-: range.
  • The other usages should also be checked (in this regex and elsewhere), but only code snippets provided should be changed.

For this file, edit line 73 of Clients/temp/test-xss-protection.js so that inside the regex, the dash - is placed at the start or end of the character class or escaped as \-, so that [a-z+.-] becomes [a-z+.\-] or [-a-z+.]. This change does not alter the intended matches or break existing functionality, but it prevents the ambiguous and dangerous character range. No new imports or dependencies are needed; only the regex itself needs to be rewritten.


Suggested changeset 1
Clients/temp/test-xss-protection.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Clients/temp/test-xss-protection.js b/Clients/temp/test-xss-protection.js
--- a/Clients/temp/test-xss-protection.js
+++ b/Clients/temp/test-xss-protection.js
@@ -70,7 +70,7 @@
     "rel",
   ],
   ALLOWED_URI_REGEXP:
-    /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i,
+    /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+\.\-]+(?:[^a-z+\.\-:]|$))/i,
   ADD_ATTR: ["target"],
   FORBID_TAGS: [
     "script",
EOF
@@ -70,7 +70,7 @@
"rel",
],
ALLOWED_URI_REGEXP:
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i,
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+\.\-]+(?:[^a-z+\.\-:]|$))/i,
ADD_ATTR: ["target"],
FORBID_TAGS: [
"script",
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
// Clean up temp file before returning error
if (tempFilePath) {
try {
await fs.promises.unlink(tempFilePath);

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 7 months ago

The problem is that tempFilePath, which is derived from file.path (coming from potentially uncontrolled user input), is passed directly to fs.promises.unlink to delete a file. To fix this securely, we must restrict deletion to files that reside inside a designated temporary directory, preventing the possibility of path traversal or deletion of unauthorized files due to malicious input. The fix involves:

  1. Define a safe temp files directory (Multer usually provides this, but it must be explicit in code).
  2. Resolve and normalize tempFilePath with path.resolve(); then, use fs.realpathSync to handle symlinks.
  3. Verify that the resolved path starts with the safe temp directory absolute path before proceeding with deletion.
  4. If the check fails, log a warning and skip deletion.

All places in the snippet where fs.promises.unlink(tempFilePath) is called must be updated to include this check.

Additional steps:

  • Add a constant for the temporary upload directory, if not already present (for example, /tmp/uploads or wherever Multer stores files).
  • Add path safety logic before every call to fs.promises.unlink(tempFilePath).

No new npm libraries are required for path checking; use Node's built-in path and fs modules.


Suggested changeset 1
Servers/controllers/fileManager.ctrl.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Servers/controllers/fileManager.ctrl.ts b/Servers/controllers/fileManager.ctrl.ts
--- a/Servers/controllers/fileManager.ctrl.ts
+++ b/Servers/controllers/fileManager.ctrl.ts
@@ -31,6 +31,8 @@
 
 const pipelineAsync = promisify(pipeline);
 
+// Define the directory where temporary uploads are stored by Multer
+const TEMP_UPLOAD_DIR = path.resolve("/tmp/uploads"); // Replace with your Multer temp directory if different
 /**
  * Upload file to file manager
  *
@@ -73,13 +75,7 @@
     if (!/^[a-zA-Z0-9_]+$/.test(tenant)) {
       // Clean up temp file before returning error
       if (tempFilePath) {
-        try {
-          await fs.promises.unlink(tempFilePath);
-        } catch (unlinkError: any) {
-          if (unlinkError.code !== 'ENOENT') {
-            console.error("Failed to clean up temporary file:", unlinkError);
-          }
-        }
+        await safeDeleteTempFile(tempFilePath);
       }
       return res.status(400).json(STATUS_CODE[400]("Invalid tenant identifier"));
     }
@@ -97,13 +93,7 @@
       });
       // Clean up temp file before returning error
       if (tempFilePath) {
-        try {
-          await fs.promises.unlink(tempFilePath);
-        } catch (unlinkError: any) {
-          if (unlinkError.code !== 'ENOENT') {
-            console.error("Failed to clean up temporary file:", unlinkError);
-          }
-        }
+        await safeDeleteTempFile(tempFilePath);
       }
       return res.status(400).json(STATUS_CODE[400](validation.error));
     }
@@ -113,13 +103,7 @@
 
     // Clean up temp file after successful processing
     if (tempFilePath) {
-      try {
-        await fs.promises.unlink(tempFilePath);
-      } catch (unlinkError: any) {
-        if (unlinkError.code !== 'ENOENT') {
-          console.error("Failed to clean up temporary file:", unlinkError);
-        }
-      }
+      await safeDeleteTempFile(tempFilePath);
     }
 
     await logSuccess({
EOF
@@ -31,6 +31,8 @@

const pipelineAsync = promisify(pipeline);

// Define the directory where temporary uploads are stored by Multer
const TEMP_UPLOAD_DIR = path.resolve("/tmp/uploads"); // Replace with your Multer temp directory if different
/**
* Upload file to file manager
*
@@ -73,13 +75,7 @@
if (!/^[a-zA-Z0-9_]+$/.test(tenant)) {
// Clean up temp file before returning error
if (tempFilePath) {
try {
await fs.promises.unlink(tempFilePath);
} catch (unlinkError: any) {
if (unlinkError.code !== 'ENOENT') {
console.error("Failed to clean up temporary file:", unlinkError);
}
}
await safeDeleteTempFile(tempFilePath);
}
return res.status(400).json(STATUS_CODE[400]("Invalid tenant identifier"));
}
@@ -97,13 +93,7 @@
});
// Clean up temp file before returning error
if (tempFilePath) {
try {
await fs.promises.unlink(tempFilePath);
} catch (unlinkError: any) {
if (unlinkError.code !== 'ENOENT') {
console.error("Failed to clean up temporary file:", unlinkError);
}
}
await safeDeleteTempFile(tempFilePath);
}
return res.status(400).json(STATUS_CODE[400](validation.error));
}
@@ -113,13 +103,7 @@

// Clean up temp file after successful processing
if (tempFilePath) {
try {
await fs.promises.unlink(tempFilePath);
} catch (unlinkError: any) {
if (unlinkError.code !== 'ENOENT') {
console.error("Failed to clean up temporary file:", unlinkError);
}
}
await safeDeleteTempFile(tempFilePath);
}

await logSuccess({
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
// Clean up temp file before returning error
if (tempFilePath) {
try {
await fs.promises.unlink(tempFilePath);

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 7 months ago

To fix this problem, we need to ensure that only files within a trusted temporary uploads directory are subject to deletion by the controller. This can be accomplished by:

  • Defining a constant with the absolute path to the server’s temp upload directory (e.g., where multer writes to).
  • Before calling unlink, normalize and resolve the tempFilePath, and ensure that the resolved path is actually within the trusted temp directory.
  • Only proceed with deletion if this validation passes; otherwise, skip unlink and potentially log an error.

Changes needed:

  • Add a constant (e.g., UPLOAD_TEMP_DIR) for the upload temp directory path (this can be configurable or set statically).
  • Update uses of fs.promises.unlink(tempFilePath) to first resolve tempFilePath and compare against the upload directory.
  • If the resolved path is not within the upload directory, skip unlink and log a warning.
  • Add necessary imports (path) and definitions within the provided code block only.

All code changes will be within the file Servers/controllers/fileManager.ctrl.ts as shown.


Suggested changeset 1
Servers/controllers/fileManager.ctrl.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Servers/controllers/fileManager.ctrl.ts b/Servers/controllers/fileManager.ctrl.ts
--- a/Servers/controllers/fileManager.ctrl.ts
+++ b/Servers/controllers/fileManager.ctrl.ts
@@ -27,6 +27,9 @@
 import * as path from "path";
 import * as fs from "fs";
 import { pipeline } from "stream";
+
+// Directory where uploaded temp files are placed (adjust to match actual multer config as necessary)
+const UPLOAD_TEMP_DIR = path.resolve(__dirname, "../../uploads/tmp");
 import { promisify } from "util";
 
 const pipelineAsync = promisify(pipeline);
@@ -74,7 +77,13 @@
       // Clean up temp file before returning error
       if (tempFilePath) {
         try {
-          await fs.promises.unlink(tempFilePath);
+          // Validate tempFilePath is under the trusted upload temp directory
+          const resolvedTemp = path.resolve(tempFilePath);
+          if (resolvedTemp.startsWith(UPLOAD_TEMP_DIR)) {
+            await fs.promises.unlink(resolvedTemp);
+          } else {
+            console.warn(`Attempted to unlink temp file outside safe directory: ${resolvedTemp}`);
+          }
         } catch (unlinkError: any) {
           if (unlinkError.code !== 'ENOENT') {
             console.error("Failed to clean up temporary file:", unlinkError);
@@ -98,7 +107,13 @@
       // Clean up temp file before returning error
       if (tempFilePath) {
         try {
-          await fs.promises.unlink(tempFilePath);
+          // Validate tempFilePath is under the trusted upload temp directory
+          const resolvedTemp = path.resolve(tempFilePath);
+          if (resolvedTemp.startsWith(UPLOAD_TEMP_DIR)) {
+            await fs.promises.unlink(resolvedTemp);
+          } else {
+            console.warn(`Attempted to unlink temp file outside safe directory: ${resolvedTemp}`);
+          }
         } catch (unlinkError: any) {
           if (unlinkError.code !== 'ENOENT') {
             console.error("Failed to clean up temporary file:", unlinkError);
@@ -114,7 +129,13 @@
     // Clean up temp file after successful processing
     if (tempFilePath) {
       try {
-        await fs.promises.unlink(tempFilePath);
+        // Validate tempFilePath is under the trusted upload temp directory
+        const resolvedTemp = path.resolve(tempFilePath);
+        if (resolvedTemp.startsWith(UPLOAD_TEMP_DIR)) {
+          await fs.promises.unlink(resolvedTemp);
+        } else {
+          console.warn(`Attempted to unlink temp file outside safe directory: ${resolvedTemp}`);
+        }
       } catch (unlinkError: any) {
         if (unlinkError.code !== 'ENOENT') {
           console.error("Failed to clean up temporary file:", unlinkError);
EOF
@@ -27,6 +27,9 @@
import * as path from "path";
import * as fs from "fs";
import { pipeline } from "stream";

// Directory where uploaded temp files are placed (adjust to match actual multer config as necessary)
const UPLOAD_TEMP_DIR = path.resolve(__dirname, "../../uploads/tmp");
import { promisify } from "util";

const pipelineAsync = promisify(pipeline);
@@ -74,7 +77,13 @@
// Clean up temp file before returning error
if (tempFilePath) {
try {
await fs.promises.unlink(tempFilePath);
// Validate tempFilePath is under the trusted upload temp directory
const resolvedTemp = path.resolve(tempFilePath);
if (resolvedTemp.startsWith(UPLOAD_TEMP_DIR)) {
await fs.promises.unlink(resolvedTemp);
} else {
console.warn(`Attempted to unlink temp file outside safe directory: ${resolvedTemp}`);
}
} catch (unlinkError: any) {
if (unlinkError.code !== 'ENOENT') {
console.error("Failed to clean up temporary file:", unlinkError);
@@ -98,7 +107,13 @@
// Clean up temp file before returning error
if (tempFilePath) {
try {
await fs.promises.unlink(tempFilePath);
// Validate tempFilePath is under the trusted upload temp directory
const resolvedTemp = path.resolve(tempFilePath);
if (resolvedTemp.startsWith(UPLOAD_TEMP_DIR)) {
await fs.promises.unlink(resolvedTemp);
} else {
console.warn(`Attempted to unlink temp file outside safe directory: ${resolvedTemp}`);
}
} catch (unlinkError: any) {
if (unlinkError.code !== 'ENOENT') {
console.error("Failed to clean up temporary file:", unlinkError);
@@ -114,7 +129,13 @@
// Clean up temp file after successful processing
if (tempFilePath) {
try {
await fs.promises.unlink(tempFilePath);
// Validate tempFilePath is under the trusted upload temp directory
const resolvedTemp = path.resolve(tempFilePath);
if (resolvedTemp.startsWith(UPLOAD_TEMP_DIR)) {
await fs.promises.unlink(resolvedTemp);
} else {
console.warn(`Attempted to unlink temp file outside safe directory: ${resolvedTemp}`);
}
} catch (unlinkError: any) {
if (unlinkError.code !== 'ENOENT') {
console.error("Failed to clean up temporary file:", unlinkError);
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
// Move file from temp directory to permanent location
// file.path contains the temp file path from multer.diskStorage
if (file.path) {
const readStream = fs.createReadStream(file.path);

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 7 months ago

To fix the problem, we must ensure that the path used for reading the file (file.path) is constrained to expected upload directories and cannot be manipulated to refer to sensitive or unintended files on the system. The typical approach here is to treat Multer's temporary directory as a "root" and check (after normalizing or resolving the path) that file.path is inside this directory before proceeding.

Specifically:

  • Identify the temp uploads directory used by Multer; by default, this could be the system's temp directory, or a custom path. For maximum safety, the code should enforce that only files under a known temp uploads root are allowed.
  • Before using fs.createReadStream(file.path), resolve the absolute path for file.path, and confirm it is a child of the known temp uploads root.
  • Only proceed if this validation passes, otherwise throw an error.

Since the code is only shown in Servers/utils/fileManager.utils.ts, the check should be implemented in the uploadFileToManager function just before reading from file.path.

We'll add:

  • Definition and calculation of the expected temp storage directory (e.g., process.env.TEMP_UPLOAD_ROOT or default to OS temp).
  • Validation logic above fs.createReadStream(...).
  • An error throw if the validation fails.
  • Optionally, import os if os.tmpdir() is used.

Suggested changeset 1
Servers/utils/fileManager.utils.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Servers/utils/fileManager.utils.ts b/Servers/utils/fileManager.utils.ts
--- a/Servers/utils/fileManager.utils.ts
+++ b/Servers/utils/fileManager.utils.ts
@@ -17,6 +17,7 @@
 import * as path from "path";
 import * as fs from "fs";
 import { promisify } from "util";
+import * as os from "os";
 import { randomBytes } from "crypto";
 import { sanitizeFilename } from "./validations/fileManagerValidation.utils";
 
@@ -55,7 +56,15 @@
   // Move file from temp directory to permanent location
   // file.path contains the temp file path from multer.diskStorage
   if (file.path) {
-    const readStream = fs.createReadStream(file.path);
+    // Validate the temp file path is under our temp directory root.
+    // You may want to set this to your Multer config, or default to OS temp dir.
+    const tempUploadsRoot = process.env.TEMP_UPLOAD_ROOT || os.tmpdir();
+    const resolvedFilePath = path.resolve(file.path);
+    const resolvedTempRoot = path.resolve(tempUploadsRoot);
+    if (!resolvedFilePath.startsWith(resolvedTempRoot + path.sep)) {
+      throw new Error("Invalid temp file path detected");
+    }
+    const readStream = fs.createReadStream(resolvedFilePath);
     const writeStream = fs.createWriteStream(permanentFilePath);
 
     await new Promise<void>((resolve, reject) => {
EOF
@@ -17,6 +17,7 @@
import * as path from "path";
import * as fs from "fs";
import { promisify } from "util";
import * as os from "os";
import { randomBytes } from "crypto";
import { sanitizeFilename } from "./validations/fileManagerValidation.utils";

@@ -55,7 +56,15 @@
// Move file from temp directory to permanent location
// file.path contains the temp file path from multer.diskStorage
if (file.path) {
const readStream = fs.createReadStream(file.path);
// Validate the temp file path is under our temp directory root.
// You may want to set this to your Multer config, or default to OS temp dir.
const tempUploadsRoot = process.env.TEMP_UPLOAD_ROOT || os.tmpdir();
const resolvedFilePath = path.resolve(file.path);
const resolvedTempRoot = path.resolve(tempUploadsRoot);
if (!resolvedFilePath.startsWith(resolvedTempRoot + path.sep)) {
throw new Error("Invalid temp file path detected");
}
const readStream = fs.createReadStream(resolvedFilePath);
const writeStream = fs.createWriteStream(permanentFilePath);

await new Promise<void>((resolve, reject) => {
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
// file.path contains the temp file path from multer.diskStorage
if (file.path) {
const readStream = fs.createReadStream(file.path);
const writeStream = fs.createWriteStream(permanentFilePath);

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 7 months ago

General fix strategy:
After creating the destination file path (permanentFilePath), normalize the path with path.resolve and ensure it remains within the expected upload directory. If the resolved permanentFilePath does not start with the absolute path of uploadsDir (plus a separator), abort the operation.

Detailed fix for the provided code:

  • After constructing permanentFilePath (line 53), use path.resolve on it and then confirm that the resolved value starts with the resolved uploadsDir (with the path separator to prevent prefix sneaking).
  • If the check fails, delete any temp file (if present), throw or return an error, and do not continue.
  • This logic should be implemented immediately before any file writing operations.

Requirements to implement:

  • Add a block after lines 53 or 54 to:
    • Compute const resolvedUploadsDir = path.resolve(uploadsDir) (if not computed there already)
    • const resolvedPermanentFilePath = path.resolve(permanentFilePath)
    • If not resolvedPermanentFilePath.startsWith(resolvedUploadsDir + path.sep), throw an error.
  • No new dependencies are required; only built-in path is needed.

Suggested changeset 1
Servers/utils/fileManager.utils.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Servers/utils/fileManager.utils.ts b/Servers/utils/fileManager.utils.ts
--- a/Servers/utils/fileManager.utils.ts
+++ b/Servers/utils/fileManager.utils.ts
@@ -52,6 +52,12 @@
   const uniqueFilename = `${timestamp}_${rand}_${sanitized}`;
   const permanentFilePath = path.join(uploadsDir, uniqueFilename);
 
+  // Security check: ensure permanentFilePath is inside uploadsDir
+  const resolvedUploadsDir = path.resolve(uploadsDir) + path.sep;
+  const resolvedPermanentFilePath = path.resolve(permanentFilePath);
+  if (!resolvedPermanentFilePath.startsWith(resolvedUploadsDir)) {
+    throw new Error("Resolved file path escapes uploads directory.");
+  }
   // Move file from temp directory to permanent location
   // file.path contains the temp file path from multer.diskStorage
   if (file.path) {
EOF
@@ -52,6 +52,12 @@
const uniqueFilename = `${timestamp}_${rand}_${sanitized}`;
const permanentFilePath = path.join(uploadsDir, uniqueFilename);

// Security check: ensure permanentFilePath is inside uploadsDir
const resolvedUploadsDir = path.resolve(uploadsDir) + path.sep;
const resolvedPermanentFilePath = path.resolve(permanentFilePath);
if (!resolvedPermanentFilePath.startsWith(resolvedUploadsDir)) {
throw new Error("Resolved file path escapes uploads directory.");
}
// Move file from temp directory to permanent location
// file.path contains the temp file path from multer.diskStorage
if (file.path) {
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
} else {
// Fallback for memory storage (if buffer exists)
if (file.buffer) {
await writeFile(permanentFilePath, file.buffer);

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 7 months ago

To address the issue, modify how the target file path (permanentFilePath) is computed. After generating the filename and building the path, use path.resolve (or fs.realpathSync after ensuring the file exists, but for uploads, path.resolve is better) to normalize it. Then check that the resulting absolute path begins with the intended uploads directory, also resolved with path.resolve to strictify the comparison. If the path is not contained within the storage root, throw an error. This prevents any possibility of traversing outside the "uploads/file-manager/" directory. Place the check immediately after constructing permanentFilePath, before writing/moving the uploaded file.

Required changes:

  • Add resolve logic and containment check after line 53 (permanentFilePath).
  • If the resolved path does not begin with the intended uploads directory, throw an error.
  • No new imports are necessary.

Suggested changeset 1
Servers/utils/fileManager.utils.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Servers/utils/fileManager.utils.ts b/Servers/utils/fileManager.utils.ts
--- a/Servers/utils/fileManager.utils.ts
+++ b/Servers/utils/fileManager.utils.ts
@@ -51,6 +51,13 @@
   const sanitized = sanitizeFilename(file.originalname);
   const uniqueFilename = `${timestamp}_${rand}_${sanitized}`;
   const permanentFilePath = path.join(uploadsDir, uniqueFilename);
+  
+  // SECURITY: verify permanentFilePath is contained within uploadsDir (normalized)
+  const normalizedUploadsDir = path.resolve(uploadsDir);
+  const normalizedFilePath = path.resolve(permanentFilePath);
+  if (!normalizedFilePath.startsWith(normalizedUploadsDir + path.sep)) {
+    throw new Error("Attempt to write file outside of uploads directory");
+  }
 
   // Move file from temp directory to permanent location
   // file.path contains the temp file path from multer.diskStorage
EOF
@@ -51,6 +51,13 @@
const sanitized = sanitizeFilename(file.originalname);
const uniqueFilename = `${timestamp}_${rand}_${sanitized}`;
const permanentFilePath = path.join(uploadsDir, uniqueFilename);

// SECURITY: verify permanentFilePath is contained within uploadsDir (normalized)
const normalizedUploadsDir = path.resolve(uploadsDir);
const normalizedFilePath = path.resolve(permanentFilePath);
if (!normalizedFilePath.startsWith(normalizedUploadsDir + path.sep)) {
throw new Error("Attempt to write file outside of uploads directory");
}

// Move file from temp directory to permanent location
// file.path contains the temp file path from multer.diskStorage
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
} catch (dbError) {
// Database insertion failed - cleanup orphaned file
try {
await fs.promises.unlink(permanentFilePath);

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 7 months ago

The best way to fix this problem is to ensure that every file path used for file system operations is guaranteed to reside inside the intended uploads root directory, regardless of any user input. This is done by (1) sanitizing the filename using a robust library (optionally, such as sanitize-filename from npm), (2) constructing the full file path using path.resolve, and (3) checking that the resolved path strictly starts with the intended root directory, aborting otherwise. This should be performed prior to every file system operation using user-controlled input, notably before calling fs.promises.unlink with permanentFilePath. For best defense-in-depth, update the code in Servers/utils/fileManager.utils.ts around the construction and deletion of the file path (permanentFilePath), using path.resolve, and add a check that it does not escape the uploads directory.

If you do not wish to introduce external dependencies, you may strengthen the sanitization logic, but the most robust solution would be to perform both path normalization and explicit root directory containment checks.


Suggested changeset 1
Servers/utils/fileManager.utils.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Servers/utils/fileManager.utils.ts b/Servers/utils/fileManager.utils.ts
--- a/Servers/utils/fileManager.utils.ts
+++ b/Servers/utils/fileManager.utils.ts
@@ -43,6 +43,8 @@
 
   // Create permanent uploads directory if it doesn't exist
   const uploadsDir = path.join(process.cwd(), "uploads", "file-manager", tenant);
+  // Save the absolute path so we can enforce root containment
+  const uploadsDirResolved = path.resolve(uploadsDir);
   await mkdir(uploadsDir, { recursive: true });
 
   // Generate unique filename with timestamp, random bytes, and sanitized original name
@@ -50,7 +52,8 @@
   const rand = randomBytes(4).toString("hex");
   const sanitized = sanitizeFilename(file.originalname);
   const uniqueFilename = `${timestamp}_${rand}_${sanitized}`;
-  const permanentFilePath = path.join(uploadsDir, uniqueFilename);
+  // Construct and normalize permanent file path
+  const permanentFilePath = path.resolve(uploadsDirResolved, uniqueFilename);
 
   // Move file from temp directory to permanent location
   // file.path contains the temp file path from multer.diskStorage
@@ -102,6 +105,10 @@
   } catch (dbError) {
     // Database insertion failed - cleanup orphaned file
     try {
+      // Only delete the file if it's under the uploads root directory
+      if (!permanentFilePath.startsWith(uploadsDirResolved + path.sep)) {
+        throw new Error(`Attempted file delete outside uploads root: ${permanentFilePath}`);
+      }
       await fs.promises.unlink(permanentFilePath);
     } catch (unlinkError: any) {
       // Ignore ENOENT (file already deleted), but log other errors
EOF
@@ -43,6 +43,8 @@

// Create permanent uploads directory if it doesn't exist
const uploadsDir = path.join(process.cwd(), "uploads", "file-manager", tenant);
// Save the absolute path so we can enforce root containment
const uploadsDirResolved = path.resolve(uploadsDir);
await mkdir(uploadsDir, { recursive: true });

// Generate unique filename with timestamp, random bytes, and sanitized original name
@@ -50,7 +52,8 @@
const rand = randomBytes(4).toString("hex");
const sanitized = sanitizeFilename(file.originalname);
const uniqueFilename = `${timestamp}_${rand}_${sanitized}`;
const permanentFilePath = path.join(uploadsDir, uniqueFilename);
// Construct and normalize permanent file path
const permanentFilePath = path.resolve(uploadsDirResolved, uniqueFilename);

// Move file from temp directory to permanent location
// file.path contains the temp file path from multer.diskStorage
@@ -102,6 +105,10 @@
} catch (dbError) {
// Database insertion failed - cleanup orphaned file
try {
// Only delete the file if it's under the uploads root directory
if (!permanentFilePath.startsWith(uploadsDirResolved + path.sep)) {
throw new Error(`Attempted file delete outside uploads root: ${permanentFilePath}`);
}
await fs.promises.unlink(permanentFilePath);
} catch (unlinkError: any) {
// Ignore ENOENT (file already deleted), but log other errors
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
} catch (unlinkError: any) {
// Ignore ENOENT (file already deleted), but log other errors
if (unlinkError.code !== 'ENOENT') {
console.error(`Failed to cleanup orphaned file after DB error: ${permanentFilePath}`, unlinkError);

Check failure

Code scanning / CodeQL

Use of externally-controlled format string High

Format string depends on a
user-provided value
.

Copilot Autofix

AI 7 months ago

The best way to fix the problem is to avoid letting untrusted input appear directly in the format string passed as the first argument to console.error. Instead, use a static string with a format specifier (e.g., %s) and pass the user-controlled value as a separate argument. This ensures that the path, no matter what it contains, will be treated as a literal string by the logger.
To implement the fix:

  • In Servers/utils/fileManager.utils.ts, update the problematic console.error call (line 109) by replacing the template string with a static format string and the tainted value as a separate argument:
    Change:
    console.error(`Failed to cleanup orphaned file after DB error: ${permanentFilePath}`, unlinkError);
    To:
    console.error("Failed to cleanup orphaned file after DB error: %s", permanentFilePath, unlinkError);
  • No new imports or helper methods are needed.
  • No other regions of the provided snippets require edits.

Suggested changeset 1
Servers/utils/fileManager.utils.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Servers/utils/fileManager.utils.ts b/Servers/utils/fileManager.utils.ts
--- a/Servers/utils/fileManager.utils.ts
+++ b/Servers/utils/fileManager.utils.ts
@@ -106,7 +106,7 @@
     } catch (unlinkError: any) {
       // Ignore ENOENT (file already deleted), but log other errors
       if (unlinkError.code !== 'ENOENT') {
-        console.error(`Failed to cleanup orphaned file after DB error: ${permanentFilePath}`, unlinkError);
+        console.error("Failed to cleanup orphaned file after DB error: %s", permanentFilePath, unlinkError);
       }
     }
     // Rethrow original database error
EOF
@@ -106,7 +106,7 @@
} catch (unlinkError: any) {
// Ignore ENOENT (file already deleted), but log other errors
if (unlinkError.code !== 'ENOENT') {
console.error(`Failed to cleanup orphaned file after DB error: ${permanentFilePath}`, unlinkError);
console.error("Failed to cleanup orphaned file after DB error: %s", permanentFilePath, unlinkError);
}
}
// Rethrow original database error
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 27, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@HarshP4585 HarshP4585 requested a review from gorkem-bwl October 28, 2025 00:06
@HarshP4585 HarshP4585 marked this pull request as ready for review October 28, 2025 00:06
@gorkem-bwl gorkem-bwl merged commit 3fb9156 into master Oct 28, 2025
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants