Validate server UUID retrieved from data/uuid using the same match from config validation (#123680)

Exports the uuid regexp used in to validate configuration for use in validating the data/uuid file. Also includes a `trim()` to allow newlines that could have been edited automatically via file editing or configuration management.
This commit is contained in:
Mat Schaffer 2022-01-26 01:00:51 +09:00 committed by GitHub
parent 2eccc81a45
commit f021f75aea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 47 additions and 17 deletions

View file

@ -22,8 +22,8 @@ jest.mock('./fs', () => ({
writeFile: jest.fn(() => Promise.resolve('')),
}));
const DEFAULT_FILE_UUID = 'FILE_UUID';
const DEFAULT_CONFIG_UUID = 'CONFIG_UUID';
const DEFAULT_FILE_UUID = 'ffffffff-bbbb-0ccc-0ddd-eeeeeeeeeeee';
const DEFAULT_CONFIG_UUID = 'cccccccc-bbbb-0ccc-0ddd-eeeeeeeeeeee';
const fileNotFoundError = { code: 'ENOENT' };
const permissionError = { code: 'EACCES' };
const isDirectoryError = { code: 'EISDIR' };
@ -91,7 +91,7 @@ describe('resolveInstanceUuid', () => {
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Updating Kibana instance UUID to: CONFIG_UUID (was: FILE_UUID)",
"Updating Kibana instance UUID to: cccccccc-bbbb-0ccc-0ddd-eeeeeeeeeeee (was: ffffffff-bbbb-0ccc-0ddd-eeeeeeeeeeee)",
]
`);
});
@ -106,7 +106,7 @@ describe('resolveInstanceUuid', () => {
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Kibana instance UUID: CONFIG_UUID",
"Kibana instance UUID: cccccccc-bbbb-0ccc-0ddd-eeeeeeeeeeee",
]
`);
});
@ -126,25 +126,45 @@ describe('resolveInstanceUuid', () => {
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Setting new Kibana instance UUID: CONFIG_UUID",
"Setting new Kibana instance UUID: cccccccc-bbbb-0ccc-0ddd-eeeeeeeeeeee",
]
`);
});
});
describe('when file is present and config property is not set', () => {
it('does not write to file and returns the file uuid', async () => {
beforeEach(() => {
serverConfig = createServerConfig(undefined);
});
it('does not write to file and returns the file uuid', async () => {
const uuid = await resolveInstanceUuid({ pathConfig, serverConfig, logger });
expect(uuid).toEqual(DEFAULT_FILE_UUID);
expect(writeFile).not.toHaveBeenCalled();
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Resuming persistent Kibana instance UUID: FILE_UUID",
"Resuming persistent Kibana instance UUID: ffffffff-bbbb-0ccc-0ddd-eeeeeeeeeeee",
]
`);
});
describe('when file contains an invalid uuid', () => {
it('throws an explicit error for uuid formatting', async () => {
mockReadFile({ uuid: 'invalid uuid in data file' });
await expect(
resolveInstanceUuid({ pathConfig, serverConfig, logger })
).rejects.toThrowErrorMatchingInlineSnapshot(`"data-folder/uuid contains an invalid UUID"`);
});
});
describe('when file contains a trailing new line', () => {
it('returns the trimmed file uuid', async () => {
mockReadFile({ uuid: DEFAULT_FILE_UUID + '\n' });
const uuid = await resolveInstanceUuid({ pathConfig, serverConfig, logger });
expect(uuid).toEqual(DEFAULT_FILE_UUID);
});
});
});
describe('when file is present with 7.6.0 UUID', () => {
@ -193,7 +213,7 @@ describe('resolveInstanceUuid', () => {
"UUID from 7.6.0 bug detected, ignoring file UUID",
],
Array [
"Setting new Kibana instance UUID: CONFIG_UUID",
"Setting new Kibana instance UUID: cccccccc-bbbb-0ccc-0ddd-eeeeeeeeeeee",
],
]
`);

View file

@ -12,6 +12,7 @@ import { PathConfigType } from '@kbn/utils';
import { readFile, writeFile } from './fs';
import { HttpConfigType } from '../http';
import { Logger } from '../logging';
import { uuidRegexp } from '../http/http_config';
const FILE_ENCODING = 'utf8';
const FILE_NAME = 'uuid';
@ -63,16 +64,24 @@ export async function resolveInstanceUuid({
}
async function readUuidFromFile(filepath: string, logger: Logger): Promise<string | undefined> {
const content = await readFileContent(filepath);
if (content === UUID_7_6_0_BUG) {
logger.debug(`UUID from 7.6.0 bug detected, ignoring file UUID`);
return undefined;
}
if (content && !content.match(uuidRegexp)) {
throw new Error(`${filepath} contains an invalid UUID`);
}
return content;
}
async function readFileContent(filepath: string): Promise<string | undefined> {
try {
const content = await readFile(filepath);
const decoded = content.toString(FILE_ENCODING);
if (decoded === UUID_7_6_0_BUG) {
logger.debug(`UUID from 7.6.0 bug detected, ignoring file UUID`);
return undefined;
} else {
return decoded;
}
return content.toString(FILE_ENCODING).trim();
} catch (e) {
if (e.code === 'ENOENT') {
// non-existent uuid file is ok, we will create it.

View file

@ -21,7 +21,8 @@ import {
} from './security_response_headers_config';
const validBasePathRegex = /^\/.*[^\/]$/;
const uuidRegexp = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
export const uuidRegexp =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
const hostURISchema = schema.uri({ scheme: ['http', 'https'] });
const match = (regex: RegExp, errorMsg: string) => (str: string) =>
regex.test(str) ? undefined : errorMsg;