[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.MONITOR_TYPE]: DataStream.BROWSER,
[ConfigKey.PARAMS]: '', [ConfigKey.PARAMS]: '',
[ConfigKey.PORT]: undefined, [ConfigKey.PORT]: null,
[ConfigKey.SCHEDULE]: { [ConfigKey.SCHEDULE]: {
unit: ScheduleUnit.MINUTES, unit: ScheduleUnit.MINUTES,
number: '10', number: '10',

View file

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

View file

@ -9,7 +9,7 @@ import React, { memo, useMemo, useCallback } from 'react';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import { EuiFormRow } from '@elastic/eui'; import { EuiFormRow } from '@elastic/eui';
import { Validation } from '../types'; import { Validation } from '../types';
import { ConfigKey } from '../types'; import { ConfigKey, MonitorFields } from '../types';
import { useBrowserSimpleFieldsContext } from '../contexts'; import { useBrowserSimpleFieldsContext } from '../contexts';
import { ScheduleField } from '../schedule_field'; import { ScheduleField } from '../schedule_field';
import { SourceField } from './source_field'; import { SourceField } from './source_field';
@ -76,7 +76,7 @@ export const BrowserSimpleFields = memo<Props>(({ validate, onFieldBlur }) => {
defaultMessage="Frequency" defaultMessage="Frequency"
/> />
} }
isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields)} isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields as Partial<MonitorFields>)}
error={ error={
<FormattedMessage <FormattedMessage
id="xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.monitorInterval.error" 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 { validate } from '../validation';
import { import {
ConfigKey, ConfigKey,
MonitorFields,
DataStream, DataStream,
TLSVersion, TLSVersion,
CommonFields, CommonFields,
@ -602,8 +603,8 @@ describe('useBarChartsHooks', () => {
it('handles browser data stream', async () => { it('handles browser data stream', async () => {
const onChange = jest.fn(); const onChange = jest.fn();
const initialProps = { const initialProps = {
defaultConfig: defaultConfig[DataStream.BROWSER], defaultConfig: defaultConfig[DataStream.BROWSER] as Partial<MonitorFields>,
config: defaultConfig[DataStream.BROWSER], config: defaultConfig[DataStream.BROWSER] as Partial<MonitorFields>,
newPolicy, newPolicy,
onChange, onChange,
validate, validate,
@ -637,7 +638,7 @@ describe('useBarChartsHooks', () => {
rerender({ rerender({
...initialProps, ...initialProps,
config, config: config as Partial<MonitorFields>,
}); });
await waitFor(() => { await waitFor(() => {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -439,8 +439,8 @@ export class SyntheticsService {
} }
formatConfigs(configs: SyntheticsMonitorWithId[]) { formatConfigs(configs: SyntheticsMonitorWithId[]) {
return configs.map((config: Partial<MonitorFields>) => return configs.map((config: SyntheticsMonitor) =>
formatMonitorConfig(Object.keys(config) as ConfigKey[], config) 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); 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": [], "locations": [],
"name": "Test HTTP Monitor 03", "name": "Test HTTP Monitor 03",
"namespace": "testnamespace", "namespace": "testnamespace",
"origin": "ui" "origin": "ui",
"urls": null,
"url.port": null
} }