Skip to content

Revert "Prod release - Security fixes" #7062

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 3, 2025

Conversation

kkartunov
Copy link
Collaborator

Reverts #7059

@kkartunov kkartunov merged commit 6d7b09e into master Feb 3, 2025
2 of 3 checks passed
return this.getSheetAPI(req, res);
}
res.status(status);
return res.send((e.response && e.response.data) || { ...e, message: e.message });

Check warning

Code scanning / CodeQL

Information exposure through a stack trace Medium

This information exposed to the user depends on
stack trace information
.

Copilot Autofix

AI 3 months ago

To fix the problem, we need to ensure that detailed error information, including stack traces, is not sent to the client. Instead, we should log the detailed error information on the server and send a generic error message to the client. This can be achieved by modifying the catch blocks to log the error and send a generic message.

  1. Modify the catch blocks in the getSheetAPI and addToSheetAPI methods to log the error details.
  2. Send a generic error message to the client instead of the detailed error information.
Suggested changeset 1
src/server/services/gSheet.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/src/server/services/gSheet.js b/src/server/services/gSheet.js
--- a/src/server/services/gSheet.js
+++ b/src/server/services/gSheet.js
@@ -59,4 +59,5 @@
       }
+      console.error("Error in getSheetAPI:", e);
       res.status(status);
-      return res.send((e.response && e.response.data) || { ...e, message: e.message });
+      return res.send({ message: "An error occurred while processing your request." });
     }
@@ -97,4 +98,5 @@
       }
+      console.error("Error in addToSheetAPI:", e);
       res.status(status);
-      return res.send((e.response && e.response.data) || { ...e, message: e.message });
+      return res.send({ message: "An error occurred while processing your request." });
     }
EOF
@@ -59,4 +59,5 @@
}
console.error("Error in getSheetAPI:", e);
res.status(status);
return res.send((e.response && e.response.data) || { ...e, message: e.message });
return res.send({ message: "An error occurred while processing your request." });
}
@@ -97,4 +98,5 @@
}
console.error("Error in addToSheetAPI:", e);
res.status(status);
return res.send((e.response && e.response.data) || { ...e, message: e.message });
return res.send({ message: "An error occurred while processing your request." });
}
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +31 to +37
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
method: 'GET',
headers: {
'Content-Type': req.headers['content-type'],
Authorization: this.authorization,
},
});

Check failure

Code scanning / CodeQL

Server-side request forgery Critical

The
URL
of this request depends on a
user-provided value
.
The
URL
of this request depends on a
user-provided value
.

Copilot Autofix

AI 3 months ago

To fix the SSRF vulnerability, we need to ensure that the user-provided values (listId and emailHash) are validated and sanitized before being used in the URL. This can be achieved by implementing a whitelist of acceptable listId values and ensuring that emailHash conforms to expected patterns (e.g., a valid MD5 hash).

  1. Implement a whitelist for listId values.
  2. Validate the emailHash to ensure it is a valid MD5 hash.
  3. Modify the checkSubscription, doRegistMember, updateMember, and subscribeTags methods to include these validations.
Suggested changeset 1
src/server/services/mailchimp.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/src/server/services/mailchimp.js b/src/server/services/mailchimp.js
--- a/src/server/services/mailchimp.js
+++ b/src/server/services/mailchimp.js
@@ -30,3 +30,5 @@
   async checkSubscription(req) {
-    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
+    const listId = this.validateListId(req.params.listId);
+    const emailHash = this.validateEmailHash(req.params.emailHash);
+    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}`, {
       method: 'GET',
@@ -41,4 +43,5 @@
   async doRegistMember(req) {
+    const listId = this.validateListId(req.params.listId);
     const formData = JSON.stringify(req.body);
-    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members`, {
+    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members`, {
       method: 'POST',
@@ -54,4 +57,6 @@
   async updateMember(req) {
+    const listId = this.validateListId(req.params.listId);
+    const emailHash = this.validateEmailHash(req.params.emailHash);
     const formData = JSON.stringify(req.body);
-    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
+    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}`, {
       method: 'PUT',
@@ -67,4 +72,6 @@
   async subscribeTags(req) {
+    const listId = this.validateListId(req.params.listId);
+    const emailHash = this.validateEmailHash(req.params.emailHash);
     const formData = JSON.stringify(req.body);
-    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}/tags`, {
+    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}/tags`, {
       method: 'POST',
@@ -110,2 +117,17 @@
   }
+  validateListId(listId) {
+    const allowedListIds = ['list1', 'list2', 'list3']; // Example whitelist
+    if (!allowedListIds.includes(listId)) {
+      throw new Error('Invalid listId');
+    }
+    return listId;
+  }
+
+  validateEmailHash(emailHash) {
+    const emailHashPattern = /^[a-f0-9]{32}$/; // MD5 hash pattern
+    if (!emailHashPattern.test(emailHash)) {
+      throw new Error('Invalid emailHash');
+    }
+    return emailHash;
+  }
 }
EOF
@@ -30,3 +30,5 @@
async checkSubscription(req) {
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
const listId = this.validateListId(req.params.listId);
const emailHash = this.validateEmailHash(req.params.emailHash);
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}`, {
method: 'GET',
@@ -41,4 +43,5 @@
async doRegistMember(req) {
const listId = this.validateListId(req.params.listId);
const formData = JSON.stringify(req.body);
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members`, {
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members`, {
method: 'POST',
@@ -54,4 +57,6 @@
async updateMember(req) {
const listId = this.validateListId(req.params.listId);
const emailHash = this.validateEmailHash(req.params.emailHash);
const formData = JSON.stringify(req.body);
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}`, {
method: 'PUT',
@@ -67,4 +72,6 @@
async subscribeTags(req) {
const listId = this.validateListId(req.params.listId);
const emailHash = this.validateEmailHash(req.params.emailHash);
const formData = JSON.stringify(req.body);
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}/tags`, {
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}/tags`, {
method: 'POST',
@@ -110,2 +117,17 @@
}
validateListId(listId) {
const allowedListIds = ['list1', 'list2', 'list3']; // Example whitelist
if (!allowedListIds.includes(listId)) {
throw new Error('Invalid listId');
}
return listId;
}

validateEmailHash(emailHash) {
const emailHashPattern = /^[a-f0-9]{32}$/; // MD5 hash pattern
if (!emailHashPattern.test(emailHash)) {
throw new Error('Invalid emailHash');
}
return emailHash;
}
}
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +43 to +50
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members`, {
method: 'POST',
headers: {
'Content-Type': req.headers['content-type'],
Authorization: this.authorization,
},
body: formData,
});

Check failure

Code scanning / CodeQL

Server-side request forgery Critical

The
URL
of this request depends on a
user-provided value
.

Copilot Autofix

AI 3 months ago

To fix the problem, we need to ensure that the user input used in constructing the URL is validated against a whitelist of acceptable values. This will prevent attackers from manipulating the URL to perform SSRF attacks. Specifically, we should validate req.params.listId to ensure it matches expected patterns or values.

  1. Create a whitelist of acceptable listId values.
  2. Validate req.params.listId against this whitelist before using it in the URL.
  3. If the listId is not valid, return an error response.
Suggested changeset 1
src/server/services/mailchimp.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/src/server/services/mailchimp.js b/src/server/services/mailchimp.js
--- a/src/server/services/mailchimp.js
+++ b/src/server/services/mailchimp.js
@@ -30,2 +30,6 @@
   async checkSubscription(req) {
+    const validListIds = ['list1', 'list2', 'list3']; // Example whitelist
+    if (!validListIds.includes(req.params.listId)) {
+      throw new Error('Invalid listId');
+    }
     const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
@@ -42,2 +46,6 @@
     const formData = JSON.stringify(req.body);
+    const validListIds = ['list1', 'list2', 'list3']; // Example whitelist
+    if (!validListIds.includes(req.params.listId)) {
+      throw new Error('Invalid listId');
+    }
     const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members`, {
@@ -55,2 +63,6 @@
     const formData = JSON.stringify(req.body);
+    const validListIds = ['list1', 'list2', 'list3']; // Example whitelist
+    if (!validListIds.includes(req.params.listId)) {
+      throw new Error('Invalid listId');
+    }
     const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
@@ -68,2 +80,6 @@
     const formData = JSON.stringify(req.body);
+    const validListIds = ['list1', 'list2', 'list3']; // Example whitelist
+    if (!validListIds.includes(req.params.listId)) {
+      throw new Error('Invalid listId');
+    }
     const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}/tags`, {
EOF
@@ -30,2 +30,6 @@
async checkSubscription(req) {
const validListIds = ['list1', 'list2', 'list3']; // Example whitelist
if (!validListIds.includes(req.params.listId)) {
throw new Error('Invalid listId');
}
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
@@ -42,2 +46,6 @@
const formData = JSON.stringify(req.body);
const validListIds = ['list1', 'list2', 'list3']; // Example whitelist
if (!validListIds.includes(req.params.listId)) {
throw new Error('Invalid listId');
}
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members`, {
@@ -55,2 +63,6 @@
const formData = JSON.stringify(req.body);
const validListIds = ['list1', 'list2', 'list3']; // Example whitelist
if (!validListIds.includes(req.params.listId)) {
throw new Error('Invalid listId');
}
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
@@ -68,2 +80,6 @@
const formData = JSON.stringify(req.body);
const validListIds = ['list1', 'list2', 'list3']; // Example whitelist
if (!validListIds.includes(req.params.listId)) {
throw new Error('Invalid listId');
}
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}/tags`, {
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +56 to +63
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
method: 'PUT',
headers: {
'Content-Type': req.headers['content-type'],
Authorization: this.authorization,
},
body: formData,
});

Check failure

Code scanning / CodeQL

Server-side request forgery Critical

The
URL
of this request depends on a
user-provided value
.
The
URL
of this request depends on a
user-provided value
.

Copilot Autofix

AI 3 months ago

To fix the problem, we need to validate and sanitize the user-provided values (req.params.listId and req.params.emailHash) before using them in the URL. This can be done by ensuring that these parameters conform to expected patterns or formats. For example, listId can be validated to ensure it matches a specific pattern, and emailHash can be checked to ensure it is a valid hash.

We will implement a validation function to check the format of listId and emailHash and use this function before constructing the URL. If the validation fails, we will throw an error or handle it appropriately.

Suggested changeset 1
src/server/services/mailchimp.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/src/server/services/mailchimp.js b/src/server/services/mailchimp.js
--- a/src/server/services/mailchimp.js
+++ b/src/server/services/mailchimp.js
@@ -24,2 +24,12 @@
 
+  isValidListId(listId) {
+    const listIdPattern = /^[a-zA-Z0-9_-]+$/;
+    return listIdPattern.test(listId);
+  }
+
+  isValidEmailHash(emailHash) {
+    const emailHashPattern = /^[a-f0-9]{32}$/;
+    return emailHashPattern.test(emailHash);
+  }
+
   /**
@@ -30,3 +40,7 @@
   async checkSubscription(req) {
-    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
+    const { listId, emailHash } = req.params;
+    if (!this.isValidListId(listId) || !this.isValidEmailHash(emailHash)) {
+      throw new Error('Invalid parameters');
+    }
+    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}`, {
       method: 'GET',
@@ -41,4 +55,8 @@
   async doRegistMember(req) {
+    const { listId } = req.params;
+    if (!this.isValidListId(listId)) {
+      throw new Error('Invalid parameters');
+    }
     const formData = JSON.stringify(req.body);
-    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members`, {
+    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members`, {
       method: 'POST',
@@ -54,4 +72,8 @@
   async updateMember(req) {
+    const { listId, emailHash } = req.params;
+    if (!this.isValidListId(listId) || !this.isValidEmailHash(emailHash)) {
+      throw new Error('Invalid parameters');
+    }
     const formData = JSON.stringify(req.body);
-    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
+    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}`, {
       method: 'PUT',
@@ -67,4 +89,8 @@
   async subscribeTags(req) {
+    const { listId, emailHash } = req.params;
+    if (!this.isValidListId(listId) || !this.isValidEmailHash(emailHash)) {
+      throw new Error('Invalid parameters');
+    }
     const formData = JSON.stringify(req.body);
-    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}/tags`, {
+    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}/tags`, {
       method: 'POST',
EOF
@@ -24,2 +24,12 @@

isValidListId(listId) {
const listIdPattern = /^[a-zA-Z0-9_-]+$/;
return listIdPattern.test(listId);
}

isValidEmailHash(emailHash) {
const emailHashPattern = /^[a-f0-9]{32}$/;
return emailHashPattern.test(emailHash);
}

/**
@@ -30,3 +40,7 @@
async checkSubscription(req) {
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
const { listId, emailHash } = req.params;
if (!this.isValidListId(listId) || !this.isValidEmailHash(emailHash)) {
throw new Error('Invalid parameters');
}
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}`, {
method: 'GET',
@@ -41,4 +55,8 @@
async doRegistMember(req) {
const { listId } = req.params;
if (!this.isValidListId(listId)) {
throw new Error('Invalid parameters');
}
const formData = JSON.stringify(req.body);
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members`, {
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members`, {
method: 'POST',
@@ -54,4 +72,8 @@
async updateMember(req) {
const { listId, emailHash } = req.params;
if (!this.isValidListId(listId) || !this.isValidEmailHash(emailHash)) {
throw new Error('Invalid parameters');
}
const formData = JSON.stringify(req.body);
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}`, {
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}`, {
method: 'PUT',
@@ -67,4 +89,8 @@
async subscribeTags(req) {
const { listId, emailHash } = req.params;
if (!this.isValidListId(listId) || !this.isValidEmailHash(emailHash)) {
throw new Error('Invalid parameters');
}
const formData = JSON.stringify(req.body);
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}/tags`, {
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}/tags`, {
method: 'POST',
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +69 to +76
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}/tags`, {
method: 'POST',
headers: {
'Content-Type': req.headers['content-type'],
Authorization: this.authorization,
},
body: formData,
});

Check failure

Code scanning / CodeQL

Server-side request forgery Critical

The
URL
of this request depends on a
user-provided value
.
The
URL
of this request depends on a
user-provided value
.

Copilot Autofix

AI 3 months ago

To fix the problem, we need to validate and sanitize the user-provided values (listId and emailHash) before incorporating them into the URL. This can be done by ensuring that these values conform to expected patterns or formats. For example, we can use regular expressions to validate that listId and emailHash contain only valid characters.

  1. Validate listId and emailHash to ensure they conform to expected patterns.
  2. Reject or sanitize any values that do not match the expected patterns.
  3. Incorporate the validated values into the URL.
Suggested changeset 1
src/server/services/mailchimp.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/src/server/services/mailchimp.js b/src/server/services/mailchimp.js
--- a/src/server/services/mailchimp.js
+++ b/src/server/services/mailchimp.js
@@ -68,3 +68,14 @@
     const formData = JSON.stringify(req.body);
-    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}/tags`, {
+    const listId = req.params.listId;
+    const emailHash = req.params.emailHash;
+
+    // Validate listId and emailHash
+    const listIdPattern = /^[a-zA-Z0-9_-]+$/;
+    const emailHashPattern = /^[a-f0-9]{32}$/;
+
+    if (!listIdPattern.test(listId) || !emailHashPattern.test(emailHash)) {
+      throw new Error('Invalid listId or emailHash');
+    }
+
+    const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}/tags`, {
       method: 'POST',
EOF
@@ -68,3 +68,14 @@
const formData = JSON.stringify(req.body);
const res = await fetch(`${this.mailchimpBaseUrl}/lists/${req.params.listId}/members/${req.params.emailHash}/tags`, {
const listId = req.params.listId;
const emailHash = req.params.emailHash;

// Validate listId and emailHash
const listIdPattern = /^[a-zA-Z0-9_-]+$/;
const emailHashPattern = /^[a-f0-9]{32}$/;

if (!listIdPattern.test(listId) || !emailHashPattern.test(emailHash)) {
throw new Error('Invalid listId or emailHash');
}

const res = await fetch(`${this.mailchimpBaseUrl}/lists/${listId}/members/${emailHash}/tags`, {
method: 'POST',
Copilot is powered by AI and may make mistakes. Always verify output.
kkartunov added a commit that referenced this pull request Feb 3, 2025
…aster-revert-reverted"

This reverts commit 6d7b09e, reversing
changes made to ffc3a93.
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.

1 participant