CVE-2022-24856: Server-Side Request Forgery (SSRF) (CWE-918)
value: FlyteConsole is the web user interface for the Flyte platform. FlyteConsole prior to version 0.52.0 is vulnerable to server-side request forgery (SSRF) when FlyteConsole is open to the general internet. An attacker can exploit any user of a vulnerable instance to access the internal metadata server or other unauthenticated URLs. Passing of headers to an unauthorized actor may occur. The patch for this issue deletes the entire `cors_proxy`, as this is not required for console anymore. A patch is available in FlyteConsole version 0.52.0. Disable FlyteConsole availability on the internet as a workaround.
CWE description: The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
CVSS v3 base score: 7.5
Published: 2022-05-17T16:15Z
Repository: https://github.com/flyteorg/flyteconsole
Language: Markdown
Files changed: CONTRIBUTING.md, Dockerfile, README.md, index.js
Commit date: 2022-04-14 15:31:24 -0700
Commit message: feat: cleanup (#389)

Signed-off-by: Carina Ursu <carina@union.ai>

Co-authored-by: Carina Ursu <carina@union.ai>
Diff: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d8e9e04..f0c5e56 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -132,29 +132,10 @@ asdf install
 
 ```bash
 yarn install
 ```
 
-## CORS Proxying: Recommended Setup
-
-In the common hosting arrangement, all API requests will be to the same origin
-serving the client application, making CORS unnecessary. However, if you would like
-to setup your local dev enviornment to target a FlyteAdmin service running on a different
-domain you will need to configure your enviornment to support CORS. One example would be
-hosting the Admin API on a different domain than the console. Another example is
-when fetching execution data from external storage such as S3.
-
-The fastest (recommended) way to setup a CORS solution is to do so within the browser.
-If you would like to handle this at the Node level you will need to disable authentication
-(see below).
-
-_NOTE:_ Do not configure for both browser and Node solutions.
-
-These instructions require using Google Chrome. You will also need to identify the
-URL of your target FlyteAdmin API instance. These instructions will use
-`https://different.admin.service.com` as an example.
-
 1. Set `ADMIN_API_URL` and `ADMIN_API_USE_SSL`
 
     ```
       export ADMIN_API_URL=https://different.admin.service.com
       export ADMIN_API_USE_SSL="https"
diff --git a/Dockerfile b/Dockerfile
index 693a346..10bff33 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,11 +15,11 @@ RUN : \
 COPY . .
 RUN : \
   # build
   && make build_prod \
   # place the runtime application in /app
-  && mv dist corsProxy.js index.js env.js plugins.js /app
+  && mv dist index.js env.js plugins.js /app
 
 FROM gcr.io/distroless/nodejs
 LABEL org.opencontainers.image.source https://github.com/lyft/flyteconsole
 
 COPY --from=builder /app app
diff --git a/README.md b/README.md
index e8f5f0b..c047de5 100644
--- a/README.md
+++ b/README.md
@@ -86,14 +86,10 @@ For help with installing dependencies look into
     This allows running the console at a prefix on the target host. This is
     necessary when hosting the API and console on the same domain (with prefixes of
     `/api/v1` and `/console` for example). For local development, this is
     usually not needed, so the default behavior is to run without a prefix.
 
-* `CORS_PROXY_PREFIX` (default: `/cors_proxy`)
-
-    Sets the local endpoint for [CORS request proxying](CONTRIBUTING.md#cors-proxying-recommended-setup).
-
 ### Running from docker image as localhost
 
 To run flyteconsole directly from your docker image as localhost you must set a
 few environment variables in your run command to setup the appliation.
 
@@ -138,30 +134,10 @@ For continious development we are using:
 * **[Google Analytics](CONTRIBUTING.md#google-analytics)**
   \- adds tracking code to the app or website. To disable use `ENABLE_GA=false`
 
 More info on each section could be found at [CONTRIBUTING.md](CONTRIBUTING.md)
 
-### CORS Proxying: Recommended setup
-
-In the common hosting arrangement, all API requests will be to the same origin
-serving the client application, making CORS unnecessary. However, if you would like
-to setup your local dev enviornment to target a FlyteAdmin service running on a different
-domain you will need to configure your enviornment support CORS. One example would be
-hosting the Admin API on a different domain than the console. Another example is
-when fetching execution data from external storage such as S3.
-
-The fastest (recommended) way to setup a CORS solution is to do so within the browser. 
-If you would like to handle this at the Node level you will need to disable authentication
-(see below)
-
-> Do not configure for both browser and Node solutions. 
-
-These instructions require using Google Chrome. You will also need to identify the 
-URL of your target FlyteAdmin API instance. These instructions will use
-`https://different.admin.service.com` as an example.
-
-
 * Set `ADMIN_API_URL` and `ADMIN_API_USE_SSL`
    
    ```bash
    export ADMIN_API_URL=https://different.admin.service.com
    export ADMIN_API_USE_SSL="https"
diff --git a/corsProxy.js b/corsProxy.js
deleted file mode 100644
index 3ddfdfe..0000000
--- a/corsProxy.js
+++ /dev/null
@@ -1,49 +0,0 @@
-const http = require('http');
-const https = require('https');
-const url = require('url');
-
-/** Mounts a proxy at `basePath` such that requests of the form
- * `${basePath}/http://www.example.com?queryString=value` will be converted to
- * `http://www.example.com?queryString=value`, preserving any request headers.
- * The response is piped directly to the client with no processing.
- */
-module.exports = function corsProxy(basePath) {
-  const pathOffset = basePath.length;
-  return function (req, res, next) {
-    const pathIndex = req.url.indexOf(basePath);
-    // base path doesn't match, ignore
-    if (pathIndex < 0) {
-      return next();
-    }
-
-    const targetUrl = req.url.substring(pathIndex + pathOffset + 1);
-    const config = url.parse(targetUrl);
-    config.method = req.method;
-
-    // We need to use a different request class depending on the
-    // protocol
-    const handler = config.protocol === 'https:' ? https : http;
-
-    const proxyReq = handler.request(config, function (proxyRes) {
-      res.writeHead(proxyRes.statusCode, proxyRes.headers);
-      proxyRes.pipe(res, {
-        end: true,
-      });
-    });
-
-    proxyReq.on('error', (err) => res.status(500).send(err));
-
-    // Copy over all headers except for 'Host', since that value would
-    // point to *this* server, and not the remote server.
-    Object.keys(req.headers).forEach((key) => {
-      if (key.toLowerCase() === 'host') {
-        return;
-      }
-      proxyReq.setHeader(key, req.headers[key]);
-    });
-
-    req.pipe(proxyReq, {
-      end: true,
-    });
-  };
-};
diff --git a/index.js b/index.js
index fd487f7..b258915 100644
--- a/index.js
+++ b/index.js
@@ -6,19 +6,17 @@
 /* eslint-disable no-console */
 const morgan = require('morgan');
 const express = require('express');
 const env = require('./env');
 const { applyMiddleware } = require('./plugins');
-const corsProxy = require('./corsProxy.js');
 
 const app = express();
 
 // Enable logging for HTTP access
 app.use(morgan('combined'));
 app.use(express.json());
 app.get(`${env.BASE_URL}/healthz`, (_req, res) => res.status(200).send());
-app.use(corsProxy(`${env.BASE_URL}${env.CORS_PROXY_PREFIX}`));
 
 if (typeof applyMiddleware === 'function') {
   console.log('Found middleware plugins, applying...');
   applyMiddleware(app);
 }

Vulnerable code: ## CORS Proxying: Recommended Setup

In the common hosting arrangement, all API requests will be to the same origin
serving the client application, making CORS unnecessary. However, if you would like
to setup your local dev enviornment to target a FlyteAdmin service running on a different
domain you will need to configure your enviornment to support CORS. One example would be
hosting the Admin API on a different domain than the console. Another example is
when fetching execution data from external storage such as S3.

The fastest (recommended) way to setup a CORS solution is to do so within the browser.
If you would like to handle this at the Node level you will need to disable authentication
(see below).

_NOTE:_ Do not configure for both browser and Node solutions.

These instructions require using Google Chrome. You will also need to identify the
URL of your target FlyteAdmin API instance. These instructions will use
`https://different.admin.service.com` as an example.

  && mv dist corsProxy.js index.js env.js plugins.js /app
* `CORS_PROXY_PREFIX` (default: `/cors_proxy`)

    Sets the local endpoint for [CORS request proxying](CONTRIBUTING.md#cors-proxying-recommended-setup).

### CORS Proxying: Recommended setup

In the common hosting arrangement, all API requests will be to the same origin
serving the client application, making CORS unnecessary. However, if you would like
to setup your local dev enviornment to target a FlyteAdmin service running on a different
domain you will need to configure your enviornment support CORS. One example would be
hosting the Admin API on a different domain than the console. Another example is
when fetching execution data from external storage such as S3.

The fastest (recommended) way to setup a CORS solution is to do so within the browser. 
If you would like to handle this at the Node level you will need to disable authentication
(see below)

> Do not configure for both browser and Node solutions. 

These instructions require using Google Chrome. You will also need to identify the 
URL of your target FlyteAdmin API instance. These instructions will use
`https://different.admin.service.com` as an example.


const http = require('http');
const https = require('https');
const url = require('url');

/** Mounts a proxy at `basePath` such that requests of the form
 * `${basePath}/http://www.example.com?queryString=value` will be converted to
 * `http://www.example.com?queryString=value`, preserving any request headers.
 * The response is piped directly to the client with no processing.
 */
module.exports = function corsProxy(basePath) {
  const pathOffset = basePath.length;
  return function (req, res, next) {
    const pathIndex = req.url.indexOf(basePath);
    // base path doesn't match, ignore
    if (pathIndex < 0) {
      return next();
    }

    const targetUrl = req.url.substring(pathIndex + pathOffset + 1);
    const config = url.parse(targetUrl);
    config.method = req.method;

    // We need to use a different request class depending on the
    // protocol
    const handler = config.protocol === 'https:' ? https : http;

    const proxyReq = handler.request(config, function (proxyRes) {
      res.writeHead(proxyRes.statusCode, proxyRes.headers);
      proxyRes.pipe(res, {
        end: true,
      });
    });

    proxyReq.on('error', (err) => res.status(500).send(err));

    // Copy over all headers except for 'Host', since that value would
    // point to *this* server, and not the remote server.
    Object.keys(req.headers).forEach((key) => {
      if (key.toLowerCase() === 'host') {
        return;
      }
      proxyReq.setHeader(key, req.headers[key]);
    });

    req.pipe(proxyReq, {
      end: true,
    });
  };
};
const corsProxy = require('./corsProxy.js');
app.use(corsProxy(`${env.BASE_URL}${env.CORS_PROXY_PREFIX}`));
Fixed code:   && mv dist index.js env.js plugins.js /app