1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-20 00:08:02 +01:00

fix: reject unauthorized client requests (#3881)

If apiTokens are enabled breaks middleware chain with a 401 if no token
is found for requests to client and frontend apis. Previously the
middleware allowed the chain to process.

Removes the regex search for multiple slashes, and instead configures
the apiTokenMiddleware to reject unauthorized requests.
This commit is contained in:
Christopher Kolstad 2023-05-27 16:29:54 +02:00 committed by GitHub
parent 3d872cf7a2
commit 52904ee038
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 20 additions and 27 deletions

View File

@ -29,7 +29,6 @@ import maintenanceMiddleware from './middleware/maintenance-middleware';
import { unless } from './middleware/unless-middleware';
import { catchAllErrorHandler } from './middleware/catch-all-error-handler';
import NotFoundError from './error/notfound-error';
import { rejectDoubleSlashesInPath } from './middleware/reject-double-slashes-in-path';
export default async function getApp(
config: IUnleashConfig,
@ -93,7 +92,6 @@ export default async function getApp(
if (config.enableOAS && services.openApiService) {
services.openApiService.useDocs(app);
}
app.use(`${baseUriPath}/`, rejectDoubleSlashesInPath);
// Support CORS preflight requests for the frontend endpoints.
// Preflight requests should not have Authorization headers,
// so this must be handled before the API token middleware.

View File

@ -4,7 +4,7 @@ import { IUnleashConfig } from '../types/option';
import { IAuthRequest } from '../routes/unleash-types';
const isClientApi = ({ path }) => {
return path && path.startsWith('/api/client');
return path && path.indexOf('/api/client') > -1;
};
const isProxyApi = ({ path }) => {
@ -15,16 +15,18 @@ const isProxyApi = ({ path }) => {
// Handle all our current proxy paths which will redirect to the new
// embedded proxy endpoint
return (
path.startsWith('/api/proxy') ||
path.startsWith('/api/development/proxy') ||
path.startsWith('/api/production/proxy') ||
path.startsWith('/api/frontend')
path.indexOf('/api/proxy') > -1 ||
path.indexOf('/api/development/proxy') > -1 ||
path.indexOf('/api/production/proxy') > -1 ||
path.indexOf('/api/frontend') > -1
);
};
export const TOKEN_TYPE_ERROR_MESSAGE =
'invalid token: expected a different token type for this endpoint';
export const NO_TOKEN_WHERE_TOKEN_WAS_REQUIRED =
'This endpoint requires an API token. Please add an authorization header to your request with a valid token';
const apiAccessMiddleware = (
{
getLogger,
@ -64,6 +66,14 @@ const apiAccessMiddleware = (
return;
}
req.user = apiUser;
} else if (isClientApi(req) || isProxyApi(req)) {
// If we're here, we know that api token middleware was enabled, otherwise we'd returned a no-op middleware
// We explicitly only protect client and proxy apis, since admin apis are protected by our permission checker
// Reject with 401
res.status(401).send({
message: NO_TOKEN_WHERE_TOKEN_WAS_REQUIRED,
});
return;
}
}
} catch (error) {

View File

@ -1,11 +0,0 @@
import { RequestHandler } from 'express';
const MULTIPLE_SLASHES = /\/\/+/;
export const rejectDoubleSlashesInPath: RequestHandler = (req, res, next) => {
if (req.path.match(MULTIPLE_SLASHES)) {
res.status(404).send();
} else {
next();
}
};

View File

@ -19,7 +19,7 @@ beforeAll(async () => {
authentication: { enableApiToken: true, type: IAuthType.DEMO },
});
appWithBaseUrl = await setupAppWithAuth(stores, {
server: { baseUriPath: '/demo' },
server: { unleashUrl: 'http://localhost:4242', basePathUri: '/demo' },
authentication: { enableApiToken: true, type: IAuthType.DEMO },
});
});
@ -31,17 +31,13 @@ afterAll(async () => {
test('Access to /api/client/features are refused no matter how many leading slashes', async () => {
await app.request.get('/api/client/features').expect(401);
await app.request.get('/////api/client/features').expect(404);
await app.request.get('//api/client/features').expect(404);
});
test('Multiple slashes anywhere in the path is not a URL that exists', async () => {
await app.request.get('/api/admin///projects/default/features').expect(404);
await app.request.get('/api/client///features').expect(404);
await app.request.get('/////api/client/features').expect(401);
await app.request.get('//api/client/features').expect(401);
});
test('multiple slashes after base path is also rejected with 404', async () => {
await appWithBaseUrl.request.get('/demo///api/client/features').expect(404);
await appWithBaseUrl.request.get('/demo///api/client/features').expect(401);
await appWithBaseUrl.request.get('/demo//api/client/features').expect(401);
await appWithBaseUrl.request.get('/demo/api/client/features').expect(401);
});