[Synthetics] Ensure port or url is never undefined, to prevent project monitor decryption errors (#134867)

This commit is contained in:
Dominique Clarke 2022-06-22 02:54:24 -04:00 committed by GitHub
parent 8039218bf3
commit fabe146739
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 87 additions and 30 deletions

View file

@ -72,7 +72,7 @@ export const DEFAULT_BROWSER_SIMPLE_FIELDS: BrowserSimpleFields = {
},
[ConfigKey.MONITOR_TYPE]: DataStream.BROWSER,
[ConfigKey.PARAMS]: '',
[ConfigKey.PORT]: undefined,
[ConfigKey.PORT]: null,
[ConfigKey.SCHEDULE]: {
unit: ScheduleUnit.MINUTES,
number: '10',

View file

@ -229,8 +229,8 @@ export const BrowserSensitiveSimpleFieldsCodec = t.intersection([
[ConfigKey.SOURCE_ZIP_USERNAME]: t.string,
[ConfigKey.SOURCE_ZIP_PASSWORD]: t.string,
[ConfigKey.PARAMS]: t.string,
[ConfigKey.URLS]: t.union([t.string, t.undefined]),
[ConfigKey.PORT]: t.union([t.number, t.undefined]),
[ConfigKey.URLS]: t.union([t.string, t.null]),
[ConfigKey.PORT]: t.union([t.number, t.null]),
}),
ZipUrlTLSFieldsCodec,
CommonFieldsCodec,

View file

@ -9,7 +9,7 @@ import React, { memo, useMemo, useCallback } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiFormRow } from '@elastic/eui';
import { Validation } from '../types';
import { ConfigKey } from '../types';
import { ConfigKey, MonitorFields } from '../types';
import { useBrowserSimpleFieldsContext } from '../contexts';
import { ScheduleField } from '../schedule_field';
import { SourceField } from './source_field';
@ -76,7 +76,7 @@ export const BrowserSimpleFields = memo<Props>(({ validate, onFieldBlur }) => {
defaultMessage="Frequency"
/>
}
isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields)}
isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields as Partial<MonitorFields>)}
error={
<FormattedMessage
id="xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorInterval.error"

View file

@ -10,6 +10,7 @@ import { NewPackagePolicy } from '@kbn/fleet-plugin/public';
import { validate } from '../validation';
import {
ConfigKey,
MonitorFields,
DataStream,
TLSVersion,
CommonFields,
@ -602,8 +603,8 @@ describe('useBarChartsHooks', () => {
it('handles browser data stream', async () => {
const onChange = jest.fn();
const initialProps = {
defaultConfig: defaultConfig[DataStream.BROWSER],
config: defaultConfig[DataStream.BROWSER],
defaultConfig: defaultConfig[DataStream.BROWSER] as Partial<MonitorFields>,
config: defaultConfig[DataStream.BROWSER] as Partial<MonitorFields>,
newPolicy,
onChange,
validate,
@ -637,7 +638,7 @@ describe('useBarChartsHooks', () => {
rerender({
...initialProps,
config,
config: config as Partial<MonitorFields>,
});
await waitFor(() => {

View file

@ -8,8 +8,7 @@
import React, { memo, useEffect, useMemo } from 'react';
import { PackagePolicyCreateExtensionComponentProps } from '@kbn/fleet-plugin/public';
import { useTrackPageview } from '@kbn/observability-plugin/public';
import { DataStream } from './types';
import { PolicyConfig } from './types';
import { DataStream, PolicyConfig, MonitorFields } from './types';
import { usePolicyConfigContext } from './contexts';
import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults';
import { CustomFields } from './custom_fields';
@ -39,8 +38,8 @@ export const SyntheticsPolicyCreateExtension = memo<PackagePolicyCreateExtension
useUpdatePolicy({
monitorType,
defaultConfig: defaultConfig[monitorType],
config: policyConfig[monitorType],
defaultConfig: defaultConfig[monitorType] as Partial<MonitorFields>,
config: policyConfig[monitorType] as Partial<MonitorFields>,
newPolicy,
onChange,
validate,

View file

@ -35,7 +35,7 @@ export const SyntheticsPolicyEditExtension = memo<SyntheticsPolicyEditExtensionP
useUpdatePolicy({
defaultConfig,
config: policyConfig[monitorType],
config: policyConfig[monitorType] as Partial<MonitorFields>,
newPolicy,
onChange,
validate,

View file

@ -9,9 +9,9 @@ import {
ConfigKey,
DataStream,
HTTPFields,
BrowserFields,
MonitorFields,
ScheduleUnit,
SyntheticsMonitor,
} from '../../../../common/runtime_types';
import { validate } from './validation';
@ -53,18 +53,20 @@ describe('[Monitor Management] validation', () => {
[ConfigKey.SOURCE_INLINE, 'step(() => {});'],
[ConfigKey.SOURCE_ZIP_URL, 'https://test.zip'],
])('Browser', (configKey, value) => {
const browserProps: Partial<BrowserFields> = {
const browserProps = {
...commonPropsValid,
[ConfigKey.MONITOR_TYPE]: DataStream.BROWSER,
[ConfigKey.TIMEOUT]: null,
[ConfigKey.URLS]: null,
[ConfigKey.PORT]: null,
[configKey]: value,
};
} as SyntheticsMonitor;
it('should return false for all valid props', () => {
const validators = validate[DataStream.BROWSER];
const keysToValidate = [ConfigKey.SCHEDULE, ConfigKey.TIMEOUT, configKey];
const validatorFns = keysToValidate.map((key) => validators[key]);
const result = validatorFns.map((fn) => fn?.(browserProps) ?? true);
const result = validatorFns.map((fn) => fn?.(browserProps as Partial<MonitorFields>) ?? true);
expect(result).not.toEqual(expect.arrayContaining([true]));
});

View file

@ -41,8 +41,8 @@ export const MonitorConfig = ({ isEdit = false }: { isEdit: boolean }) => {
const { isValid, config } = useFormatMonitor({
monitorType,
validate,
config: policyConfig[monitorType],
defaultConfig: DEFAULT_FIELDS[monitorType],
config: policyConfig[monitorType] as Partial<MonitorFieldsType>,
defaultConfig: DEFAULT_FIELDS[monitorType] as Partial<MonitorFieldsType>,
});
const [hasBeenSubmitted, setHasBeenSubmitted] = useState(false);

View file

@ -9,10 +9,10 @@ import {
ConfigKey,
DataStream,
HTTPFields,
BrowserFields,
MonitorFields,
ScheduleUnit,
ServiceLocations,
SyntheticsMonitor,
} from '../../../../common/runtime_types';
import { validate, validateCommon } from './validation';
@ -96,18 +96,18 @@ describe('[Monitor Management] validation', () => {
[ConfigKey.SOURCE_INLINE, 'step(() => {});'],
[ConfigKey.SOURCE_ZIP_URL, 'https://test.zip'],
])('Browser', (configKey, value) => {
const browserProps: Partial<BrowserFields> = {
const browserProps = {
...commonPropsValid,
[ConfigKey.MONITOR_TYPE]: DataStream.BROWSER,
[ConfigKey.TIMEOUT]: undefined,
[configKey]: value,
};
} as SyntheticsMonitor;
it('should return false for all valid props', () => {
const validators = validate[DataStream.BROWSER];
const keysToValidate = [ConfigKey.SCHEDULE, ConfigKey.TIMEOUT, configKey];
const validatorFns = keysToValidate.map((key) => validators[key]);
const result = validatorFns.map((fn) => fn?.(browserProps) ?? true);
const result = validatorFns.map((fn) => fn?.(browserProps as Partial<MonitorFields>) ?? true);
expect(result).not.toEqual(expect.arrayContaining([true]));
});

View file

@ -173,8 +173,8 @@ describe('validateMonitor', () => {
[ConfigKey.SOURCE_ZIP_PASSWORD]: 'password',
[ConfigKey.SOURCE_ZIP_PROXY_URL]: 'http://proxy-url.com',
[ConfigKey.PARAMS]: '',
[ConfigKey.URLS]: undefined,
[ConfigKey.PORT]: undefined,
[ConfigKey.URLS]: null,
[ConfigKey.PORT]: null,
};
testBrowserAdvancedFields = {
@ -309,7 +309,7 @@ describe('validateMonitor', () => {
const testMonitor = {
...testHTTPFields,
...({
[ConfigKey.URLS]: undefined,
[ConfigKey.URLS]: null,
} as unknown as Partial<HTTPFields>),
} as MonitorFields;

View file

@ -70,7 +70,7 @@ export const hydrateSavedObjects = async ({
monitors
.filter((monitor) => missingInfoIds.includes(monitor.id))
.forEach((monitor) => {
let resultAttributes: Partial<SyntheticsMonitor> = monitor.attributes;
let resultAttributes: SyntheticsMonitor = monitor.attributes;
let isUpdated = false;

View file

@ -439,8 +439,8 @@ export class SyntheticsService {
}
formatConfigs(configs: SyntheticsMonitorWithId[]) {
return configs.map((config: Partial<MonitorFields>) =>
formatMonitorConfig(Object.keys(config) as ConfigKey[], config)
return configs.map((config: SyntheticsMonitor) =>
formatMonitorConfig(Object.keys(config) as ConfigKey[], config as Partial<MonitorFields>)
);
}

View file

@ -576,5 +576,58 @@ export default function ({ getService }: FtrProviderContext) {
await security.role.delete(roleName);
}
});
it('project monitors - is able to decrypt monitor when updated after hydration', async () => {
try {
await supertest
.put(API_URLS.SYNTHETICS_MONITORS_PROJECT)
.set('kbn-xsrf', 'true')
.send(projectMonitors);
const response = await supertest
.get(API_URLS.SYNTHETICS_MONITORS)
.query({
filter: `${syntheticsMonitorType}.attributes.journey_id: ${projectMonitors.monitors[0].id}`,
})
.set('kbn-xsrf', 'true')
.expect(200);
const { monitors } = response.body;
// add urls and ports to mimic hydration
const updates = {
[ConfigKey.URLS]: 'https://modified-host.com',
[ConfigKey.PORT]: 443,
};
const modifiedMonitor = { ...monitors[0]?.attributes, ...updates };
await supertest
.put(API_URLS.SYNTHETICS_MONITORS + '/' + monitors[0]?.id)
.set('kbn-xsrf', 'true')
.send(modifiedMonitor)
.expect(200);
// update project monitor via push api
const apiResponse = await supertest
.put(API_URLS.SYNTHETICS_MONITORS_PROJECT)
.set('kbn-xsrf', 'true')
.send(projectMonitors)
.expect(200);
expect(apiResponse.body.updatedMonitors).eql([projectMonitors.monitors[0].id]);
// ensure that monitor can still be decrypted
await supertest
.get(API_URLS.SYNTHETICS_MONITORS + '/' + monitors[0]?.id)
.set('kbn-xsrf', 'true')
.expect(200);
} finally {
await Promise.all([
projectMonitors.monitors.map((monitor) => {
return deleteMonitor(monitor.id, projectMonitors.project);
}),
]);
}
});
});
}

View file

@ -43,5 +43,7 @@
"locations": [],
"name": "Test HTTP Monitor 03",
"namespace": "testnamespace",
"origin": "ui"
"origin": "ui",
"urls": null,
"url.port": null
}