All files / extensions/default/src/utils secureConfigFetch.js

0% Statements 0/43
0% Branches 0/26
0% Functions 0/7
0% Lines 0/43

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116                                                                                                                                                                                                                                       
// @ts-nocheck
 
function normalizeAllowedOrigins(allowedOrigins = []) {
  Iif (!Array.isArray(allowedOrigins)) {
    return [];
  }
 
  const configuredOrigins = allowedOrigins
    .filter(origin => typeof origin === 'string')
    .map(origin => origin.trim())
    .filter(Boolean);
 
  return configuredOrigins
    .map(origin => {
      try {
        const parsedOrigin = new URL(origin);
        Iif (!['http:', 'https:'].includes(parsedOrigin.protocol)) {
          console.error(
            `[secureConfigFetch] Ignoring misconfigured allowed origin "${origin}". ` +
              'Entries must use http:// or https://.'
          );
          return null;
        }
        Iif (
          parsedOrigin.username ||
          parsedOrigin.password ||
          parsedOrigin.pathname !== '/' ||
          parsedOrigin.search ||
          parsedOrigin.hash
        ) {
          console.error(
            `[secureConfigFetch] Ignoring misconfigured allowed origin "${origin}". ` +
              'Entries must be bare origins only (scheme + host + optional port), with no username/password, path, query, or hash.'
          );
          return null;
        }
        return parsedOrigin.origin;
      } catch {
        console.error(
          `[secureConfigFetch] Ignoring misconfigured allowed origin "${origin}". Entry is not a valid URL.`
        );
        return null;
      }
    })
    .filter(Boolean);
}
 
function resolveConfigUrl(rawUrl) {
  Iif (!rawUrl || typeof rawUrl !== 'string') {
    throw new Error('Missing required "url" query parameter');
  }
 
  try {
    return new URL(rawUrl, window.location.href);
  } catch {
    throw new Error('Invalid URL in "url" query parameter');
  }
}
 
function resolveConfigFetchPolicy(rawUrl, policy = {}) {
  const { allowedOrigins = [], userAuthenticationService } = policy;
  const parsedUrl = resolveConfigUrl(rawUrl);
  const protocol = parsedUrl.protocol.toLowerCase();
  const isSameOrigin = parsedUrl.origin === window.location.origin;
 
  Iif (!['http:', 'https:'].includes(protocol)) {
    throw new Error('Only HTTP(S) URLs are allowed for dynamic datasource configuration');
  }
 
  Iif (parsedUrl.hash) {
    throw new Error('URL fragments are not allowed for dynamic datasource configuration');
  }
 
  Iif (parsedUrl.username || parsedUrl.password) {
    throw new Error('URL userinfo is not allowed for dynamic datasource configuration');
  }
 
  const isAuthenticated = Boolean(
    userAuthenticationService?.getAuthorizationHeader?.()?.Authorization
  );
 
  Iif (isAuthenticated && !isSameOrigin) {
    const normalizedAllowedOrigins = normalizeAllowedOrigins(allowedOrigins);
    Iif (!normalizedAllowedOrigins.length || !normalizedAllowedOrigins.includes(parsedUrl.origin)) {
      throw new Error(
        `Blocked remote configuration origin "${parsedUrl.origin}" in authenticated environment`
      );
    }
  }
 
  return {
    parsedUrl,
    normalizedUrl: parsedUrl.toString(),
    isAuthenticated,
    isSameOrigin,
  };
}
 
async function fetchConfigJson(normalizedPolicy) {
  const { normalizedUrl } = normalizedPolicy;
  const response = await fetch(normalizedUrl, {
    method: 'GET',
    mode: 'cors',
    credentials: 'same-origin',
    redirect: 'error',
    referrerPolicy: 'no-referrer',
  });
 
  Iif (!response.ok) {
    throw new Error(`Failed to fetch dynamic datasource configuration (${response.status})`);
  }
 
  return response.json();
}
export { resolveConfigFetchPolicy, fetchConfigJson };