Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/cors-proxy-image/Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@
FROM --platform=linux/amd64 registry.access.redhat.com/ubi9/ubi-minimal:9.5

ARG CORS_PROXY_DEFAULT_PORT=8080
ARG CORS_PROXY_DEFAULT_ORIGIN=*
ARG CORS_PROXY_DEFAULT_ALLOWED_ORIGINS=
ARG CORS_PROXY_DEFAULT_VERBOSE=false
ARG CORS_PROXY_DEFAULT_ALLOW_HOSTS=localhost,*.github.com

ENV HOME /home/kie-sandbox
ENV NVM_DIR $HOME/.nvm
ENV NODE_VERSION v22.14.0

ENV CORS_PROXY_HTTP_PORT=$CORS_PROXY_DEFAULT_PORT
ENV CORS_PROXY_ORIGIN=$CORS_PROXY_DEFAULT_ORIGIN
ENV CORS_PROXY_ALLOWED_ORIGINS=$CORS_PROXY_DEFAULT_ALLOWED_ORIGINS
ENV CORS_PROXY_VERBOSE=$CORS_PROXY_DEFAULT_VERBOSE
ENV CORS_PROXY_ALLOW_HOSTS=$CORS_PROXY_DEFAULT_ALLOW_HOSTS

RUN mkdir $HOME \
&& chgrp -R 0 $HOME \
Expand Down
11 changes: 9 additions & 2 deletions packages/cors-proxy-image/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ export CORS_PROXY_IMAGE__imageAccount=<account>
export CORS_PROXY_IMAGE__imageName=<image-name>
export CORS_PROXY_IMAGE__imageBuildTag=<image-tag>
export CORS_PROXY_IMAGE__imagePort=<port>
export CORS_PROXY_IMAGE__imageOrigin=<origin>
export CORS_PROXY_IMAGE__imageAllowedOrigins=<allowed-origins> # Comma-separated list
export CORS_PROXY_IMAGE__imageVerbose=<verbose>
export CORS_PROXY_IMAGE__imageAllowHosts=<allow-hosts> # e.g., "*.example.com,*.github.com,localhost"
```

Default values can be found [here](./env/index.js).
Expand All @@ -62,7 +63,13 @@ docker images
Start up a new container with:

```bash
docker run -p 8080:8080 -i --rm docker.io/apache/incubator-kie-cors-proxy:main
docker run -p 8080:8080 -i --rm -e CORS_PROXY_ALLOWED_ORIGINS="http://localhost:9001" docker.io/apache/incubator-kie-cors-proxy:main
```

Or in production:

```bash
docker run -p 8080:8080 -i --rm -e CORS_PROXY_ALLOWED_ORIGINS="https://example.com,https://other.example.com" -e CORS_PROXY_ALLOW_HOSTS="*.example.com,*.github.com,localhost" docker.io/apache/incubator-kie-cors-proxy:main
```

The service will be up at http://localhost:8080
Expand Down
13 changes: 9 additions & 4 deletions packages/cors-proxy-image/env/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,18 @@ module.exports = composeEnv([rootEnv], {
default: corsProxyEnv.env.corsProxy.dev.port,
description: "HTTP port where the CORS proxy will run inside this image.",
},
CORS_PROXY_IMAGE__imageOrigin: {
default: corsProxyEnv.env.corsProxy.dev.origin,
description: "Origin to be used for the CORS proxy running inside this image.",
CORS_PROXY_IMAGE__imageAllowedOrigins: {
default: corsProxyEnv.env.corsProxy.dev.allowedOrigins,
description: "Comma-separated list of allowed origins for the CORS proxy running inside this image.",
},
CORS_PROXY_IMAGE__imageVerbose: {
default: false,
description: "Toggle verbose mode on the CORS proxy logs.",
},
CORS_PROXY_IMAGE__imageAllowHosts: {
default: corsProxyEnv.env.corsProxy.dev.allowHosts,
description: "Comma-separated list of allowed host patterns for domain filtering. Supports wildcards.",
},
}),
get env() {
return {
Expand All @@ -62,8 +66,9 @@ module.exports = composeEnv([rootEnv], {
name: getOrDefault(this.vars.CORS_PROXY_IMAGE__imageName),
buildTag: getOrDefault(this.vars.CORS_PROXY_IMAGE__imageBuildTag),
port: getOrDefault(this.vars.CORS_PROXY_IMAGE__imagePort),
origin: getOrDefault(this.vars.CORS_PROXY_IMAGE__imageOrigin),
allowedOrigins: getOrDefault(this.vars.CORS_PROXY_IMAGE__imageAllowedOrigins),
verbose: getOrDefault(this.vars.CORS_PROXY_IMAGE__imageVerbose),
allowHosts: getOrDefault(this.vars.CORS_PROXY_IMAGE__imageAllowHosts),
},
},
};
Expand Down
2 changes: 1 addition & 1 deletion packages/cors-proxy-image/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"copy:cors-proxy-package": "run-script-os",
"copy:cors-proxy-package:linux:darwin": "cp -R ./node_modules/@kie-tools/cors-proxy/dist/ dist-dev/cors-proxy/",
"copy:cors-proxy-package:win32": "pnpm powershell \"Copy-Item -R ./node_modules/@kie-tools/cors-proxy/dist/ dist-dev/cors-proxy/\"",
"docker:build": "kie-tools--image-builder build -r \"$(build-env corsProxyImage.image.registry)\" -a \"$(build-env corsProxyImage.image.account)\" -n \"$(build-env corsProxyImage.image.name)\" -t \"$(build-env corsProxyImage.image.buildTag)\" --build-arg \"CORS_PROXY_DEFAULT_HTTP_PORT=$(build-env corsProxyImage.image.port)\" --build-arg \"CORS_PROXY_DEFAULT_ORIGIN=$(build-env corsProxyImage.image.origin)\" --build-arg \"CORS_PROXY_DEFAULT_VERBOSE=$(build-env corsProxyImage.image.verbose)\""
"docker:build": "kie-tools--image-builder build -r \"$(build-env corsProxyImage.image.registry)\" -a \"$(build-env corsProxyImage.image.account)\" -n \"$(build-env corsProxyImage.image.name)\" -t \"$(build-env corsProxyImage.image.buildTag)\" --build-arg \"CORS_PROXY_DEFAULT_PORT=$(build-env corsProxyImage.image.port)\" --build-arg \"CORS_PROXY_DEFAULT_ALLOWED_ORIGINS=$(build-env corsProxyImage.image.allowedOrigins)\" --build-arg \"CORS_PROXY_DEFAULT_VERBOSE=$(build-env corsProxyImage.image.verbose)\" --build-arg \"CORS_PROXY_DEFAULT_ALLOW_HOSTS=$(build-env corsProxyImage.image.allowHosts)\""
},
"devDependencies": {
"@kie-tools/cors-proxy": "workspace:*",
Expand Down
19 changes: 11 additions & 8 deletions packages/cors-proxy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,22 @@ This package contains a `cors-proxy`, which is a simple Node.js application inte

The `cors-proxy` can be configured via environment variables:

- CORS_PROXY_HTTP_PORT: Sets the HTTP Port the proxy should listen to
- CORS_PROXY_ORIGIN: Sets the value of the 'Access-Control-Allow-Origin' header. Defaults to `*`.
- CORS_PROXY_ALLOWED_ORIGINS: **Required** Comma-separated list of allowed origins. Wildcard `*` is not allowed for security reasons.
- CORS_PROXY_HTTP_PORT: Sets the HTTP Port the proxy should listen to. Defaults to `8080`
- CORS_PROXY_VERBOSE: Allows the proxy to run in verbose mode... useful to trace requests on development environments. Defaults to `false`
- CORS_PROXY_USE_HTTP_FOR_HOSTS: Comma-separated list of hosts that should use the `http` protocol for proxied requests. Defaults to an empty list.
- CORS_PROXY_ALLOW_HOSTS: Comma-separated list of allowed host patterns for domain filtering. Supports wildcards (e.g., `*.example.com`, `*.github.com`). Only requests to matching hosts will be proxied. Defaults to `localhost,*.github.com`.
- HTTP_PROXY or HTTPS_PROXY: Url of a proxy that will be used to proxy the requests `cors-proxy` is already proxying.
- NODE_EXTRA_CA_CERTS: This is used by NodeJS itself to add cartificates to the chain. See more at https://nodejs.org/api/cli.html#node_extra_ca_certsfile

For example:

```bash
export CORS_PROXY_HTTP_PORT=8080
export CORS_PROXY_ORIGIN=*
export CORS_PROXY_ALLOWED_ORIGINS="https://example.com,https://other.example.com"
export CORS_PROXY_VERBOSE=false
export CORS_PROXY_USE_HTTP_FOR_HOSTS="localhost:8080,localhost:8081"
export CORS_PROXY_ALLOW_HOSTS="*.example.com,*.github.com"
```

# Build
Expand All @@ -56,16 +58,17 @@ node ./dist/index.js
# Running `cors-proxy` in dev mode.

```bash
pnpm -F @kie-tools/cors-proxy start
CORS_PROXY__allowedOrigins="http://localhost:9001" pnpm -F @kie-tools/cors-proxy start
```

You can also use the following envs to configure `cors-proxy` when starting in dev-mode:

```bash
export CORS_PROXY__allowedOrigins="http://localhost:9001"
export CORS_PROXY__port=*
export CORS_PROXY__origin=*
export CORS_PROXY__verbose=false
export CORS_PROXY__useHttpForHosts="localhost:8080,localhost:8081"
export CORS_PROXY__allowHosts="*.example.com,*.github.com,localhost"
```

Default values can be found [here](./env/index.js).
Expand All @@ -83,7 +86,7 @@ mitmweb --set listen_port=<PORT> --showhost
Now set the HTTPS_PROXY and NODE_EXTRA_CA_CERTS environment variables before starting the `cors-proxy` service:

```bash
export HTTPS_PROXY=http://localhost:<PORT
export HTTPS_PROXY=http://localhost:<PORT>
export NODE_EXTRA_CA_CERTS=~/.mitmproxy/mitmproxy-ca-cert.pem
```

Expand All @@ -93,9 +96,9 @@ Set the rest of the environment variables and start the `cors-proxy` service:

```bash
export CORS_PROXY__port=*
export CORS_PROXY__origin=*
export CORS_PROXY__allowedOrigins="http://localhost:9001"

pnpm -F @kie-tools/cors-proxy start
pnpm -F @kie-tools/cors-proxy start
```

---
Expand Down
15 changes: 10 additions & 5 deletions packages/cors-proxy/env/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ const { varsWithName, composeEnv, getOrDefault } = require("@kie-tools-scripts/b

module.exports = composeEnv([require("@kie-tools/root-env/env")], {
vars: varsWithName({
CORS_PROXY__allowedOrigins: {
default: "http://localhost:9001",
description: "Comma-separated list of allowed origins used to set on the 'Access-Control-Allow-Origin' header",
},
CORS_PROXY__port: {
default: 8080,
description: "HTTP Port the proxy should listen to",
},
CORS_PROXY__origin: {
default: "*",
description: "Value to set on the 'Access-Control-Allow-Origin' header",
},
CORS_PROXY__verbose: {
default: true,
description: "Allows the proxy to run in verbose mode... useful to trace requests on development environments",
Expand All @@ -37,15 +37,20 @@ module.exports = composeEnv([require("@kie-tools/root-env/env")], {
default: true,
description: "Use `http` as default protocol for proxied requests. If `false`, `https` is used.",
},
CORS_PROXY__allowHosts: {
default: "localhost,*.github.com",
description: "Comma-separated list of allowed host patterns. Supports wildcards (e.g., '*.example.com').",
},
}),
get env() {
return {
corsProxy: {
dev: {
allowedOrigins: getOrDefault(this.vars.CORS_PROXY__allowedOrigins),
port: getOrDefault(this.vars.CORS_PROXY__port),
origin: getOrDefault(this.vars.CORS_PROXY__origin),
verbose: getOrDefault(this.vars.CORS_PROXY__verbose),
useHttpForHosts: getOrDefault(this.vars.CORS_PROXY__useHttpForHosts),
allowHosts: getOrDefault(this.vars.CORS_PROXY__allowHosts),
},
},
};
Expand Down
6 changes: 4 additions & 2 deletions packages/cors-proxy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
"build:prod": "pnpm lint && pnpm test && rimraf dist && webpack",
"lint": "run-script-if --bool \"$(build-env linters.run)\" --then \"kie-tools--eslint ./src\"",
"start": "run-script-os",
"start:darwin:linux": "pnpm build:dev && cross-env CORS_PROXY_USE_HTTP_FOR_HOSTS=$(build-env corsProxy.dev.useHttpForHosts) CORS_PROXY_HTTP_PORT=$(build-env corsProxy.dev.port) CORS_PROXY_ORIGIN=$(build-env corsProxy.dev.origin) CORS_PROXY_VERBOSE=$(build-env corsProxy.dev.verbose) node dist/index.js",
"start:win32": "pnpm build:dev && pnpm powershell \"cross-env CORS_PROXY_USE_HTTP_FOR_HOSTS=$(build-env corsProxy.dev.useHttpForHosts) CORS_PROXY_HTTP_PORT=$(build-env corsProxy.dev.port) CORS_PROXY_ORIGIN=$(build-env corsProxy.dev.origin) CORS_PROXY_VERBOSE=$(build-env corsProxy.dev.verbose) node dist/index.js\"",
"start:darwin:linux": "pnpm build:dev && cross-env CORS_PROXY_USE_HTTP_FOR_HOSTS=$(build-env corsProxy.dev.useHttpForHosts) CORS_PROXY_HTTP_PORT=$(build-env corsProxy.dev.port) CORS_PROXY_ALLOWED_ORIGINS=$(build-env corsProxy.dev.allowedOrigins) CORS_PROXY_VERBOSE=$(build-env corsProxy.dev.verbose) CORS_PROXY_ALLOW_HOSTS=$(build-env corsProxy.dev.allowHosts) node dist/index.js",
"start:win32": "pnpm build:dev && pnpm powershell \"cross-env CORS_PROXY_USE_HTTP_FOR_HOSTS=$(build-env corsProxy.dev.useHttpForHosts) CORS_PROXY_HTTP_PORT=$(build-env corsProxy.dev.port) CORS_PROXY_ALLOWED_ORIGINS=$(build-env corsProxy.dev.allowedOrigins) CORS_PROXY_VERBOSE=$(build-env corsProxy.dev.verbose) CORS_PROXY_ALLOW_HOSTS=$(build-env corsProxy.dev.allowHosts) node dist/index.js\"",
"test": "run-script-if --ignore-errors \"$(build-env tests.ignoreFailures)\" --bool \"$(build-env tests.run)\" --then \"jest --silent --verbose --passWithNoTests\""
},
"dependencies": {
"cors": "^2.8.5",
"express": "^4.21.2",
"https-proxy-agent": "^7.0.6",
"minimatch": "^3.0.5",
"node-fetch": "^3.3.2"
},
"devDependencies": {
Expand All @@ -43,6 +44,7 @@
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.12",
"@types/minimatch": "^3.0.5",
"@types/node": "^22.14.1",
"cross-env": "^7.0.3",
"jest": "^29.7.0",
Expand Down
18 changes: 17 additions & 1 deletion packages/cors-proxy/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,28 @@ function getPort(): number {
return 8080;
}

function getAllowedOrigins(): string[] {
const origins = process.env.CORS_PROXY_ALLOWED_ORIGINS || "";
const originsList = origins.split(",").map((o) => o.trim());

if (originsList.some((o) => o === "")) {
throw new Error("Invalid origin: empty origins are not allowed in CORS_PROXY_ALLOWED_ORIGINS.");
}

if (originsList.some((o) => o === "*")) {
throw new Error('Invalid origin: wildcard "*" is not allowed in CORS_PROXY_ALLOWED_ORIGINS.');
}

return originsList;
}

export const run = () => {
startServer({
allowedOrigins: getAllowedOrigins(),
port: getPort(),
origin: process.env.CORS_PROXY_ORIGIN ?? "*",
verbose: process.env.CORS_PROXY_VERBOSE === "true",
hostsToUseHttp: (process.env.CORS_PROXY_USE_HTTP_FOR_HOSTS || undefined)?.split(",") ?? [],
allowHosts: process.env.CORS_PROXY_ALLOW_HOSTS?.split(",") || ["localhost", "*.github.com"],
});
};

Expand Down
24 changes: 18 additions & 6 deletions packages/cors-proxy/src/proxy/ExpressCorsProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import * as https from "https";
import fetch from "node-fetch";
import { Request, Response } from "express";
import { HttpsProxyAgent } from "https-proxy-agent";
import * as minimatch from "minimatch";
import { GIT_CORS_CONFIG, isGitOperation } from "./git";
import { CorsProxyHeaderKeys, CorsConfig, CorsProxy } from "@kie-tools/cors-proxy-api/dist";

Expand All @@ -37,16 +38,16 @@ export class ExpressCorsProxy implements CorsProxy<Request, Response> {

constructor(
private readonly args: {
origin: string;
allowedOrigins: string[];
verbose: boolean;
hostsToUseHttp: string[];
allowHosts: string[];
}
) {
this.logger = new Logger(args.verbose);

this.logger.debug("");
this.logger.debug("Proxy Configuration:");
this.logger.debug("* Accept Origin Header: ", `"${args.origin}"`);
this.logger.debug("* Verbose: ", args.verbose);
this.logger.debug("");
}
Expand Down Expand Up @@ -92,9 +93,6 @@ export class ExpressCorsProxy implements CorsProxy<Request, Response> {
});
this.logger.debug("Proxy Response status: ", proxyResponse.status);

// Setting up the headers to the original response...
res.header("Access-Control-Allow-Origin", this.args.origin);

if (req.method == "OPTIONS") {
res.header("Access-Control-Allow-Methods", info.corsConfig?.allowMethods.join(", ") ?? "*");
res.header("Access-Control-Allow-Headers", info.corsConfig?.allowHeaders.join(", ") ?? "*");
Expand All @@ -105,6 +103,9 @@ export class ExpressCorsProxy implements CorsProxy<Request, Response> {
}

proxyResponse.headers.forEach((value, header) => {
if (header.toLowerCase() === "access-control-allow-origin") {
return;
}
if (!info.corsConfig || info.corsConfig.exposeHeaders.includes(header)) {
res.setHeader(header, value);
}
Expand All @@ -118,7 +119,7 @@ export class ExpressCorsProxy implements CorsProxy<Request, Response> {

res.status(proxyResponse.status);

this.logger.debug("Writting Response...");
this.logger.debug("Writing Response...");
if (proxyResponse.body) {
const stream = proxyResponse.body.pipe(res);
stream.on("close", () => {
Expand All @@ -140,7 +141,18 @@ export class ExpressCorsProxy implements CorsProxy<Request, Response> {
}

private resolveRequestInfo(request: Request): ProxyRequestInfo {
const origin = request.header("origin");
const targetUrl: string = (request.headers[CorsProxyHeaderKeys.TARGET_URL] as string) ?? request.url;
const parsedTargetUrl = new URL(targetUrl);
Copy link
Member

@thiagoelg thiagoelg Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue seems to be here. Things like /api.rm2.thpm.p1.openshiftapps.com:6443/version are not valid as a parameter for new URL() and throw errors due to the leading /.

Copy link
Member

@thiagoelg thiagoelg Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But even after doing const parsedTargetUrl = new URL(targetUrl.slice(1)) this is the resulting URL:

[0] { targetUrl: '/api.rm2.thpm.p1.openshiftapps.com:6443/version' }
[0] {
[0]   parsedTargetUrl: URL {
[0]     href: 'api.rm2.thpm.p1.openshiftapps.com:6443/version',
[0]     origin: 'null',
[0]     protocol: 'api.rm2.thpm.p1.openshiftapps.com:',
[0]     username: '',
[0]     password: '',
[0]     host: '',
[0]     hostname: '',
[0]     port: '',
[0]     pathname: '6443/version',
[0]     search: '',
[0]     searchParams: URLSearchParams {},
[0]     hash: ''
[0]   }
[0] }

Notice that the hostname is empty (probably because the URL doesn't have the protocol), and it will make the allowHosts check fail.


if (!this.args.allowHosts.some((pattern) => minimatch(parsedTargetUrl.hostname, pattern))) {
throw new Error(`The target URL in not allowed. Requested: ${targetUrl}`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new Error(`The target URL in not allowed. Requested: ${targetUrl}`);
throw new Error(`The target URL is not allowed. Requested: ${targetUrl}`);

}

if (!origin || !this.args.allowedOrigins.includes(origin)) {
throw new Error(`Origin ${origin} is not allowed`);
}

if (!targetUrl || targetUrl == "/") {
throw new Error("Couldn't resolve the target URL...");
}
Expand Down
16 changes: 13 additions & 3 deletions packages/cors-proxy/src/proxy/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,21 @@ import * as cors from "cors";
import { ExpressCorsProxy } from "./ExpressCorsProxy";

export type ServerArgs = {
allowedOrigins: string[];
port: number;
origin: string;
verbose: boolean;
hostsToUseHttp: string[];
allowHosts: string[];
};

export const startServer = (args: ServerArgs): void => {
console.log("Starting CORS proxy...");
console.log("====================================================");
console.log(`Origin: ${args.origin}`);
console.log(`Allowed Origins: ${args.allowedOrigins.join(", ")}`);
console.log(`Port: ${args.port}`);
console.log(`Verbose: ${args.verbose}`);
console.log(`Hosts to proxy with HTTP: ${args.hostsToUseHttp}`);
console.log(`Allow hosts: ${args.allowHosts}`);
console.log("====================================================");

const app: express.Express = express();
Expand All @@ -44,7 +46,15 @@ export const startServer = (args: ServerArgs): void => {

const proxy = new ExpressCorsProxy(args);

const corsHandler = cors({ origin: args.origin });
const corsHandler = cors({
origin: (origin, cb) => {
if (!origin || !args.allowedOrigins.includes(origin)) {
return cb(null, false);
}

return cb(null, origin);
},
});

app.use(corsHandler);
app.options("/", corsHandler); // enable pre-flight requests
Expand Down
Loading
Loading