mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 03:01:21 -04:00
[Defend Workflows][Trusted Apps Advanced Mode][API] Updates the validator to use the advanced mode schema (#221980)
## Summary - [x] Modifies the trusted apps validator to use an advanced mode "free-for-all" schema identical to what is allowed in the event filters validator if there is `form_mode:advanced` present in the exception item's `tags` field. - [x] Hides the api behind the feature flag: `trustedAppsAdvancedMode` - [x] Adds api functional tests --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
12e71e8bab
commit
8f76a4f706
5 changed files with 224 additions and 10 deletions
|
@ -263,6 +263,11 @@ export const allowedExperimentalValues = Object.freeze({
|
|||
* Automatically installs the security AI prompts package
|
||||
*/
|
||||
securityAIPromptsEnabled: false,
|
||||
|
||||
/**
|
||||
* Enables advanced mode for Trusted Apps creation and update
|
||||
*/
|
||||
trustedAppsAdvancedMode: false,
|
||||
});
|
||||
|
||||
type ExperimentalConfigKeys = Array<keyof ExperimentalFeatures>;
|
||||
|
|
|
@ -187,6 +187,26 @@ const TrustedAppDataSchema = schema.object(
|
|||
{ unknowns: 'ignore' }
|
||||
);
|
||||
|
||||
/**
|
||||
* Schema to validate Trusted Apps in Advanced mode
|
||||
*/
|
||||
const TrustedAppAdvancedModeDataSchema = schema.object(
|
||||
{
|
||||
entries: schema.arrayOf(
|
||||
schema.object(
|
||||
{
|
||||
field: schema.string(),
|
||||
},
|
||||
{ unknowns: 'ignore' }
|
||||
),
|
||||
{ minSize: 1 }
|
||||
),
|
||||
},
|
||||
{
|
||||
unknowns: 'ignore',
|
||||
}
|
||||
);
|
||||
|
||||
export class TrustedAppValidator extends BaseValidator {
|
||||
static isTrustedApp(item: { listId: string }): boolean {
|
||||
return item.listId === ENDPOINT_ARTIFACT_LISTS.trustedApps.id;
|
||||
|
@ -270,7 +290,16 @@ export class TrustedAppValidator extends BaseValidator {
|
|||
await this.validateBasicData(item);
|
||||
|
||||
try {
|
||||
TrustedAppDataSchema.validate(item, { os: item.osTypes[0] });
|
||||
const isTAAdvancedModeFeatureFlagEnabled =
|
||||
this.endpointAppContext.experimentalFeatures.trustedAppsAdvancedMode;
|
||||
const isAdvancedMode = item.tags.includes('form_mode:advanced');
|
||||
if (!isTAAdvancedModeFeatureFlagEnabled && isAdvancedMode) {
|
||||
throw new Error('Trusted apps advanced mode feature is not enabled');
|
||||
} else if (isTAAdvancedModeFeatureFlagEnabled && isAdvancedMode) {
|
||||
TrustedAppAdvancedModeDataSchema.validate(item);
|
||||
} else {
|
||||
TrustedAppDataSchema.validate(item, { os: item.osTypes[0] });
|
||||
}
|
||||
} catch (error) {
|
||||
throw new EndpointArtifactExceptionValidationError(error.message);
|
||||
}
|
||||
|
|
|
@ -6,11 +6,15 @@
|
|||
*/
|
||||
|
||||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
import type { ExperimentalFeatures as SecuritySolutionExperimentalFeatures } from '@kbn/security-solution-plugin/common';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(
|
||||
require.resolve('../../../../../config/ess/config.base.edr_workflows.trial')
|
||||
);
|
||||
const securitySolutionEnableExperimental: Array<keyof SecuritySolutionExperimentalFeatures> = [
|
||||
'trustedAppsAdvancedMode',
|
||||
];
|
||||
|
||||
return {
|
||||
...functionalConfig.getAll(),
|
||||
|
@ -18,5 +22,19 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
junit: {
|
||||
reportName: 'EDR Workflows - Artifacts Integration Tests - ESS Env - Trial License',
|
||||
},
|
||||
kbnTestServer: {
|
||||
...functionalConfig.get('kbnTestServer'),
|
||||
serverArgs: [
|
||||
...functionalConfig.get('kbnTestServer.serverArgs').filter(
|
||||
// Exclude Security solution experimental features
|
||||
// properties since we are overriding them here
|
||||
(arg: string) => !arg.includes('xpack.securitySolution.enableExperimental')
|
||||
),
|
||||
// SECURITY SOLUTION: set any experimental feature flags for testing
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify(
|
||||
securitySolutionEnableExperimental
|
||||
)}`,
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,11 +6,15 @@
|
|||
*/
|
||||
|
||||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
import type { ExperimentalFeatures as SecuritySolutionExperimentalFeatures } from '@kbn/security-solution-plugin/common';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(
|
||||
require.resolve('../../../../../config/serverless/config.base.edr_workflows')
|
||||
);
|
||||
const securitySolutionEnableExperimental: Array<keyof SecuritySolutionExperimentalFeatures> = [
|
||||
'trustedAppsAdvancedMode',
|
||||
];
|
||||
|
||||
return {
|
||||
...functionalConfig.getAll(),
|
||||
|
@ -18,5 +22,19 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
junit: {
|
||||
reportName: 'EDR Workflows - Artifacts Integration Tests - Serverless Env - Complete',
|
||||
},
|
||||
kbnTestServer: {
|
||||
...functionalConfig.get('kbnTestServer'),
|
||||
serverArgs: [
|
||||
...functionalConfig.get('kbnTestServer.serverArgs').filter(
|
||||
// Exclude Security solution experimental features
|
||||
// properties since we are overriding them here
|
||||
(arg: string) => !arg.includes('xpack.securitySolution.enableExperimental')
|
||||
),
|
||||
// SECURITY SOLUTION: set any experimental feature flags for testing
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify(
|
||||
securitySolutionEnableExperimental
|
||||
)}`,
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -241,28 +241,35 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('should not error if signer is set for a windows os entry item', async () => {
|
||||
const body = trustedAppApiCalls[0].getBody();
|
||||
const body = trustedAppApiCall.getBody();
|
||||
|
||||
// Match request version with artifact version
|
||||
if ('_version' in body) {
|
||||
body._version = trustedAppData.artifact._version;
|
||||
}
|
||||
return body;
|
||||
|
||||
body.os_types = ['windows'];
|
||||
body.entries = exceptionsGenerator.generateTrustedAppSignerEntry();
|
||||
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCalls[0].method](
|
||||
trustedAppApiCalls[0].path
|
||||
)
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('should not error if signer is set for a mac os entry item', async () => {
|
||||
const body = trustedAppApiCalls[0].getBody();
|
||||
const body = trustedAppApiCall.getBody();
|
||||
|
||||
// Match request version with artifact version
|
||||
if ('_version' in body) {
|
||||
body._version = trustedAppData.artifact._version;
|
||||
}
|
||||
return body;
|
||||
|
||||
body.os_types = ['macos'];
|
||||
body.entries = exceptionsGenerator.generateTrustedAppSignerEntry('mac');
|
||||
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCalls[0].method](
|
||||
trustedAppApiCalls[0].path
|
||||
)
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(200);
|
||||
|
@ -294,6 +301,143 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.expect(anEndpointArtifactError)
|
||||
.expect(anErrorMessageWith(/invalid policy ids/));
|
||||
});
|
||||
|
||||
describe('when in advanced form mode', () => {
|
||||
const getAdvancedModeBody = () => {
|
||||
const body = trustedAppApiCall.getBody();
|
||||
body.tags.push('form_mode:advanced');
|
||||
|
||||
// Match request version with artifact version
|
||||
if ('_version' in body) {
|
||||
body._version = trustedAppData.artifact._version;
|
||||
}
|
||||
return body;
|
||||
};
|
||||
|
||||
it(`should NOT error on [${trustedAppApiCall.method}] if invalid condition entry fields are used`, async () => {
|
||||
const body = getAdvancedModeBody();
|
||||
body.entries[0].field = 'some.invalid.field';
|
||||
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it(`should NOT error on [${trustedAppApiCall.method}] if a condition entry field is used more than once`, async () => {
|
||||
const body = getAdvancedModeBody();
|
||||
body.entries.push({ ...body.entries[0] });
|
||||
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it(`should NOT error on [${trustedAppApiCall.method}] if an invalid hash is used`, async () => {
|
||||
const body = getAdvancedModeBody();
|
||||
|
||||
body.entries = [
|
||||
{
|
||||
field: 'process.hash.md5',
|
||||
operator: 'included',
|
||||
type: 'match',
|
||||
value: '1',
|
||||
},
|
||||
];
|
||||
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it(`should NOT error on [${trustedAppApiCall.method}] if signer is set for a non windows os entry item`, async () => {
|
||||
const body = getAdvancedModeBody();
|
||||
|
||||
body.os_types = ['linux'];
|
||||
body.entries = exceptionsGenerator.generateTrustedAppSignerEntry();
|
||||
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it(`should NOT error on [${trustedAppApiCall.method} if Mac signer field is used for Windows entry`, async () => {
|
||||
const body = getAdvancedModeBody();
|
||||
|
||||
body.os_types = ['windows'];
|
||||
body.entries = exceptionsGenerator.generateTrustedAppSignerEntry('mac');
|
||||
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it(`should NOT error on [${trustedAppApiCall.method} if Windows signer field is used for Mac entry`, async () => {
|
||||
const body = getAdvancedModeBody();
|
||||
|
||||
body.os_types = ['macos'];
|
||||
body.entries = exceptionsGenerator.generateTrustedAppSignerEntry();
|
||||
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('should not error if signer is set for a windows os entry item', async () => {
|
||||
const body = getAdvancedModeBody();
|
||||
|
||||
body.os_types = ['windows'];
|
||||
body.entries = exceptionsGenerator.generateTrustedAppSignerEntry();
|
||||
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('should not error if signer is set for a mac os entry item', async () => {
|
||||
const body = getAdvancedModeBody();
|
||||
|
||||
body.os_types = ['macos'];
|
||||
body.entries = exceptionsGenerator.generateTrustedAppSignerEntry('mac');
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it(`should error on [${trustedAppApiCall.method}] if more than one OS is set`, async () => {
|
||||
const body = getAdvancedModeBody();
|
||||
|
||||
body.os_types = ['linux', 'windows'];
|
||||
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(400)
|
||||
.expect(anEndpointArtifactError)
|
||||
.expect(anErrorMessageWith(/\[osTypes\]: array size is \[2\]/));
|
||||
});
|
||||
|
||||
it(`should error on [${trustedAppApiCall.method}] if policy id is invalid`, async () => {
|
||||
const body = getAdvancedModeBody();
|
||||
|
||||
body.tags = [`${BY_POLICY_ARTIFACT_TAG_PREFIX}123`];
|
||||
|
||||
// Using superuser here as we need custom license for this action
|
||||
await endpointPolicyManagerSupertest[trustedAppApiCall.method](trustedAppApiCall.path)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(body)
|
||||
.expect(400)
|
||||
.expect(anEndpointArtifactError)
|
||||
.expect(anErrorMessageWith(/invalid policy ids/));
|
||||
});
|
||||
});
|
||||
}
|
||||
for (const trustedAppApiCall of [...needsWritePrivilege, ...needsReadPrivilege]) {
|
||||
it(`should not error on [${trustedAppApiCall.method}] - [${trustedAppApiCall.info}]`, async () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue