forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Whitelist email server in built-in email server action - second try (e…
…lastic#52221) resolves elastic#50721 note this branch was previously merged into master and then reverted: elastic#51489 (prior PR made shape changes this one didn't take into account) Uses the same whitelist config value / utilities that the webhook action already uses. Was already mentioned in the README doc that email uses this whitelist config value :-) Required a change to the functional tests to use a host already whitelisted in config, made for the the webhook action tests. Also realized some jest tests on email were bogus, so fixed those (was passing `user` in config, which is invalid, and masking the actual thing being tested).
- Loading branch information
Showing
5 changed files
with
239 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,15 +8,30 @@ jest.mock('./lib/send_email', () => ({ | |
sendEmail: jest.fn(), | ||
})); | ||
|
||
import { Logger } from '../../../../../../src/core/server'; | ||
import { savedObjectsClientMock } from '../../../../../../src/core/server/mocks'; | ||
|
||
import { ActionType, ActionTypeExecutorOptions } from '../types'; | ||
import { ActionsConfigurationUtilities } from '../actions_config'; | ||
import { validateConfig, validateSecrets, validateParams } from '../lib'; | ||
import { savedObjectsClientMock } from '../../../../../../src/core/server/mocks'; | ||
import { createActionTypeRegistry } from './index.test'; | ||
import { sendEmail } from './lib/send_email'; | ||
import { ActionParamsType, ActionTypeConfigType, ActionTypeSecretsType } from './email'; | ||
import { | ||
ActionParamsType, | ||
ActionTypeConfigType, | ||
ActionTypeSecretsType, | ||
getActionType, | ||
} from './email'; | ||
|
||
const sendEmailMock = sendEmail as jest.Mock; | ||
|
||
const configUtilsMock: ActionsConfigurationUtilities = { | ||
isWhitelistedHostname: _ => true, | ||
isWhitelistedUri: _ => true, | ||
ensureWhitelistedHostname: _ => {}, | ||
ensureWhitelistedUri: _ => {}, | ||
}; | ||
|
||
const ACTION_TYPE_ID = '.email'; | ||
const NO_OP_FN = () => {}; | ||
|
||
|
@@ -27,6 +42,7 @@ const services = { | |
}; | ||
|
||
let actionType: ActionType; | ||
let mockedLogger: jest.Mocked<Logger>; | ||
|
||
beforeAll(() => { | ||
const { actionTypeRegistry } = createActionTypeRegistry(); | ||
|
@@ -69,8 +85,6 @@ describe('config validation', () => { | |
|
||
test('config validation fails when config is not valid', () => { | ||
const baseConfig: Record<string, any> = { | ||
user: 'bob', | ||
password: 'supersecret', | ||
from: '[email protected]', | ||
}; | ||
|
||
|
@@ -85,21 +99,21 @@ describe('config validation', () => { | |
expect(() => { | ||
validateConfig(actionType, baseConfig); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"error validating action type config: [user]: definition for this key is missing"` | ||
`"error validating action type config: either [service] or [host]/[port] is required"` | ||
); | ||
|
||
// host but no port | ||
expect(() => { | ||
validateConfig(actionType, { ...baseConfig, host: 'elastic.co' }); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"error validating action type config: [user]: definition for this key is missing"` | ||
`"error validating action type config: [port] is required if [service] is not provided"` | ||
); | ||
|
||
// port but no host | ||
expect(() => { | ||
validateConfig(actionType, { ...baseConfig, port: 8080 }); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"error validating action type config: [user]: definition for this key is missing"` | ||
`"error validating action type config: [host] is required if [service] is not provided"` | ||
); | ||
|
||
// invalid service | ||
|
@@ -109,7 +123,64 @@ describe('config validation', () => { | |
service: 'bad-nodemailer-service', | ||
}); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"error validating action type config: [user]: definition for this key is missing"` | ||
`"error validating action type config: [service] value 'bad-nodemailer-service' is not valid"` | ||
); | ||
}); | ||
|
||
// nodemailer supports a service named 'AOL' that maps to the host below | ||
const NODEMAILER_AOL_SERVICE = 'AOL'; | ||
const NODEMAILER_AOL_SERVICE_HOST = 'smtp.aol.com'; | ||
|
||
test('config validation handles email host whitelisting', () => { | ||
actionType = getActionType({ | ||
logger: mockedLogger, | ||
configurationUtilities: { | ||
...configUtilsMock, | ||
isWhitelistedHostname: hostname => hostname === NODEMAILER_AOL_SERVICE_HOST, | ||
}, | ||
}); | ||
const baseConfig = { | ||
from: '[email protected]', | ||
}; | ||
const whitelistedConfig1 = { | ||
...baseConfig, | ||
service: NODEMAILER_AOL_SERVICE, | ||
}; | ||
const whitelistedConfig2 = { | ||
...baseConfig, | ||
host: NODEMAILER_AOL_SERVICE_HOST, | ||
port: 42, | ||
}; | ||
const notWhitelistedConfig1 = { | ||
...baseConfig, | ||
service: 'gmail', | ||
}; | ||
|
||
const notWhitelistedConfig2 = { | ||
...baseConfig, | ||
host: 'smtp.gmail.com', | ||
port: 42, | ||
}; | ||
|
||
const validatedConfig1 = validateConfig(actionType, whitelistedConfig1); | ||
expect(validatedConfig1.service).toEqual(whitelistedConfig1.service); | ||
expect(validatedConfig1.from).toEqual(whitelistedConfig1.from); | ||
|
||
const validatedConfig2 = validateConfig(actionType, whitelistedConfig2); | ||
expect(validatedConfig2.host).toEqual(whitelistedConfig2.host); | ||
expect(validatedConfig2.port).toEqual(whitelistedConfig2.port); | ||
expect(validatedConfig2.from).toEqual(whitelistedConfig2.from); | ||
|
||
expect(() => { | ||
validateConfig(actionType, notWhitelistedConfig1); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"error validating action type config: [service] value 'gmail' resolves to host 'smtp.gmail.com' which is not in the whitelistedHosts configuration"` | ||
); | ||
|
||
expect(() => { | ||
validateConfig(actionType, notWhitelistedConfig2); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"error validating action type config: [host] value 'smtp.gmail.com' is not in the whitelistedHosts configuration"` | ||
); | ||
}); | ||
}); | ||
|
@@ -140,16 +211,16 @@ describe('params validation', () => { | |
message: 'this is the message', | ||
}; | ||
expect(validateParams(actionType, params)).toMatchInlineSnapshot(` | ||
Object { | ||
"bcc": Array [], | ||
"cc": Array [], | ||
"message": "this is the message", | ||
"subject": "this is a test", | ||
"to": Array [ | ||
"[email protected]", | ||
], | ||
} | ||
`); | ||
Object { | ||
"bcc": Array [], | ||
"cc": Array [], | ||
"message": "this is the message", | ||
"subject": "this is a test", | ||
"to": Array [ | ||
"[email protected]", | ||
], | ||
} | ||
`); | ||
}); | ||
|
||
test('params validation fails when params is not valid', () => { | ||
|
@@ -194,29 +265,29 @@ describe('execute()', () => { | |
sendEmailMock.mockReset(); | ||
await actionType.executor(executorOptions); | ||
expect(sendEmailMock.mock.calls[0][1]).toMatchInlineSnapshot(` | ||
Object { | ||
"content": Object { | ||
"message": "a message to you", | ||
"subject": "the subject", | ||
}, | ||
"routing": Object { | ||
"bcc": Array [ | ||
"[email protected]", | ||
], | ||
"cc": Array [ | ||
"[email protected]", | ||
], | ||
"from": "[email protected]", | ||
"to": Array [ | ||
"[email protected]", | ||
], | ||
}, | ||
"transport": Object { | ||
"password": "supersecret", | ||
"service": "__json", | ||
"user": "bob", | ||
}, | ||
} | ||
`); | ||
Object { | ||
"content": Object { | ||
"message": "a message to you", | ||
"subject": "the subject", | ||
}, | ||
"routing": Object { | ||
"bcc": Array [ | ||
"[email protected]", | ||
], | ||
"cc": Array [ | ||
"[email protected]", | ||
], | ||
"from": "[email protected]", | ||
"to": Array [ | ||
"[email protected]", | ||
], | ||
}, | ||
"transport": Object { | ||
"password": "supersecret", | ||
"service": "__json", | ||
"user": "bob", | ||
}, | ||
} | ||
`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.