"Fossies" - the Fresh Open Source Software Archive

Member "cli-1.1260.0/src/lib/config/api-url.ts" (4 Dec 2023, 6187 Bytes) of package /linux/misc/snyk-cli-1.1260.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2   This file is trying to make sense from different Snyk API URLs configurations
    3 
    4   API URL settings could be defined in a few ways:
    5   - snyk config file with key "endpoint" (including override with SNYK_CFG_ENDPOINT envvar!)
    6   - SNYK_API envvar
    7   - Snyk REST API had their own envvars to be set
    8 
    9   And API URL itself could (currently) point to multiple places
   10   - https://snyk.io/api/v1 (old default)
   11   - https://snyk.io/api
   12   - https://app.snyk.io/api
   13   - https://app.snyk.io/api/v1
   14   - https://api.snyk.io/v1
   15 
   16   For Snyk REST API it's a bit simpler:
   17   - https://api.snyk.io/rest
   18 
   19 
   20   There are also other URLs - one for the snyk auth command, one for Snyk Code Proxy
   21 
   22   Idea is to configure a single URL and derive the rest from it.
   23   This file handles an internal concept of a Base URL and logic needed to derive the other URLs
   24   In a backwards compatible way.
   25 */
   26 
   27 import * as path from 'path';
   28 import * as Debug from 'debug';
   29 import { color } from '../theme';
   30 
   31 const debug = Debug('snyk');
   32 
   33 /**
   34  * @description Get a Base URL for Snyk APIs
   35  * @export
   36  * @param {string} defaultUrl URL to default to, should be the one defined in the config.default.json file
   37  * @param {(string | undefined)} envvarDefinedApiUrl if there is an URL defined in the SNYK_API envvar
   38  * @param {(string | undefined)} configDefinedApiUrl if there is an URL defined in the 'endpoint' key of the config
   39  * @returns {string} Returns a Base URL - without the /v1. Use this to construct derived URLs
   40  */
   41 export function getBaseApiUrl(
   42   defaultUrl: string,
   43   envvarDefinedApiUrl?: string,
   44   configDefinedApiUrl?: string,
   45 ): string {
   46   const defaultBaseApiUrl = stripV1FromApiUrl(defaultUrl);
   47   // Use SNYK_API envvar by default
   48   if (envvarDefinedApiUrl) {
   49     return validateUrlOrReturnDefault(
   50       envvarDefinedApiUrl,
   51       "'SNYK_API' environment variable",
   52       defaultBaseApiUrl,
   53     );
   54   }
   55 
   56   if (configDefinedApiUrl) {
   57     return validateUrlOrReturnDefault(
   58       configDefinedApiUrl,
   59       "'endpoint' config option. See 'snyk config' command. The value of 'endpoint' is currently set as",
   60       defaultBaseApiUrl,
   61     );
   62   }
   63 
   64   return defaultBaseApiUrl; // Fallback to default
   65 }
   66 
   67 /**
   68  * @description Macro to validate user-defined URL and fallback to default if needed
   69  * @param {string} urlString "dirty" user defined string coming from config, envvar or a flag
   70  * @param {string} optionName For formatting error messages
   71  * @param {string} defaultUrl What to return if urlString does not pass
   72  * @returns {string}
   73  */
   74 function validateUrlOrReturnDefault(
   75   urlString: string,
   76   optionName: string,
   77   defaultUrl: string,
   78 ): string {
   79   const parsedEndpoint = parseURLWithoutThrowing(urlString);
   80   // Endpoint option must be a valid URL including protocol
   81   if (!parsedEndpoint || !parsedEndpoint.protocol || !parsedEndpoint.host) {
   82     console.error(
   83       color.status.error(
   84         `Invalid ${optionName} '${urlString}'. Value must be a valid URL including protocol. Using default Snyk API URL '${defaultUrl}'`,
   85       ),
   86     );
   87     return defaultUrl;
   88   }
   89   // TODO: this debug is not printed when using the --debug flag, because flags are parsed after config. Making it async works around this
   90   setTimeout(
   91     () => debug(`Using a custom Snyk API ${optionName} '${urlString}'`),
   92     1,
   93   );
   94   return stripV1FromApiUrl(urlString);
   95 }
   96 
   97 function parseURLWithoutThrowing(urlString: string): URL | undefined {
   98   try {
   99     return new URL(urlString);
  100   } catch (error) {
  101     return undefined;
  102   }
  103 }
  104 
  105 /**
  106  * @description Removes /v1 suffix from URL if present
  107  * @param {string} url
  108  * @returns {string}
  109  */
  110 function stripV1FromApiUrl(url: string): string {
  111   const parsedUrl = new URL(url);
  112   if (/\/v1\/?$/.test(parsedUrl.pathname)) {
  113     parsedUrl.pathname = parsedUrl.pathname.replace(/\/v1\/?$/, '/');
  114     return parsedUrl.toString();
  115   }
  116   return url;
  117 }
  118 
  119 export function getV1ApiUrl(baseApiUrl: string): string {
  120   const parsedBaseUrl = new URL(baseApiUrl);
  121   parsedBaseUrl.pathname = path.join(parsedBaseUrl.pathname, 'v1');
  122   return parsedBaseUrl.toString();
  123 }
  124 
  125 /**
  126  * @description Return Snyk REST API URL
  127  * @export
  128  * @param {string} baseApiUrl
  129  * @param {string} envvarDefinedRestApiUrl
  130  * @param {string} envvarDefinedRestV3Url
  131  * @returns {string}
  132  */
  133 export function getRestApiUrl(
  134   baseApiUrl: string,
  135   envvarDefinedRestApiUrl?: string,
  136   envvarDefinedRestV3Url?: string,
  137 ): string {
  138   // REST API URL should always look like this: https://api.$DOMAIN/rest
  139   const parsedBaseUrl = new URL(baseApiUrl);
  140   parsedBaseUrl.pathname = '/rest';
  141 
  142   if (parsedBaseUrl.host?.startsWith('app.')) {
  143     // Rewrite app.snyk.io/ to api.snyk.io/rest
  144     parsedBaseUrl.host = parsedBaseUrl.host.replace(/^app\./, 'api.');
  145   } else if (
  146     // Ignore localhosts and URLs with api. already defined
  147     !parsedBaseUrl.host?.startsWith('localhost') &&
  148     !parsedBaseUrl.host?.startsWith('api.')
  149   ) {
  150     // Otherwise add the api. subdomain
  151     parsedBaseUrl.host = 'api.' + parsedBaseUrl.host;
  152   }
  153 
  154   const defaultRestApiUrl = parsedBaseUrl.toString();
  155 
  156   // TODO: notify users they can set just the (SNYK_)API envvar
  157   if (envvarDefinedRestV3Url) {
  158     return validateUrlOrReturnDefault(
  159       envvarDefinedRestV3Url,
  160       "'SNYK_API_V3_URL' environment variable",
  161       defaultRestApiUrl,
  162     );
  163   }
  164 
  165   if (envvarDefinedRestApiUrl) {
  166     return validateUrlOrReturnDefault(
  167       envvarDefinedRestApiUrl,
  168       "'SNYK_API_REST_URL' environment variable",
  169       defaultRestApiUrl,
  170     );
  171   }
  172 
  173   return defaultRestApiUrl; // Fallback to default
  174 }
  175 
  176 export function getHiddenApiUrl(restUrl: string): string {
  177   const parsedBaseUrl = new URL(restUrl);
  178 
  179   parsedBaseUrl.pathname = '/hidden';
  180 
  181   return parsedBaseUrl.toString();
  182 }
  183 
  184 export function getRootUrl(apiUrlString: string): string {
  185   // based on https://docs.snyk.io/snyk-processes/data-residency-at-snyk#what-regions-are-available the pattern is as follows
  186   // https://app.[region.]snyk.io
  187   // given an api url that starts with api means, that we can replace "api" by "app".
  188 
  189   const apiUrl = new URL(apiUrlString);
  190   apiUrl.host = apiUrl.host.replace(/^api\./, '');
  191 
  192   const rootUrl = apiUrl.protocol + '//' + apiUrl.host;
  193   return rootUrl;
  194 }