Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TheHive Case Connector #180138

Merged
merged 23 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9d1522e
Add thehive case connector
brijesh-elastic Apr 5, 2024
8a527cc
Merge branch 'main' into thehive_case_connector
brijesh-elastic Apr 17, 2024
b5d8164
resolve review comments
brijesh-elastic Apr 17, 2024
1c8d856
Merge branch 'main' into thehive_case_connector
brijesh-elastic Apr 19, 2024
0eeed1b
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Apr 19, 2024
4f2cc46
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 19, 2024
5f96889
Add documentation and functional test
brijesh-elastic Apr 19, 2024
86c87fd
update minimumLicenseRequired to platinum
brijesh-elastic Apr 23, 2024
d6b7ae3
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 23, 2024
a31e7b8
Merge branch 'main' into thehive_case_connector
brijesh-elastic May 6, 2024
f576b5f
Refactor UI and update server code to align with CaseConnector framew…
brijesh-elastic May 6, 2024
c5eee3f
Merge branch 'main' into thehive_case_connector
brijesh-elastic May 30, 2024
fa74245
Disable Recovered ActionGroup
brijesh-elastic May 30, 2024
57bb7fe
Merge branch 'main' into thehive_case_connector
brijesh-elastic Jul 15, 2024
51d4bea
Apply Translations and fix test cases
brijesh-elastic Jul 16, 2024
c9636fe
Resolve list and test issue
brijesh-elastic Jul 16, 2024
beb6a40
Resolve build failure
brijesh-elastic Jul 17, 2024
f5a5a60
hide the connector in UI for intermediate release
js-jankisalvi Jul 25, 2024
3ee54d4
Merge branch 'main' into thehive_case_connector
js-jankisalvi Jul 25, 2024
103d1a9
Merge branch 'main' into thehive_case_connector
elasticmachine Jul 26, 2024
d5c5eca
Resolve bugfix
brijesh-elastic Jul 29, 2024
c269aee
Merge branch 'main' into thehive_case_connector
elasticmachine Jul 29, 2024
a6c054e
Preserve severity, tlp and tag values in UI
brijesh-elastic Jul 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions x-pack/plugins/stack_connectors/common/thehive/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { i18n } from '@kbn/i18n';

export const THEHIVE_TITLE = i18n.translate(
'xpack.stackConnectors.components.thehive.connectorTypeTitle',
{
defaultMessage: 'TheHive',
}
);
export const THEHIVE_CONNECTOR_ID = '.thehive';

export enum SUB_ACTION {
PUSH_TO_SERVICE = 'pushToService',
CREATE_ALERT = 'createAlert',
}
export enum TheHiveSeverity {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember discussing it but just to be sure, are we sure that the severity and the TLP values are fixed and cannot be changed by users in TheHive?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, users can't change or add new enum for severity and TLP in TheHive.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I confirm, the TLP, PAP and Severity are static values in TheHive

LOW = 1,
MEDIUM = 2,
HIGH = 3,
CRITICAL = 4,
}
export enum TheHiveTLP {
CLEAR = 0,
GREEN = 1,
AMBER = 2,
AMBER_STRICT = 3,
RED = 4,
}
179 changes: 179 additions & 0 deletions x-pack/plugins/stack_connectors/common/thehive/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { schema } from '@kbn/config-schema';
import { TheHiveSeverity, TheHiveTLP, SUB_ACTION } from './constants';

export const TheHiveConfigSchema = schema.object({
url: schema.string(),
organisation: schema.nullable(schema.string()),
});

export const TheHiveSecretsSchema = schema.object({
api_key: schema.string()
});

export const ExecutorSubActionPushParamsSchema = schema.object({
incident: schema.object({
title: schema.string(),
description: schema.string(),
externalId: schema.nullable(schema.string()),
severity: schema.nullable(schema.number({ defaultValue: TheHiveSeverity.MEDIUM })),
tlp: schema.nullable(schema.number({ defaultValue: TheHiveTLP.AMBER })),
tags: schema.nullable(schema.arrayOf(schema.string())),
}),
comments: schema.nullable(
schema.arrayOf(
schema.object({
comment: schema.string(),
commentId: schema.string(),
})
)
),
});

export const ExecutorSubActionGetIncidentParamsSchema = schema.object({
externalId: schema.string(),
});

export const ExecutorSubActionCreateAlertParamsSchema = schema.object({
title: schema.string(),
description: schema.string(),
type: schema.string(),
source: schema.string(),
sourceRef: schema.string(),
severity: schema.nullable(schema.number({ defaultValue: TheHiveSeverity.MEDIUM })),
tlp: schema.nullable(schema.number({ defaultValue: TheHiveTLP.AMBER })),
tags: schema.nullable(schema.arrayOf(schema.string())),
});

export const ExecutorParamsSchema = schema.oneOf([
schema.object({
subAction: schema.literal(SUB_ACTION.PUSH_TO_SERVICE),
subActionParams: ExecutorSubActionPushParamsSchema,
}),
schema.object({
subAction: schema.literal(SUB_ACTION.CREATE_ALERT),
subActionParams: ExecutorSubActionCreateAlertParamsSchema,
}),
]);


export const TheHiveIncidentResponseSchema = schema.object(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure that we need all the fields to be listed in the schema? Do we use them all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we don't use all of them. However, I've included them all as a precaution for future needs.

LMK your thoughts.

Copy link
Member

@cnasikas cnasikas Apr 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what is best tbh. Assuming TheHIve will not introduce any breaking changes in the fields I do not see any harm in keeping them. If they do then our validation will start failing. The more field we have the more the chance for this to happen. If they are documented here https://docs.strangebee.com/thehive/api-docs/#operation/Create%20case then I think it is fine to keep them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, They are documented.

{
_id: schema.string(),
_type: schema.string(),
_createdBy: schema.string(),
_updatedBy: schema.nullable(schema.string()),
_createdAt: schema.number(),
_updatedAt: schema.nullable(schema.number()),
number: schema.number(),
title: schema.string(),
description: schema.string(),
severity: schema.number(),
severityLabel: schema.string(),
startDate: schema.number(),
endDate: schema.nullable(schema.number()),
tags: schema.nullable(schema.arrayOf(schema.string())),
flag: schema.boolean(),
tlp: schema.number(),
tlpLabel: schema.string(),
pap: schema.number(),
papLabel: schema.string(),
status: schema.string(),
stage: schema.string(),
summary: schema.nullable(schema.string()),
impactStatus: schema.nullable(schema.string()),
assignee: schema.nullable(schema.string()),
customFields: schema.nullable(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of schema.object({}, { unknowns: 'allow' }) you can use schema.recordOf(schema.stirng(), schema.any())

userPermissions: schema.nullable(schema.arrayOf(schema.string())),
extraData: schema.object({}, { unknowns: 'allow' }),
newDate: schema.number(),
inProgressDate: schema.nullable(schema.number()),
closedDate: schema.nullable(schema.number()),
alertDate: schema.nullable(schema.number()),
alertNewDate: schema.nullable(schema.number()),
alertInProgressDate: schema.nullable(schema.number()),
alertImportedDate: schema.nullable(schema.number()),
timeToDetect: schema.number(),
timeToTriage: schema.nullable(schema.number()),
timeToQualify: schema.nullable(schema.number()),
timeToAcknowledge: schema.nullable(schema.number()),
timeToResolve: schema.nullable(schema.number()),
handlingDuration: schema.nullable(schema.number()),
},
{ unknowns: 'ignore' }
);

export const TheHiveUpdateIncidentResponseSchema = schema.any();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does TheHive return when you update a case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a PATCH request and it returns 204 No Content.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Do you know if schema.never() works? Not of this PR, but maybe the framework should skip the validation if the response is 204 No Content.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will check and update.


export const TheHiveAddCommentResponseSchema = schema.object(
{
_id: schema.string(),
_type: schema.string(),
createdBy: schema.string(),
createdAt: schema.number(),
updatedAt: schema.nullable(schema.number()),
updatedBy: schema.nullable(schema.string()),
message: schema.string(),
isEdited: schema.boolean(),
extraData: schema.object({}, { unknowns: 'allow' }),
},
{ unknowns: 'ignore' }
);

export const TheHiveCreateAlertResponseSchema = schema.object(
{
_id: schema.string(),
_type: schema.string(),
_createdBy: schema.string(),
_updatedBy: schema.nullable(schema.string()),
_createdAt: schema.number(),
_updatedAt: schema.nullable(schema.number()),
type: schema.string(),
source: schema.string(),
sourceRef: schema.string(),
externalLink: schema.nullable(schema.string()),
title: schema.string(),
description: schema.string(),
severity: schema.number(),
severityLabel: schema.string(),
date: schema.number(),
tags: schema.nullable(schema.arrayOf(schema.string())),
tlp: schema.number(),
tlpLabel: schema.string(),
pap: schema.number(),
papLabel: schema.string(),
follow: schema.nullable(schema.boolean()),
customFields: schema.nullable(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))),
caseTemplate: schema.nullable(schema.string()),
observableCount: schema.number(),
caseId: schema.nullable(schema.string()),
status: schema.string(),
stage: schema.string(),
assignee: schema.nullable(schema.string()),
summary: schema.nullable(schema.string()),
extraData: schema.object({}, { unknowns: 'allow' }),
newDate: schema.number(),
inProgressDate: schema.nullable(schema.number()),
closedDate: schema.nullable(schema.number()),
importedDate: schema.nullable(schema.number()),
timeToDetect: schema.number(),
timeToTriage: schema.nullable(schema.number()),
timeToQualify: schema.nullable(schema.number()),
timeToAcknowledge: schema.nullable(schema.number()),
},
{ unknowns: 'ignore' }
);

export const TheHiveFailureResponseSchema = schema.object(
{
type: schema.number(),
message: schema.string(),
},
{ unknowns: 'allow' }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we ignore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will remove it.

);
38 changes: 38 additions & 0 deletions x-pack/plugins/stack_connectors/common/thehive/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { TypeOf } from '@kbn/config-schema';
import {
TheHiveConfigSchema,
TheHiveSecretsSchema,
ExecutorParamsSchema,
ExecutorSubActionPushParamsSchema,
ExecutorSubActionCreateAlertParamsSchema,
TheHiveFailureResponseSchema,
} from './schema';

export type TheHiveConfig = TypeOf<typeof TheHiveConfigSchema>;
export type TheHiveSecrets = TypeOf<typeof TheHiveSecretsSchema>;

export type ExecutorParams = TypeOf<typeof ExecutorParamsSchema>;
export type ExecutorSubActionPushParams = TypeOf<typeof ExecutorSubActionPushParamsSchema>;
export type ExecutorSubActionCreateAlertParams = TypeOf<typeof ExecutorSubActionCreateAlertParamsSchema>;

export type TheHiveFailureResponse = TypeOf<typeof TheHiveFailureResponseSchema>;

export interface ExternalServiceIncidentResponse {
id: string;
title: string;
url: string;
pushedDate: string;
}

export interface ExternalServiceCommentResponse {
commentId: string;
pushedDate: string;
externalCommentId?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { getXmattersConnectorType } from './xmatters';
import { getD3SecurityConnectorType } from './d3security';
import { ExperimentalFeaturesService } from '../common/experimental_features_service';
import { getSentinelOneConnectorType } from './sentinelone';
import { getTheHiveConnectorType } from './thehive';

export interface RegistrationServices {
validateEmailAddresses: (
Expand Down Expand Up @@ -68,6 +69,7 @@ export function registerConnectorTypes({
connectorTypeRegistry.register(getTorqConnectorType());
connectorTypeRegistry.register(getTinesConnectorType());
connectorTypeRegistry.register(getD3SecurityConnectorType());
connectorTypeRegistry.register(getTheHiveConnectorType());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @cnasikas, does the new convention to put new connectors under a feature flag apply to this PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey! Yes, we need to follow the new intermediate release process for all new connectors.


if (ExperimentalFeaturesService.get().sentinelOneConnectorOn) {
connectorTypeRegistry.register(getSentinelOneConnectorType());
Expand Down
Loading