Skip to content

Conversation

@harshithad0703
Copy link
Contributor

No description provided.

@harshithad0703 harshithad0703 requested a review from a team as a code owner October 30, 2025 07:03
@github-actions
Copy link

github-actions bot commented Oct 30, 2025

Coverage report for commit: ab52f72
File: coverage/clover.xml

Cover ┌─────────────────────────┐ Freq.
   0% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  10% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  20% │ ██░░░░░░░░░░░░░░░░░░░░░ │  2.3%
  30% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  40% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  50% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  60% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  70% │ ██████░░░░░░░░░░░░░░░░░ │  9.1%
  80% │ ███████████████████████ │ 38.6%
  90% │ ██████████████░░░░░░░░░ │ 22.7%
 100% │ █████████████████░░░░░░ │ 27.3%
      └─────────────────────────┘
 *Legend:* █ = Current Distribution 
Summary - Lines: 82.57% | Methods: 95.88% | Branches: 68.60%
FilesLinesMethodsBranches
lib
   contentstack.js100.00%100.00%100.00%
   contentstackClient.js83.02%92.86%69.35%
   contentstackCollection.js94.12%100.00%86.67%
   entity.js76.09%100.00%61.33%
lib/core
   Util.js78.18%95.00%63.92%
   concurrency-queue.js69.45%71.15%65.75%
   contentstackError.js100.00%100.00%100.00%
   contentstackHTTPClient.js98.28%100.00%94.55%
   oauthHandler.js88.81%100.00%65.99%
lib/organization
   index.js72.73%100.00%53.09%
lib/organization/teams
   index.js89.47%100.00%65.79%
lib/organization/teams/stackRoleMappings
   index.js78.57%100.00%51.16%
lib/organization/teams/teamUsers
   index.js100.00%100.00%93.75%
lib/query
   index.js73.91%100.00%61.82%
lib/stack/asset/folders
   index.js100.00%100.00%100.00%
lib/stack/asset
   index.js86.25%100.00%74.00%
lib/stack/auditlog
   index.js91.67%100.00%71.88%
lib/stack/branch
   compare.js68.18%100.00%62.50%
   index.js94.87%100.00%77.78%
   mergeQueue.js83.33%100.00%63.16%
lib/stack/branchAlias
   index.js79.31%100.00%60.98%
lib/stack/contentType/entry
   index.js83.04%100.00%59.29%
lib/stack/contentType/entry/variants
   index.js79.31%100.00%62.86%
lib/stack/contentType
   index.js84.62%100.00%70.13%
lib/stack/deliveryToken
   index.js95.24%80.00%89.47%
lib/stack/deliveryToken/previewToken
   index.js21.43%25.00%14.29%
lib/stack/environment
   index.js100.00%100.00%100.00%
lib/stack/extension
   index.js93.88%100.00%85.71%
lib/stack/globalField
   index.js91.67%100.00%82.35%
lib/stack
   index.js80.66%92.86%70.61%
lib/stack/label
   index.js100.00%100.00%100.00%
lib/stack/locale
   index.js100.00%100.00%100.00%
lib/stack/managementToken
   index.js100.00%100.00%94.44%
lib/stack/release
   index.js86.67%100.00%68.09%
lib/stack/roles
   index.js100.00%100.00%100.00%
lib/stack/taxonomy
   index.js84.00%100.00%61.29%
lib/stack/taxonomy/terms
   index.js84.62%100.00%66.67%
lib/stack/variantGroup
   index.js81.58%100.00%60.47%
lib/stack/variantGroup/variants
   index.js77.50%100.00%54.90%
lib/stack/variants
   index.js76.32%100.00%53.19%
lib/stack/webhook
   index.js84.48%100.00%67.80%
lib/stack/workflow
   index.js83.64%100.00%67.69%
lib/stack/workflow/publishRules
   index.js100.00%100.00%100.00%
lib/user
   index.js91.43%100.00%78.57%

🤖 comment via lucassabreu/comment-coverage-clover

developerHubHost = process.env.HOST.replace(/^dev\d*-api\./, 'dev-developerhub-api.')
} else if (process.env.HOST.startsWith('stag')) {
developerHubHost = process.env.HOST.replace(/^stag\d*-api\./, 'stag-developerhub-api.')
} else if (process.env.HOST.includes('contentstack.com') || process.env.HOST.includes('contentstack.io')) {

Check failure

Code scanning / CodeQL

Incomplete URL substring sanitization

'[contentstack.com](1)' can be anywhere in the URL, and arbitrary hosts may come before or after it.

Copilot Autofix

AI about 2 months ago

To fix this issue, we should avoid using a substring match on the host value. Instead, we should parse the host value and check that it matches an explicit whitelist of allowed hosts, or at least, that the host domain is exactly one of the intended domains (e.g., contentstack.com, contentstack.io), including allowed subdomains if needed.
We can use Node's native url module (URL class in modern JS) to safely parse the hostname of process.env.HOST and then check if it matches the allowed domains, either by exact match or by using a helper function that matches allowed subdomains.
Change only the code in file test/sanity-check/api/oauth-test.js at or around line 33 where the current substring check exists. Add import of Node's URL class if not available, and replace the .includes check with a check that gets the hostname and matches allowed values.


Suggested changeset 1
test/sanity-check/api/oauth-test.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/test/sanity-check/api/oauth-test.js b/test/sanity-check/api/oauth-test.js
--- a/test/sanity-check/api/oauth-test.js
+++ b/test/sanity-check/api/oauth-test.js
@@ -30,9 +30,24 @@
     developerHubHost = process.env.HOST.replace(/^dev\d*-api\./, 'dev-developerhub-api.')
   } else if (process.env.HOST.startsWith('stag')) {
     developerHubHost = process.env.HOST.replace(/^stag\d*-api\./, 'stag-developerhub-api.')
-  } else if (process.env.HOST.includes('contentstack.com') || process.env.HOST.includes('contentstack.io')) {
-    // For standard Contentstack region hosts (e.g., au-api.contentstack.com -> au-developerhub-api.contentstack.com)
-    developerHubHost = process.env.HOST.replace(/-api\./, '-developerhub-api.')
+  } else {
+    // Safer domain check: for standard Contentstack region hosts (e.g., au-api.contentstack.com -> au-developerhub-api.contentstack.com)
+    try {
+      // Ensure HOST is a valid URL with protocol; if not, default to https for parsing
+      let hostToParse = process.env.HOST;
+      if (!/^https?:\/\//.test(hostToParse)) {
+        hostToParse = `https://${hostToParse}`;
+      }
+      const urlObj = new URL(hostToParse);
+      const hostname = urlObj.hostname;
+      // Allow only contentstack.com or contentstack.io or their subdomains
+      const allowedDomains = ["contentstack.com", "contentstack.io"];
+      if (allowedDomains.some(d => hostname === d || hostname.endsWith('.' + d))) {
+        developerHubHost = process.env.HOST.replace(/-api\./, '-developerhub-api.');
+      }
+    } catch (err) {
+      // Invalid HOST format; handle error or skip
+    }
   }
 
   if (developerHubHost) {
EOF
@@ -30,9 +30,24 @@
developerHubHost = process.env.HOST.replace(/^dev\d*-api\./, 'dev-developerhub-api.')
} else if (process.env.HOST.startsWith('stag')) {
developerHubHost = process.env.HOST.replace(/^stag\d*-api\./, 'stag-developerhub-api.')
} else if (process.env.HOST.includes('contentstack.com') || process.env.HOST.includes('contentstack.io')) {
// For standard Contentstack region hosts (e.g., au-api.contentstack.com -> au-developerhub-api.contentstack.com)
developerHubHost = process.env.HOST.replace(/-api\./, '-developerhub-api.')
} else {
// Safer domain check: for standard Contentstack region hosts (e.g., au-api.contentstack.com -> au-developerhub-api.contentstack.com)
try {
// Ensure HOST is a valid URL with protocol; if not, default to https for parsing
let hostToParse = process.env.HOST;
if (!/^https?:\/\//.test(hostToParse)) {
hostToParse = `https://${hostToParse}`;
}
const urlObj = new URL(hostToParse);
const hostname = urlObj.hostname;
// Allow only contentstack.com or contentstack.io or their subdomains
const allowedDomains = ["contentstack.com", "contentstack.io"];
if (allowedDomains.some(d => hostname === d || hostname.endsWith('.' + d))) {
developerHubHost = process.env.HOST.replace(/-api\./, '-developerhub-api.');
}
} catch (err) {
// Invalid HOST format; handle error or skip
}
}

if (developerHubHost) {
Copilot is powered by AI and may make mistakes. Always verify output.
developerHubHost = process.env.HOST.replace(/^dev\d*-api\./, 'dev-developerhub-api.')
} else if (process.env.HOST.startsWith('stag')) {
developerHubHost = process.env.HOST.replace(/^stag\d*-api\./, 'stag-developerhub-api.')
} else if (process.env.HOST.includes('contentstack.com') || process.env.HOST.includes('contentstack.io')) {

Check failure

Code scanning / CodeQL

Incomplete URL substring sanitization

'[contentstack.io](1)' can be anywhere in the URL, and arbitrary hosts may come before or after it.

Copilot Autofix

AI about 2 months ago

The fix is to avoid substring checks for hostnames and instead use proper host parsing and exact or suffix matching. On line 33, instead of checking if process.env.HOST.includes('contentstack.com') || process.env.HOST.includes('contentstack.io'), parse process.env.HOST as a hostname, then check if the hostname is exactly an allowed value or is a subdomain (with suffix matching starting on a dot). For Node.js, this can be achieved using the URL constructor for URLs, or with a regular expression if the value is always a bare hostname.

Since the code is working with just the hostname (not a full URL), we should check if the hostname ends with, for example, .contentstack.com or .contentstack.io or is exactly those domains.

Change:

process.env.HOST.includes('contentstack.com') || process.env.HOST.includes('contentstack.io')

to a suffix check:

isAllowedContentstackHost(process.env.HOST)

and define the helper:

function isAllowedContentstackHost(host) {
  return (
    host === 'contentstack.com' ||
    host.endsWith('.contentstack.com') ||
    host === 'contentstack.io' ||
    host.endsWith('.contentstack.io')
  );
}

Ensure this helper is included near the top of the file.

Suggested changeset 1
test/sanity-check/api/oauth-test.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/test/sanity-check/api/oauth-test.js b/test/sanity-check/api/oauth-test.js
--- a/test/sanity-check/api/oauth-test.js
+++ b/test/sanity-check/api/oauth-test.js
@@ -5,6 +5,17 @@
 import dotenv from 'dotenv'
 
 dotenv.config()
+
+// Safely check for allowed Contentstack hosts (not substring)
+function isAllowedContentstackHost(host) {
+  return (
+    host === 'contentstack.com' ||
+    host.endsWith('.contentstack.com') ||
+    host === 'contentstack.io' ||
+    host.endsWith('.contentstack.io')
+  );
+}
+
 let accessToken = ''
 let loggedinUserID = ''
 let authUrl = ''
@@ -30,7 +41,7 @@
     developerHubHost = process.env.HOST.replace(/^dev\d*-api\./, 'dev-developerhub-api.')
   } else if (process.env.HOST.startsWith('stag')) {
     developerHubHost = process.env.HOST.replace(/^stag\d*-api\./, 'stag-developerhub-api.')
-  } else if (process.env.HOST.includes('contentstack.com') || process.env.HOST.includes('contentstack.io')) {
+  } else if (isAllowedContentstackHost(process.env.HOST)) {
     // For standard Contentstack region hosts (e.g., au-api.contentstack.com -> au-developerhub-api.contentstack.com)
     developerHubHost = process.env.HOST.replace(/-api\./, '-developerhub-api.')
   }
EOF
@@ -5,6 +5,17 @@
import dotenv from 'dotenv'

dotenv.config()

// Safely check for allowed Contentstack hosts (not substring)
function isAllowedContentstackHost(host) {
return (
host === 'contentstack.com' ||
host.endsWith('.contentstack.com') ||
host === 'contentstack.io' ||
host.endsWith('.contentstack.io')
);
}

let accessToken = ''
let loggedinUserID = ''
let authUrl = ''
@@ -30,7 +41,7 @@
developerHubHost = process.env.HOST.replace(/^dev\d*-api\./, 'dev-developerhub-api.')
} else if (process.env.HOST.startsWith('stag')) {
developerHubHost = process.env.HOST.replace(/^stag\d*-api\./, 'stag-developerhub-api.')
} else if (process.env.HOST.includes('contentstack.com') || process.env.HOST.includes('contentstack.io')) {
} else if (isAllowedContentstackHost(process.env.HOST)) {
// For standard Contentstack region hosts (e.g., au-api.contentstack.com -> au-developerhub-api.contentstack.com)
developerHubHost = process.env.HOST.replace(/-api\./, '-developerhub-api.')
}
Copilot is powered by AI and may make mistakes. Always verify output.
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.

2 participants