[SIEM] Server NP Followup (#64010)
* Remove unused file This was moved to a constant in common/constants. * Remove unused types Omit is now part of Typescript, and Pick3 is unused. * Define and export SIEM's plugin contracts They're empty for now. * Import config type from config file Instead of our plugin index, which could only cause circular dependencies. * SiemClient API uses getter function instead of direct property access * Add public mock for SiemClient * Fix typo in extract-mitre-attacks script We were backgrounding the process (&) instead of ANDing it with the linting. Whoops! * Remove missed instance of old siemClient API I missed this one when grepping, but typescript and CI saved me. * Use our client mock in our test suite This was causing some test failures as I forgot to update the client mock * Update script following updates to the output's usage This was changed in https://github.com/elastic/kibana/pull/55883 but the script was not updated accordingly.
This commit is contained in:
parent
5a55c9a8f6
commit
ff5971ac17
29 changed files with 61 additions and 51 deletions
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
/** The comma-delimited list of Elasticsearch indices from which the SIEM app collects events */
|
||||
export const defaultIndexPattern = [
|
||||
'apm-*-transaction*',
|
||||
'auditbeat-*',
|
||||
'endgame-*',
|
||||
'filebeat-*',
|
||||
'packetbeat-*',
|
||||
'winlogbeat-*',
|
||||
];
|
|
@ -6,12 +6,6 @@
|
|||
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export type Pick3<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2]> = {
|
||||
[P1 in K1]: { [P2 in K2]: { [P3 in K3]: T[K1][K2][P3] } };
|
||||
};
|
||||
|
||||
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
||||
|
||||
// This type is for typing EuiDescriptionList
|
||||
export interface DescriptionList {
|
||||
title: NonNullable<ReactNode>;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"private": true,
|
||||
"license": "Elastic-License",
|
||||
"scripts": {
|
||||
"extract-mitre-attacks": "node scripts/extract_tactics_techniques_mitre.js & node ../../../scripts/eslint ../../legacy/plugins/siem/public/pages/detection_engine/mitre/mitre_tactics_techniques.ts --fix",
|
||||
"extract-mitre-attacks": "node scripts/extract_tactics_techniques_mitre.js && node ../../../scripts/eslint ../../legacy/plugins/siem/public/pages/detection_engine/mitre/mitre_tactics_techniques.ts --fix",
|
||||
"build-graphql-types": "node scripts/generate_types_from_graphql.js",
|
||||
"cypress:open": "cypress open --config-file ./cypress/cypress.json",
|
||||
"cypress:run": "cypress run --spec ./cypress/integration/**/*.spec.ts --config-file ./cypress/cypress.json --reporter ../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json; status=$?; ../../node_modules/.bin/mochawesome-merge --reportDir ../../../target/kibana-siem/cypress/results > ../../../target/kibana-siem/cypress/results/output.json; ../../../node_modules/.bin/marge ../../../target/kibana-siem/cypress/results/output.json --reportDir ../../../target/kibana-siem/cypress/results; mkdir -p ../../../target/junit && cp ../../../target/kibana-siem/cypress/results/*.xml ../../../target/junit/ && exit $status;",
|
||||
|
|
|
@ -123,7 +123,7 @@ async function main() {
|
|||
.replace(/}"/g, '}')
|
||||
.replace(/"{/g, '{')};
|
||||
|
||||
export const techniques = ${JSON.stringify(techniques, null, 2)};
|
||||
export const technique = ${JSON.stringify(techniques, null, 2)};
|
||||
|
||||
export const techniquesOptions: MitreTechniquesOptions[] =
|
||||
${JSON.stringify(getTechniquesOptions(techniques), null, 2)
|
||||
|
|
|
@ -9,7 +9,7 @@ import { createMockConfig } from '../lib/detection_engine/routes/__mocks__';
|
|||
import { SiemClient } from './client';
|
||||
|
||||
describe('SiemClient', () => {
|
||||
describe('#signalsIndex', () => {
|
||||
describe('#getSignalsIndex', () => {
|
||||
it('returns the index scoped to the specified spaceId', () => {
|
||||
const mockConfig = {
|
||||
...createMockConfig(),
|
||||
|
@ -18,7 +18,7 @@ describe('SiemClient', () => {
|
|||
const spaceId = 'fooSpace';
|
||||
const client = new SiemClient(spaceId, mockConfig);
|
||||
|
||||
expect(client.signalsIndex).toEqual('mockSignalsIndex-fooSpace');
|
||||
expect(client.getSignalsIndex()).toEqual('mockSignalsIndex-fooSpace');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,14 +4,16 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ConfigType } from '..';
|
||||
import { ConfigType } from '../config';
|
||||
|
||||
export class SiemClient {
|
||||
public readonly signalsIndex: string;
|
||||
private readonly signalsIndex: string;
|
||||
|
||||
constructor(private spaceId: string, private config: ConfigType) {
|
||||
const configuredSignalsIndex = this.config.signalsIndex;
|
||||
|
||||
this.signalsIndex = `${configuredSignalsIndex}-${this.spaceId}`;
|
||||
}
|
||||
|
||||
public getSignalsIndex = (): string => this.signalsIndex;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { KibanaRequest } from '../../../../../src/core/server';
|
||||
import { SiemClient } from './client';
|
||||
import { ConfigType } from '..';
|
||||
import { ConfigType } from '../config';
|
||||
|
||||
interface SetupDependencies {
|
||||
getSpaceId?: (request: KibanaRequest) => string | undefined;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { PluginInitializerContext } from '../../../../src/core/server';
|
||||
import { Plugin } from './plugin';
|
||||
import { Plugin, PluginSetup, PluginStart } from './plugin';
|
||||
import { configSchema, ConfigType } from './config';
|
||||
|
||||
export const plugin = (context: PluginInitializerContext) => {
|
||||
|
@ -14,4 +14,4 @@ export const plugin = (context: PluginInitializerContext) => {
|
|||
|
||||
export const config = { schema: configSchema };
|
||||
|
||||
export { ConfigType };
|
||||
export { ConfigType, Plugin, PluginSetup, PluginStart };
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
import { alertsClientMock } from '../../../../../../alerting/server/mocks';
|
||||
import { actionsClientMock } from '../../../../../../actions/server/mocks';
|
||||
import { licensingMock } from '../../../../../../licensing/server/mocks';
|
||||
import { siemMock } from '../../../../mocks';
|
||||
|
||||
const createMockClients = () => ({
|
||||
actionsClient: actionsClientMock.create(),
|
||||
|
@ -20,7 +21,7 @@ const createMockClients = () => ({
|
|||
clusterClient: elasticsearchServiceMock.createScopedClusterClient(),
|
||||
licensing: { license: licensingMock.createLicenseMock() },
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
siemClient: { signalsIndex: 'mockSignalsIndex' },
|
||||
siemClient: siemMock.createClient(),
|
||||
});
|
||||
|
||||
const createRequestContextMock = (
|
||||
|
|
|
@ -37,7 +37,7 @@ export const createIndexRoute = (router: IRouter) => {
|
|||
return siemResponse.error({ statusCode: 404 });
|
||||
}
|
||||
|
||||
const index = siemClient.signalsIndex;
|
||||
const index = siemClient.getSignalsIndex();
|
||||
const indexExists = await getIndexExists(callCluster, index);
|
||||
if (indexExists) {
|
||||
return siemResponse.error({
|
||||
|
|
|
@ -45,7 +45,7 @@ export const deleteIndexRoute = (router: IRouter) => {
|
|||
}
|
||||
|
||||
const callCluster = clusterClient.callAsCurrentUser;
|
||||
const index = siemClient.signalsIndex;
|
||||
const index = siemClient.getSignalsIndex();
|
||||
const indexExists = await getIndexExists(callCluster, index);
|
||||
|
||||
if (!indexExists) {
|
||||
|
|
|
@ -29,7 +29,7 @@ export const readIndexRoute = (router: IRouter) => {
|
|||
return siemResponse.error({ statusCode: 404 });
|
||||
}
|
||||
|
||||
const index = siemClient.signalsIndex;
|
||||
const index = siemClient.getSignalsIndex();
|
||||
const indexExists = await getIndexExists(clusterClient.callAsCurrentUser, index);
|
||||
|
||||
if (indexExists) {
|
||||
|
|
|
@ -36,7 +36,7 @@ export const readPrivilegesRoute = (
|
|||
return siemResponse.error({ statusCode: 404 });
|
||||
}
|
||||
|
||||
const index = siemClient.signalsIndex;
|
||||
const index = siemClient.getSignalsIndex();
|
||||
const clusterPrivileges = await readPrivileges(clusterClient.callAsCurrentUser, index);
|
||||
const privileges = merge(clusterPrivileges, {
|
||||
is_authenticated: security?.authc.isAuthenticated(request) ?? false,
|
||||
|
|
|
@ -49,7 +49,7 @@ export const addPrepackedRulesRoute = (router: IRouter) => {
|
|||
const rulesToInstall = getRulesToInstall(rulesFromFileSystem, prepackagedRules);
|
||||
const rulesToUpdate = getRulesToUpdate(rulesFromFileSystem, prepackagedRules);
|
||||
|
||||
const { signalsIndex } = siemClient;
|
||||
const signalsIndex = siemClient.getSignalsIndex();
|
||||
if (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0) {
|
||||
const signalsIndexExists = await getIndexExists(
|
||||
clusterClient.callAsCurrentUser,
|
||||
|
|
|
@ -92,7 +92,7 @@ export const createRulesBulkRoute = (router: IRouter) => {
|
|||
try {
|
||||
validateLicenseForRuleType({ license: context.licensing.license, ruleType: type });
|
||||
|
||||
const finalIndex = outputIndex ?? siemClient.signalsIndex;
|
||||
const finalIndex = outputIndex ?? siemClient.getSignalsIndex();
|
||||
const indexExists = await getIndexExists(clusterClient.callAsCurrentUser, finalIndex);
|
||||
if (!indexExists) {
|
||||
return createBulkErrorObject({
|
||||
|
|
|
@ -82,7 +82,7 @@ export const createRulesRoute = (router: IRouter): void => {
|
|||
return siemResponse.error({ statusCode: 404 });
|
||||
}
|
||||
|
||||
const finalIndex = outputIndex ?? siemClient.signalsIndex;
|
||||
const finalIndex = outputIndex ?? siemClient.getSignalsIndex();
|
||||
const indexExists = await getIndexExists(clusterClient.callAsCurrentUser, finalIndex);
|
||||
if (!indexExists) {
|
||||
return siemResponse.error({
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { IRouter } from '../../../../../../../../src/core/server';
|
||||
import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants';
|
||||
import { ConfigType } from '../../../..';
|
||||
import { ConfigType } from '../../../../config';
|
||||
import { ExportRulesRequestParams } from '../../rules/types';
|
||||
import { getNonPackagedRulesCount } from '../../rules/get_existing_prepackaged_rules';
|
||||
import { exportRulesSchema, exportRulesQuerySchema } from '../schemas/export_rules_schema';
|
||||
|
|
|
@ -126,6 +126,7 @@ describe('import_rules_route', () => {
|
|||
});
|
||||
|
||||
test('returns an error if the index does not exist', async () => {
|
||||
clients.siemClient.getSignalsIndex.mockReturnValue('mockSignalsIndex');
|
||||
clients.clusterClient.callAsCurrentUser.mockResolvedValue(getEmptyIndex());
|
||||
const response = await server.inject(request, context);
|
||||
expect(response.status).toEqual(200);
|
||||
|
|
|
@ -10,7 +10,7 @@ import { extname } from 'path';
|
|||
import { IRouter } from '../../../../../../../../src/core/server';
|
||||
import { createPromiseFromStreams } from '../../../../../../../../src/legacy/utils/streams';
|
||||
import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants';
|
||||
import { ConfigType } from '../../../..';
|
||||
import { ConfigType } from '../../../../config';
|
||||
import { createRules } from '../../rules/create_rules';
|
||||
import { ImportRulesRequestParams } from '../../rules/types';
|
||||
import { readRules } from '../../rules/read_rules';
|
||||
|
@ -147,7 +147,7 @@ export const importRulesRoute = (router: IRouter, config: ConfigType) => {
|
|||
ruleType: type,
|
||||
});
|
||||
|
||||
const signalsIndex = siemClient.signalsIndex;
|
||||
const signalsIndex = siemClient.getSignalsIndex();
|
||||
const indexExists = await getIndexExists(
|
||||
clusterClient.callAsCurrentUser,
|
||||
signalsIndex
|
||||
|
|
|
@ -83,7 +83,7 @@ export const updateRulesBulkRoute = (router: IRouter) => {
|
|||
version,
|
||||
exceptions_list,
|
||||
} = payloadRule;
|
||||
const finalIndex = outputIndex ?? siemClient.signalsIndex;
|
||||
const finalIndex = outputIndex ?? siemClient.getSignalsIndex();
|
||||
const idOrRuleIdOrUnknown = id ?? ruleId ?? '(unknown id)';
|
||||
try {
|
||||
validateLicenseForRuleType({ license: context.licensing.license, ruleType: type });
|
||||
|
|
|
@ -83,7 +83,7 @@ export const updateRulesRoute = (router: IRouter) => {
|
|||
return siemResponse.error({ statusCode: 404 });
|
||||
}
|
||||
|
||||
const finalIndex = outputIndex ?? siemClient.signalsIndex;
|
||||
const finalIndex = outputIndex ?? siemClient.getSignalsIndex();
|
||||
const rule = await updateRules({
|
||||
alertsClient,
|
||||
actionsClient,
|
||||
|
|
|
@ -44,7 +44,7 @@ export const setSignalsStatusRoute = (router: IRouter) => {
|
|||
}
|
||||
try {
|
||||
const result = await clusterClient.callAsCurrentUser('updateByQuery', {
|
||||
index: siemClient.signalsIndex,
|
||||
index: siemClient.getSignalsIndex(),
|
||||
body: {
|
||||
script: {
|
||||
source: `ctx._source.signal.status = '${status}'`,
|
||||
|
|
|
@ -29,7 +29,7 @@ export const querySignalsRoute = (router: IRouter) => {
|
|||
|
||||
try {
|
||||
const result = await clusterClient.callAsCurrentUser('search', {
|
||||
index: siemClient.signalsIndex,
|
||||
index: siemClient.getSignalsIndex(),
|
||||
body: { query, aggs, _source, track_total_hits, size },
|
||||
ignoreUnavailable: true,
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ import { set as _set } from 'lodash/fp';
|
|||
|
||||
import { TIMELINE_EXPORT_URL } from '../../../../common/constants';
|
||||
import { IRouter } from '../../../../../../../src/core/server';
|
||||
import { ConfigType } from '../../..';
|
||||
import { ConfigType } from '../../../config';
|
||||
import { transformError, buildSiemResponse } from '../../detection_engine/routes/utils';
|
||||
|
||||
import { getExportTimelineByObjectIds } from './utils/export_timelines';
|
||||
|
|
|
@ -32,7 +32,7 @@ import { IRouter } from '../../../../../../../src/core/server';
|
|||
import { SetupPlugins } from '../../../plugin';
|
||||
import { ImportTimelinesPayloadSchemaRt } from './schemas/import_timelines_schema';
|
||||
import { importRulesSchema } from '../../detection_engine/routes/schemas/response/import_rules_schema';
|
||||
import { ConfigType } from '../../..';
|
||||
import { ConfigType } from '../../../config';
|
||||
|
||||
import { Timeline } from '../saved_object';
|
||||
import { validate } from '../../detection_engine/routes/rules/validate';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { AuthenticatedUser } from '../../../security/public';
|
||||
import { RequestHandlerContext } from '../../../../../src/core/server';
|
||||
export { ConfigType as Configuration } from '../';
|
||||
export { ConfigType as Configuration } from '../config';
|
||||
|
||||
import { Authentications } from './authentications';
|
||||
import { Events } from './events';
|
||||
|
|
17
x-pack/plugins/siem/server/mocks.ts
Normal file
17
x-pack/plugins/siem/server/mocks.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SiemClient } from './types';
|
||||
|
||||
type SiemClientMock = jest.Mocked<SiemClient>;
|
||||
const createSiemClientMock = (): SiemClientMock =>
|
||||
(({
|
||||
getSignalsIndex: jest.fn(),
|
||||
} as unknown) as SiemClientMock);
|
||||
|
||||
export const siemMock = {
|
||||
createClient: createSiemClientMock,
|
||||
};
|
|
@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import {
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
Plugin as IPlugin,
|
||||
PluginInitializerContext,
|
||||
Logger,
|
||||
} from '../../../../src/core/server';
|
||||
|
@ -61,7 +62,12 @@ export interface StartPlugins {
|
|||
alerting: AlertingStart;
|
||||
}
|
||||
|
||||
export class Plugin {
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface PluginSetup {}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface PluginStart {}
|
||||
|
||||
export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, StartPlugins> {
|
||||
readonly name = 'siem';
|
||||
private readonly logger: Logger;
|
||||
private readonly config$: Observable<ConfigType>;
|
||||
|
@ -204,7 +210,11 @@ export class Plugin {
|
|||
|
||||
const libs = compose(core, plugins, this.context.env.mode.prod);
|
||||
initServer(libs);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public start(core: CoreStart, plugins: StartPlugins) {}
|
||||
public start(core: CoreStart, plugins: StartPlugins) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import { getPrepackagedRulesStatusRoute } from '../lib/detection_engine/routes/r
|
|||
import { importTimelinesRoute } from '../lib/timeline/routes/import_timelines_route';
|
||||
import { exportTimelinesRoute } from '../lib/timeline/routes/export_timelines_route';
|
||||
import { SetupPlugins } from '../plugin';
|
||||
import { ConfigType } from '..';
|
||||
import { ConfigType } from '../config';
|
||||
|
||||
export const initRoutes = (
|
||||
router: IRouter,
|
||||
|
|
Loading…
Reference in a new issue