This is an automated email from the ASF dual-hosted git repository.
thiagoelg pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git
The following commit(s) were added to refs/heads/main by this push:
new 26689ddb9ad kie-tools#3349: [cors-proxy] Implement domains filtering
(#3353)
26689ddb9ad is described below
commit 26689ddb9ad8f89f9fd6915a5df5c34b614ce6de
Author: Fabrizio Antonangeli <[email protected]>
AuthorDate: Tue Dec 9 15:08:02 2025 +0100
kie-tools#3349: [cors-proxy] Implement domains filtering (#3353)
---
packages/cors-proxy-image/Containerfile | 2 +
packages/cors-proxy-image/README.md | 3 +-
packages/cors-proxy-image/env/index.js | 5 +
packages/cors-proxy-image/package.json | 2 +-
packages/cors-proxy/README.md | 3 +
packages/cors-proxy/env/index.js | 6 +
packages/cors-proxy/package.json | 6 +-
packages/cors-proxy/src/index.ts | 16 ++
packages/cors-proxy/src/proxy/ExpressCorsProxy.ts | 16 +-
packages/cors-proxy/src/proxy/server.ts | 2 +
packages/cors-proxy/tests/ExpressCorsProxy.test.ts | 187 +++++++++++++++++++++
packages/cors-proxy/tests/index.test.ts | 57 ++++++-
packages/cors-proxy/tests/server.test.ts | 37 ++++
packages/online-editor/package.json | 4 +-
pnpm-lock.yaml | 8 +-
15 files changed, 337 insertions(+), 17 deletions(-)
diff --git a/packages/cors-proxy-image/Containerfile
b/packages/cors-proxy-image/Containerfile
index 84450e5b32c..3505965e50b 100644
--- a/packages/cors-proxy-image/Containerfile
+++ b/packages/cors-proxy-image/Containerfile
@@ -20,6 +20,7 @@ FROM --platform=linux/amd64
registry.access.redhat.com/ubi9/ubi-minimal:9.5
ARG CORS_PROXY_DEFAULT_PORT=8080
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
@@ -28,6 +29,7 @@ ENV NODE_VERSION v22.14.0
ENV CORS_PROXY_HTTP_PORT=$CORS_PROXY_DEFAULT_PORT
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 \
diff --git a/packages/cors-proxy-image/README.md
b/packages/cors-proxy-image/README.md
index 98f17ba6100..01f8a9eccd1 100644
--- a/packages/cors-proxy-image/README.md
+++ b/packages/cors-proxy-image/README.md
@@ -41,6 +41,7 @@ export CORS_PROXY_IMAGE__imageBuildTag=<image-tag>
export CORS_PROXY_IMAGE__imagePort=<port>
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).
@@ -68,7 +69,7 @@ docker run -p 8080:8080 -i --rm -e
CORS_PROXY_ALLOWED_ORIGINS="http://localhost:
Or in production:
```bash
-docker run -p 8080:8080 -i --rm -e
CORS_PROXY_ALLOWED_ORIGINS="https://example.com,https://other.example.com"
docker.io/apache/incubator-kie-cors-proxy:main
+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
diff --git a/packages/cors-proxy-image/env/index.js
b/packages/cors-proxy-image/env/index.js
index 411050ddea1..cecfcb1af31 100644
--- a/packages/cors-proxy-image/env/index.js
+++ b/packages/cors-proxy-image/env/index.js
@@ -52,6 +52,10 @@ module.exports = composeEnv([rootEnv], {
default: false,
description: "Toggle verbose mode on the CORS proxy logs.",
},
+ CORS_PROXY_IMAGE__imageAllowHosts: {
+ default: corsProxyEnv.env.corsProxy.dev.allowedHosts,
+ description: "Comma-separated list of allowed host patterns for domain
filtering. Supports wildcards.",
+ },
}),
get env() {
return {
@@ -64,6 +68,7 @@ module.exports = composeEnv([rootEnv], {
port: getOrDefault(this.vars.CORS_PROXY_IMAGE__imagePort),
allowedOrigins:
getOrDefault(this.vars.CORS_PROXY_IMAGE__imageAllowedOrigins),
verbose: getOrDefault(this.vars.CORS_PROXY_IMAGE__imageVerbose),
+ allowedHosts:
getOrDefault(this.vars.CORS_PROXY_IMAGE__imageAllowHosts),
},
},
};
diff --git a/packages/cors-proxy-image/package.json
b/packages/cors-proxy-image/package.json
index d518b2b0fab..840fe25224f 100644
--- a/packages/cors-proxy-image/package.json
+++ b/packages/cors-proxy-image/package.json
@@ -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_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 corsProxyImag [...]
+ "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 corsProxyImag [...]
},
"devDependencies": {
"@kie-tools/cors-proxy": "workspace:*",
diff --git a/packages/cors-proxy/README.md b/packages/cors-proxy/README.md
index 9a4edc4d157..9eddcc989ef 100644
--- a/packages/cors-proxy/README.md
+++ b/packages/cors-proxy/README.md
@@ -27,6 +27,7 @@ The `cors-proxy` can be configured via environment variables:
- 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_ALLOWED_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
@@ -37,6 +38,7 @@ export CORS_PROXY_HTTP_PORT=8080
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_ALLOWED_HOSTS="*.example.com,*.github.com"
```
# Build
@@ -66,6 +68,7 @@ export CORS_PROXY__allowedOrigins="http://localhost:9001"
export CORS_PROXY__port=*
export CORS_PROXY__verbose=false
export CORS_PROXY__useHttpForHosts="localhost:8080,localhost:8081"
+export CORS_PROXY__allowedHosts="*.example.com,*.github.com,localhost"
```
Default values can be found [here](./env/index.js).
diff --git a/packages/cors-proxy/env/index.js b/packages/cors-proxy/env/index.js
index c7ace6d1fed..4703529f6d1 100644
--- a/packages/cors-proxy/env/index.js
+++ b/packages/cors-proxy/env/index.js
@@ -37,6 +37,11 @@ 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__allowedHosts: {
+ default: "*",
+ description:
+ "Comma-separated list of allowed host patterns. Supports wildcards
(e.g., '*.target.example.com,*.github.com').",
+ },
}),
get env() {
return {
@@ -46,6 +51,7 @@ module.exports =
composeEnv([require("@kie-tools/root-env/env")], {
port: getOrDefault(this.vars.CORS_PROXY__port),
verbose: getOrDefault(this.vars.CORS_PROXY__verbose),
useHttpForHosts: getOrDefault(this.vars.CORS_PROXY__useHttpForHosts),
+ allowedHosts: getOrDefault(this.vars.CORS_PROXY__allowedHosts),
},
},
};
diff --git a/packages/cors-proxy/package.json b/packages/cors-proxy/package.json
index 82cc1781adf..91a61e20027 100644
--- a/packages/cors-proxy/package.json
+++ b/packages/cors-proxy/package.json
@@ -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_ALLOWED_ORIGINS=$(build-env corsProxy.dev.allowedOrigins)
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_ALLOWED_ORIGINS=$(build-env corsProxy.dev.allowedOrigins)
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_ALLOWED_HOSTS=$(build-env corsProxy.dev.allowedHosts) 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_ALLOWED_HOSTS=$(build-env corsProxy.dev.allowedHosts) 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": {
@@ -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",
diff --git a/packages/cors-proxy/src/index.ts b/packages/cors-proxy/src/index.ts
index 6020b615b0e..7caec5711f0 100644
--- a/packages/cors-proxy/src/index.ts
+++ b/packages/cors-proxy/src/index.ts
@@ -42,12 +42,28 @@ function getAllowedOrigins(): string[] {
return originsList;
}
+function getAllowedHosts(): string[] {
+ const hosts = process.env.CORS_PROXY_ALLOWED_HOSTS ||
"localhost,*.github.com";
+ const hostsList = hosts.split(",").map((o) => o.trim());
+
+ if (hostsList.some((o) => o === "")) {
+ throw new Error("Invalid host: empty hosts are not allowed in
CORS_PROXY_ALLOWED_HOSTS.");
+ }
+
+ if (hostsList.some((o) => o === "*")) {
+ console.warn('Wildcard alone "*" is strongly discouraged in
CORS_PROXY_ALLOWED_HOSTS production environments.');
+ }
+
+ return hostsList;
+}
+
export const run = () => {
startServer({
allowedOrigins: getAllowedOrigins(),
port: getPort(),
verbose: process.env.CORS_PROXY_VERBOSE === "true",
hostsToUseHttp: (process.env.CORS_PROXY_USE_HTTP_FOR_HOSTS ||
undefined)?.split(",") ?? [],
+ allowedHosts: getAllowedHosts(),
});
};
diff --git a/packages/cors-proxy/src/proxy/ExpressCorsProxy.ts
b/packages/cors-proxy/src/proxy/ExpressCorsProxy.ts
index 9ae560ab737..edc367daed9 100644
--- a/packages/cors-proxy/src/proxy/ExpressCorsProxy.ts
+++ b/packages/cors-proxy/src/proxy/ExpressCorsProxy.ts
@@ -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";
@@ -38,6 +39,7 @@ export class ExpressCorsProxy implements CorsProxy<Request,
Response> {
constructor(
private readonly args: {
allowedOrigins: string[];
+ allowedHosts: string[];
verbose: boolean;
hostsToUseHttp: string[];
}
@@ -117,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", () => {
@@ -138,9 +140,17 @@ export class ExpressCorsProxy implements
CorsProxy<Request, Response> {
}
}
+ private validateTargetUrl(targetUrl: string): boolean {
+ const protocol = /^https?:\/\//.test(targetUrl) ? "" : "https:/";
+ const parsedTargetUrl = new URL(protocol + targetUrl);
+
+ return this.args.allowedHosts.some((pattern) =>
minimatch(parsedTargetUrl.hostname, pattern));
+ }
+
private resolveRequestInfo(request: Request): ProxyRequestInfo {
const origin = request.header("origin");
const targetUrl: string = (request.headers[CorsProxyHeaderKeys.TARGET_URL]
as string) ?? request.url;
+
if (!origin || !this.args.allowedOrigins.includes(origin)) {
throw new Error(`Origin ${origin} is not allowed`);
}
@@ -149,6 +159,10 @@ export class ExpressCorsProxy implements
CorsProxy<Request, Response> {
throw new Error("Couldn't resolve the target URL...");
}
+ if (!this.validateTargetUrl(targetUrl)) {
+ throw new Error(`The target URL is not allowed. Requested:
${targetUrl}`);
+ }
+
const proxyUrl = new URL(`protocol://${targetUrl.substring(1)}`);
const protocol = this.args.hostsToUseHttp.includes(proxyUrl.host) ? "http"
: "https";
const proxyUrlString = targetUrl.startsWith("/") ?
`${protocol}:/${targetUrl}` : undefined;
diff --git a/packages/cors-proxy/src/proxy/server.ts
b/packages/cors-proxy/src/proxy/server.ts
index cb71c6e9cdb..66c79397ade 100644
--- a/packages/cors-proxy/src/proxy/server.ts
+++ b/packages/cors-proxy/src/proxy/server.ts
@@ -27,6 +27,7 @@ export type ServerArgs = {
port: number;
verbose: boolean;
hostsToUseHttp: string[];
+ allowedHosts: string[];
};
export const startServer = (args: ServerArgs): void => {
@@ -36,6 +37,7 @@ export const startServer = (args: ServerArgs): void => {
console.log(`Port: ${args.port}`);
console.log(`Verbose: ${args.verbose}`);
console.log(`Hosts to proxy with HTTP: ${args.hostsToUseHttp}`);
+ console.log(`Allow hosts: ${args.allowedHosts}`);
console.log("====================================================");
const app: express.Express = express();
diff --git a/packages/cors-proxy/tests/ExpressCorsProxy.test.ts
b/packages/cors-proxy/tests/ExpressCorsProxy.test.ts
new file mode 100644
index 00000000000..73352f7504e
--- /dev/null
+++ b/packages/cors-proxy/tests/ExpressCorsProxy.test.ts
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const mockFetch = jest.fn();
+jest.mock("node-fetch", () => ({
+ __esModule: true,
+ default: mockFetch,
+}));
+
+import { ExpressCorsProxy } from "../src/proxy/ExpressCorsProxy";
+import { Request, Response } from "express";
+
+const getProxy = (allowedHosts: string[]) =>
+ new ExpressCorsProxy({
+ allowedOrigins: ["http://example.com"],
+ verbose: false,
+ hostsToUseHttp: [],
+ allowedHosts,
+ });
+
+const getMockFetchResponse = () =>
+ Promise.resolve({
+ status: 200,
+ headers: new Map([["content-type", "application/json"]]),
+ });
+
+const getMockResponse = (): Response =>
+ ({
+ header: () => {},
+ setHeader: () => {},
+ getHeaders: () => {},
+ status: () => {},
+ end: () => {},
+ write: () => {},
+ on: () => {},
+ once: () => {},
+ emit: () => {},
+ }) as any as Response;
+
+const getMockGetRequest = (url: string) =>
+ ({
+ header: (header: string) => {
+ if (header === "origin") {
+ return "http://example.com";
+ }
+ },
+ headers: {
+ origin: "http://example.com",
+ },
+ url,
+ method: "GET",
+ }) as any as Request;
+
+const getMockPostRequest = (targetUrl: string) =>
+ ({
+ header: (header: string) => {
+ if (header === "origin") {
+ return "http://example.com";
+ }
+ },
+ headers: {
+ origin: "http://example.com",
+ "target-url": targetUrl,
+ },
+ method: "POST",
+ }) as any as Request;
+
+describe("ExpressCorsProxy allowed hosts functionality", () => {
+ const mockWarn = (console.warn = jest.fn());
+ const mockNext = jest.fn();
+ const allowedRequests = [
+ ["http://", "localhost:8081", "localhost,*.github.com"],
+ ["https://", "localhost:4000", "localhost,*.github.com"],
+ ["http://", "gist.github.com", "localhost,*.github.com"],
+ ["http://", "www.github.com", "localhost,*.github.com"],
+ ["https://", "www.github.com", "localhost,*.github.com"],
+ ["http://", "test.target.example.com",
"*.target.example.com,*.github.com"],
+ ["http://", "test.target.example.com:9000",
"*.target.example.com,*.github.com"],
+ [
+ "http://",
+ "test.target.example.com:9000/path/to/something?search=test&sortBy=name",
+ "*.target.example.com,*.github.com",
+ ],
+ ["https://", "test.target.example.com",
"*.target.example.com,*.github.com"],
+ ["https://", "test.target.example.com/graphql",
"*.target.example.com,*.github.com"],
+ ["https://", "test.target.example.com/openapi",
"*.target.example.com,*.github.com"],
+ ["http://", "gist.github.com", "*.target.example.com,*.github.com"],
+ ["http://", "www.github.com", "*.target.example.com,*.github.com"],
+ ["https://", "www.github.com", "*.target.example.com,*.github.com"],
+ [
+ "https://",
+ "api.aaa.bbb.p1.openshiftapps.com:6443/version",
+ "*.target.example.com,*.github.com,*.openshiftapps.com",
+ ],
+ ];
+ const deniedRequests = [
+ ["http://", "www.example.com"],
+ ["http://", "www.example.com/graphql"],
+ ["http://", "api.notvalid.com"],
+ ["http://", "api.notvalid.com/openapi.json"],
+ ["http://", "notvalid.com"],
+ ["http://", "api.notvalid.com/www.github.com"],
+ ];
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ mockFetch.mockImplementation(getMockFetchResponse);
+ });
+
+ describe("Test GET requests", () => {
+ it.each(allowedRequests)(
+ `should allow GET requests to %s%s with allowed hosts: %s`,
+ async (protocol, targetUrl, allowedHosts) => {
+ const proxy = getProxy(allowedHosts.split(","));
+
+ await proxy.handle(getMockGetRequest(`/${targetUrl}`),
getMockResponse(), mockNext);
+
+ expect(mockWarn).not.toHaveBeenCalled();
+ expect(mockFetch).toHaveBeenCalledWith(
+ new URL(protocol + targetUrl),
+ expect.objectContaining({ method: "GET" })
+ );
+ expect(mockNext).not.toHaveBeenCalled();
+ }
+ );
+
+ it.each(deniedRequests)(
+ "should not allow requests to %s%s with allowed hosts:
*.target.example.com,*.github.com",
+ async (protocol, targetUrl) => {
+ const proxy = getProxy(["*.target.example.com", "*.github.com"]);
+
+ await proxy.handle(getMockGetRequest(`/${targetUrl}`),
getMockResponse(), mockNext);
+
+ expect(mockWarn).toHaveBeenCalledWith(expect.any(String),
expect.stringContaining("not allowed"));
+ expect(mockFetch).not.toHaveBeenCalled();
+ expect(mockNext).toHaveBeenCalled();
+ }
+ );
+ });
+
+ describe("Test POST requests", () => {
+ it.each(allowedRequests)(
+ `should allow POST requests to %s%s with allowed hosts: %s`,
+ async (protocol, targetUrl, allowedHosts) => {
+ const proxy = getProxy(allowedHosts.split(","));
+
+ await proxy.handle(getMockPostRequest(protocol + targetUrl),
getMockResponse(), mockNext);
+
+ expect(mockWarn).not.toHaveBeenCalled();
+ expect(mockFetch).toHaveBeenCalledWith(
+ new URL(protocol + targetUrl),
+ expect.objectContaining({ method: "POST" })
+ );
+ expect(mockNext).not.toHaveBeenCalled();
+ }
+ );
+
+ it.each(deniedRequests)(
+ "should not allow requests to %s%s with allowed hosts:
*.target.example.com,*.github.com",
+ async (protocol, targetUrl) => {
+ const proxy = getProxy(["*.target.example.com", "*.github.com"]);
+
+ await proxy.handle(getMockPostRequest(protocol + targetUrl),
getMockResponse(), mockNext);
+
+ expect(mockWarn).toHaveBeenCalledWith(expect.any(String),
expect.stringContaining("not allowed"));
+ expect(mockFetch).not.toHaveBeenCalled();
+ expect(mockNext).toHaveBeenCalled();
+ }
+ );
+ });
+});
diff --git a/packages/cors-proxy/tests/index.test.ts
b/packages/cors-proxy/tests/index.test.ts
index 393a5601236..aa20bf3f0cb 100644
--- a/packages/cors-proxy/tests/index.test.ts
+++ b/packages/cors-proxy/tests/index.test.ts
@@ -41,6 +41,20 @@ describe("index.ts test", () => {
jest.resetAllMocks();
});
+ it("Default values", () => {
+ setEnv({});
+
+ run();
+ expect(startServer).toHaveBeenCalledWith(
+ expect.objectContaining({
+ port: 8080,
+ verbose: false,
+ allowedHosts: ["localhost", "*.github.com"],
+ allowedOrigins: ["http://localhost:9001"],
+ })
+ );
+ });
+
it("Custom port", () => {
setEnv({
CORS_PROXY_HTTP_PORT: "90",
@@ -53,6 +67,7 @@ describe("index.ts test", () => {
port: 90,
allowedOrigins: ["http://example.com"],
verbose: false,
+ allowedHosts: ["localhost", "*.github.com"],
})
);
});
@@ -68,10 +83,29 @@ describe("index.ts test", () => {
port: 8080,
allowedOrigins: ["http://example.com"],
verbose: true,
+ allowedHosts: ["localhost", "*.github.com"],
})
);
});
+ describe("Allowed hosts configuration", () => {
+ it.each(["*.target.example.com,*.github.com", "*"])("Custom allow hosts
with: %s", (allowedHosts) => {
+ setEnv({
+ CORS_PROXY_ALLOWED_ORIGINS: "http://example.com",
+ CORS_PROXY_ALLOWED_HOSTS: allowedHosts,
+ });
+ run();
+ expect(startServer).toHaveBeenCalledWith(
+ expect.objectContaining({
+ port: 8080,
+ verbose: false,
+ allowedHosts: allowedHosts.split(","),
+ allowedOrigins: ["http://example.com"],
+ })
+ );
+ });
+ });
+
describe("Allowed origins configuration", () => {
it("Single allowed origin", () => {
setEnv({
@@ -84,6 +118,7 @@ describe("index.ts test", () => {
port: 8080,
allowedOrigins: ["http://example.com"],
verbose: false,
+ allowedHosts: ["localhost", "*.github.com"],
})
);
});
@@ -99,6 +134,7 @@ describe("index.ts test", () => {
port: 8080,
allowedOrigins: ["http://example.com", "https://other.example.com",
"http://localhost:9001"],
verbose: false,
+ allowedHosts: ["localhost", "*.github.com"],
})
);
});
@@ -121,15 +157,18 @@ describe("index.ts test", () => {
}).toThrow(new Error("Invalid origin: empty origins are not allowed in
CORS_PROXY_ALLOWED_ORIGINS."));
});
- it("Should throw an error when wildcard '*' is in the list", () => {
- setEnv({
- CORS_PROXY_ALLOWED_ORIGINS: "http://example.com,*",
- });
-
- expect(() => {
- run();
- }).toThrow(new Error('Invalid origin: wildcard "*" is not allowed in
CORS_PROXY_ALLOWED_ORIGINS.'));
- });
+ it.each(["*,http://example.com", "http://example.com,*", "*"])(
+ "Should throw an error when wildcard '*' is in the list with
CORS_PROXY_ALLOWED_ORIGINS: %s",
+ (allowedOrigins) => {
+ setEnv({
+ CORS_PROXY_ALLOWED_ORIGINS: allowedOrigins,
+ });
+
+ expect(() => {
+ run();
+ }).toThrow(new Error('Invalid origin: wildcard "*" is not allowed in
CORS_PROXY_ALLOWED_ORIGINS.'));
+ }
+ );
it("Should throw an error when there are empty origins in the list", () =>
{
setEnv({
diff --git a/packages/cors-proxy/tests/server.test.ts
b/packages/cors-proxy/tests/server.test.ts
index 77b35c3cd9c..5678134ede1 100644
--- a/packages/cors-proxy/tests/server.test.ts
+++ b/packages/cors-proxy/tests/server.test.ts
@@ -45,6 +45,7 @@ describe("CORS handler logic test", () => {
port: 8080,
verbose: false,
hostsToUseHttp: [],
+ allowedHosts: ["localhost"],
};
startServer(args);
@@ -77,6 +78,7 @@ describe("CORS handler logic test", () => {
port: 8080,
verbose: false,
hostsToUseHttp: [],
+ allowedHosts: ["localhost"],
};
startServer(args);
@@ -113,6 +115,7 @@ describe("CORS handler logic test", () => {
port: 8080,
verbose: false,
hostsToUseHttp: [],
+ allowedHosts: ["localhost"],
};
startServer(args);
@@ -146,6 +149,40 @@ describe("CORS handler logic test", () => {
port: 8080,
verbose: false,
hostsToUseHttp: [],
+ allowedHosts: ["localhost"],
+ };
+
+ startServer(args);
+
+ const req: any = {
+ headers: {
+ origin: "http://example.com",
+ },
+ method: "GET",
+ };
+
+ const res: any = {
+ statusCode: 200,
+ setHeader: jest.fn(),
+ getHeader: jest.fn(),
+ };
+
+ const next = function () {
+
expect(res.setHeader).toHaveBeenCalledWith("Access-Control-Allow-Origin",
"http://example.com");
+ done();
+ };
+
+ const corsMiddleware = corsMock.mock.results[0].value;
+ corsMiddleware(req, res, next);
+ });
+
+ it("should not allow requests with origin http://notvalid:9000", (done) =>
{
+ const args: ServerArgs = {
+ allowedOrigins: ["http://localhost:9000"],
+ port: 8080,
+ verbose: false,
+ hostsToUseHttp: [],
+ allowedHosts: ["localhost"],
};
startServer(args);
diff --git a/packages/online-editor/package.json
b/packages/online-editor/package.json
index 7bdc0d5a7ec..2443c665040 100644
--- a/packages/online-editor/package.json
+++ b/packages/online-editor/package.json
@@ -18,8 +18,8 @@
"build:dev": "rimraf dist && webpack --config webpack.config.ts --env dev",
"build:prod": "pnpm lint && pnpm test && rimraf dist && webpack && pnpm
test-e2e",
"lint": "run-script-if --bool \"$(build-env linters.run)\" --then
\"kie-tools--eslint ./src\"",
- "start": "concurrently 'pnpm start:cors-proxy' 'pnpm
start:extended-services' 'pnpm start:kie-sandbox' 'pnpm
start:kie-sandbox-accelerator-quarkus'",
- "start:cors-proxy": "cross-env
CORS_PROXY__origin=https://localhost:$(build-env onlineEditor.dev.port)
CORS_PROXY__useHttpForHosts=\"localhost:$(build-env
kieSandboxAcceleratorQuarkus.dev.port)\" npm --prefix
./node_modules/@kie-tools/cors-proxy run start",
+ "start": "concurrently --no-colors 'pnpm start:cors-proxy' 'pnpm
start:extended-services' 'pnpm start:kie-sandbox' 'pnpm
start:kie-sandbox-accelerator-quarkus'",
+ "start:cors-proxy": "cross-env
CORS_PROXY__allowedOrigins=http://localhost:$(build-env onlineEditor.dev.port)
CORS_PROXY__useHttpForHosts=\"localhost:$(build-env
kieSandboxAcceleratorQuarkus.dev.port) CORS_PROXY__allowedHosts=*\" npm
--prefix ./node_modules/@kie-tools/cors-proxy run start",
"start:extended-services": "cross-env
EXTENDED_SERVICES_JAVA__host=\"0.0.0.0\" npm --prefix
./node_modules/@kie-tools/extended-services-java run start",
"start:kie-sandbox": "cross-env EXTENDED_SERVICES_JAVA__host=\"0.0.0.0\"
webpack serve --host 0.0.0.0 --env dev",
"start:kie-sandbox-accelerator-quarkus": "npm --prefix
./node_modules/@kie-tools/kie-sandbox-accelerator-quarkus run start",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 77004101ddb..2d13f2056bd 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1898,6 +1898,9 @@ importers:
https-proxy-agent:
specifier: ^7.0.6
version: 7.0.6
+ minimatch:
+ specifier: ^3.0.5
+ version: 3.1.2
node-fetch:
specifier: ^3.3.2
version: 3.3.2
@@ -1935,6 +1938,9 @@ importers:
'@types/jest':
specifier: ^29.5.12
version: 29.5.12
+ '@types/minimatch':
+ specifier: ^3.0.5
+ version: 3.0.5
'@types/node':
specifier: ^22.14.1
version: 22.18.8
@@ -60236,7 +60242,7 @@ snapshots:
watchpack: 2.4.2
webpack-sources: 3.2.3
optionalDependencies:
- webpack-cli: 4.10.0([email protected])([email protected])
+ webpack-cli: 4.10.0([email protected])
transitivePeerDependencies:
- '@swc/core'
- esbuild
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]