diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 81f698c5f321..70a13b03863d 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -68731,6 +68731,68 @@ components: type: string x-enum-varnames: - MANAGED_ORGS + MatchingSignalAttributes: + description: Attributes of a matching security signal. + properties: + event_tracker_id: + description: The tracker ID linking the signal back to the originating event. Distinct from `id`, which identifies the matching signal itself. + example: AAAAAWgOAX0mtsWfeQAAAABzX1RyYWNrZXJfMTIzNDU2Nzg5MA + type: string + severity: + description: The severity of the signal. + example: high + type: string + title: + description: The title of the signal. + example: Unusual login activity detected + type: string + trigger_time_ms: + description: The Unix timestamp (in milliseconds) at which the signal was triggered. + example: 1707393746000 + format: int64 + type: integer + required: + - event_tracker_id + - severity + - title + - trigger_time_ms + type: object + MatchingSignalData: + description: A security signal that matches the queried event. + properties: + attributes: + $ref: "#/components/schemas/MatchingSignalAttributes" + id: + description: The ID of the matching signal. + example: AAAAAWgN8Xwgr1vKDQAAAABBV2dOOFh3ZzZobm1mWXJFYTR0OA + type: string + type: + $ref: "#/components/schemas/MatchingSignalType" + required: + - id + - type + - attributes + type: object + MatchingSignalType: + default: matching_signal + description: The type of the resource. The value should always be `matching_signal`. + enum: + - matching_signal + example: matching_signal + type: string + x-enum-varnames: + - MATCHING_SIGNAL + MatchingSignalsResponse: + description: Response containing the list of security signals matching an event. + properties: + data: + description: Array of matching signals. + items: + $ref: "#/components/schemas/MatchingSignalData" + type: array + required: + - data + type: object MaxSessionDurationType: description: Data type of a maximum session duration update. enum: [max_session_duration] @@ -211106,6 +211168,63 @@ paths: x-unstable: |- **Note**: This endpoint is in Preview and is subject to change. If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/). + /api/v2/security_monitoring/events/{event_id}/matching_signals: + get: + description: Returns the list of security signals that match a given event on the given track. + operationId: GetMatchingSignals + parameters: + - description: The ID of the event to find matching signals for. + in: path + name: event_id + required: true + schema: + type: string + - description: The product track that the event belongs to. + in: query + name: track + required: true + schema: + type: string + responses: + "200": + content: + application/json: + examples: + default: + value: + data: + - attributes: + event_tracker_id: AAAAAWgOAX0mtsWfeQAAAABzX1RyYWNrZXJfMTIzNDU2Nzg5MA + severity: high + title: Unusual login activity detected + trigger_time_ms: 1707393746000 + id: AAAAAWgN8Xwgr1vKDQAAAABBV2dOOFh3ZzZobm1mWXJFYTR0OA + type: matching_signal + schema: + $ref: "#/components/schemas/MatchingSignalsResponse" + description: OK + "400": + $ref: "#/components/responses/BadRequestResponse" + "403": + $ref: "#/components/responses/NotAuthorizedResponse" + "404": + $ref: "#/components/responses/NotFoundResponse" + "429": + $ref: "#/components/responses/TooManyRequestsResponse" + security: + - apiKeyAuth: [] + appKeyAuth: [] + - AuthZ: + - security_monitoring_signals_read + summary: Get signals matching an event + tags: ["Security Monitoring"] + x-permission: + operator: OR + permissions: + - security_monitoring_signals_read + x-unstable: |- + **Note**: This endpoint is in preview and is subject to change. + If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/). /api/v2/security_monitoring/rules: get: description: List rules. diff --git a/features/v2/security_monitoring.feature b/features/v2/security_monitoring.feature index a72d703edd34..bb21f193462b 100644 --- a/features/v2/security_monitoring.feature +++ b/features/v2/security_monitoring.feature @@ -2532,6 +2532,33 @@ Feature: Security Monitoring When the request is sent Then the response status is 200 OK + @generated @skip @team:DataDog/cloud-siem + Scenario: Get signals matching an event returns "Bad Request" response + Given operation "GetMatchingSignals" enabled + And new "GetMatchingSignals" request + And request contains "event_id" parameter from "REPLACE.ME" + And request contains "track" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 400 Bad Request + + @generated @skip @team:DataDog/cloud-siem + Scenario: Get signals matching an event returns "Not Found" response + Given operation "GetMatchingSignals" enabled + And new "GetMatchingSignals" request + And request contains "event_id" parameter from "REPLACE.ME" + And request contains "track" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 404 Not Found + + @generated @skip @team:DataDog/cloud-siem + Scenario: Get signals matching an event returns "OK" response + Given operation "GetMatchingSignals" enabled + And new "GetMatchingSignals" request + And request contains "event_id" parameter from "REPLACE.ME" + And request contains "track" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 200 OK + @generated @skip @team:DataDog/cloud-siem Scenario: Get suggested actions for a signal returns "Not Found" response Given new "GetSuggestedActionsMatchingSignal" request diff --git a/features/v2/undo.json b/features/v2/undo.json index 1abefe2e9ed3..316bfc290bba 100644 --- a/features/v2/undo.json +++ b/features/v2/undo.json @@ -8745,6 +8745,12 @@ "type": "safe" } }, + "GetMatchingSignals": { + "tag": "Security Monitoring", + "undo": { + "type": "safe" + } + }, "ListSecurityMonitoringRules": { "tag": "Security Monitoring", "undo": { diff --git a/private/bdd_runner/src/support/scenarios_model_mapping.ts b/private/bdd_runner/src/support/scenarios_model_mapping.ts index f04ae0389649..8930f273b4f5 100644 --- a/private/bdd_runner/src/support/scenarios_model_mapping.ts +++ b/private/bdd_runner/src/support/scenarios_model_mapping.ts @@ -7214,6 +7214,17 @@ export const ScenariosModelMappings: { [key: string]: OperationMapping } = { }, operationResponseType: "SingleEntityContextResponse", }, + "SecurityMonitoringApi.V2.GetMatchingSignals": { + eventId: { + type: "string", + format: "", + }, + track: { + type: "string", + format: "", + }, + operationResponseType: "MatchingSignalsResponse", + }, "SecurityMonitoringApi.V2.ListSecurityMonitoringRules": { pageSize: { type: "number", diff --git a/services/security_monitoring/src/v2/SecurityMonitoringApi.ts b/services/security_monitoring/src/v2/SecurityMonitoringApi.ts index 578731cf8ed9..d3b17e5d6b0d 100644 --- a/services/security_monitoring/src/v2/SecurityMonitoringApi.ts +++ b/services/security_monitoring/src/v2/SecurityMonitoringApi.ts @@ -90,6 +90,7 @@ import { ListHistoricalJobsResponse } from "./models/ListHistoricalJobsResponse" import { ListSecurityFindingsResponse } from "./models/ListSecurityFindingsResponse"; import { ListVulnerabilitiesResponse } from "./models/ListVulnerabilitiesResponse"; import { ListVulnerableAssetsResponse } from "./models/ListVulnerableAssetsResponse"; +import { MatchingSignalsResponse } from "./models/MatchingSignalsResponse"; import { MuteFindingsRequest } from "./models/MuteFindingsRequest"; import { MuteFindingsResponse } from "./models/MuteFindingsResponse"; import { MuteRuleCreateRequest } from "./models/MuteRuleCreateRequest"; @@ -5092,6 +5093,80 @@ export class SecurityMonitoringApiRequestFactory extends BaseAPIRequestFactory { return requestContext; } + public async getMatchingSignals( + eventId: string, + track: string, + _options?: Configuration, + ): Promise { + const _config = _options || this.configuration; + + if ( + !_config.unstableOperations["SecurityMonitoringApi.v2.getMatchingSignals"] + ) { + throw new Error( + "Unstable operation 'getMatchingSignals' is disabled. Enable it by setting `configuration.unstableOperations['SecurityMonitoringApi.v2.getMatchingSignals'] = true`", + ); + } + + // verify required parameter 'eventId' is not null or undefined + if (eventId === null || eventId === undefined) { + throw new RequiredError("eventId", "getMatchingSignals"); + } + + // verify required parameter 'track' is not null or undefined + if (track === null || track === undefined) { + throw new RequiredError("track", "getMatchingSignals"); + } + + // Path Params + const localVarPath = + "/api/v2/security_monitoring/events/{event_id}/matching_signals".replace( + "{event_id}", + encodeURIComponent(String(eventId)), + ); + + // Make Request Context + const { server, overrides } = _config.getServerAndOverrides( + "SecurityMonitoringApi.v2.getMatchingSignals", + SecurityMonitoringApi.operationServers, + ); + const requestContext = server.makeRequestContext( + localVarPath, + HttpMethod.GET, + overrides, + ); + requestContext.setHeaderParam("Accept", "application/json"); + requestContext.setHttpConfig(_config.httpConfig); + + // Set User-Agent + if (this.userAgent) { + requestContext.setHeaderParam("User-Agent", this.userAgent); + } + + // Set IaC header + if (_config.isIaC) { + requestContext.setHeaderParam("X-Datadog-Managed-By", "iac"); + } + + // Query Params + if (track !== undefined) { + requestContext.setQueryParam( + "track", + serialize(track, TypingInfo, "string", ""), + "", + ); + } + + // Apply auth methods + applySecurityAuthentication(_config, requestContext, [ + "apiKeyAuth", + "appKeyAuth", + "AuthZ", + ]); + + return requestContext; + } + public async getResourceEvaluationFilters( cloudProvider?: string, accountId?: string, @@ -16897,6 +16972,67 @@ export class SecurityMonitoringApiResponseProcessor { ); } + /** + * Unwraps the actual response sent by the server from the response context and deserializes the response content + * to the expected objects + * + * @params response Response returned by the server for a request to getMatchingSignals + * @throws ApiException if the response code was not in [200, 299] + */ + public async getMatchingSignals( + response: ResponseContext, + ): Promise { + const contentType = normalizeMediaType(response.headers["content-type"]); + if (response.httpStatusCode === 200) { + const body: MatchingSignalsResponse = deserialize( + parse(await response.body.text(), contentType), + TypingInfo, + "MatchingSignalsResponse", + ) as MatchingSignalsResponse; + return body; + } + if ( + response.httpStatusCode === 400 || + response.httpStatusCode === 403 || + response.httpStatusCode === 404 || + response.httpStatusCode === 429 + ) { + const bodyText = parse(await response.body.text(), contentType); + let body: APIErrorResponse; + try { + body = deserialize( + bodyText, + TypingInfo, + "APIErrorResponse", + ) as APIErrorResponse; + } catch (error) { + logger.debug(`Got error deserializing error: ${error}`); + throw new ApiException( + response.httpStatusCode, + bodyText, + ); + } + throw new ApiException(response.httpStatusCode, body); + } + + // Work around for missing responses in specification, e.g. for petstore.yaml + if (response.httpStatusCode >= 200 && response.httpStatusCode <= 299) { + const body: MatchingSignalsResponse = deserialize( + parse(await response.body.text(), contentType), + TypingInfo, + "MatchingSignalsResponse", + "", + ) as MatchingSignalsResponse; + return body; + } + + const body = (await response.body.text()) || ""; + throw new ApiException( + response.httpStatusCode, + 'Unknown API Status Code!\nBody: "' + body + '"', + ); + } + /** * Unwraps the actual response sent by the server from the response context and deserializes the response content * to the expected objects @@ -23836,6 +23972,19 @@ export interface SecurityMonitoringApiGetInvestigationLogQueriesMatchingSignalRe signalId: string; } +export interface SecurityMonitoringApiGetMatchingSignalsRequest { + /** + * The ID of the event to find matching signals for. + * @type string + */ + eventId: string; + /** + * The product track that the event belongs to. + * @type string + */ + track: string; +} + export interface SecurityMonitoringApiGetResourceEvaluationFiltersRequest { /** * Filter resource filters by cloud provider (e.g. aws, gcp, azure). @@ -27210,6 +27359,28 @@ export class SecurityMonitoringApi { }); } + /** + * Returns the list of security signals that match a given event on the given track. + * @param param The request object + */ + public getMatchingSignals( + param: SecurityMonitoringApiGetMatchingSignalsRequest, + options?: Configuration, + ): Promise { + const requestContextPromise = this.requestFactory.getMatchingSignals( + param.eventId, + param.track, + options, + ); + return requestContextPromise.then((requestContext) => { + return this.configuration.httpApi + .send(requestContext) + .then((responseContext) => { + return this.responseProcessor.getMatchingSignals(responseContext); + }); + }); + } + /** * List resource filters. * @param param The request object diff --git a/services/security_monitoring/src/v2/index.ts b/services/security_monitoring/src/v2/index.ts index 44211a9befb3..b7d2efa87158 100644 --- a/services/security_monitoring/src/v2/index.ts +++ b/services/security_monitoring/src/v2/index.ts @@ -74,6 +74,7 @@ export { SecurityMonitoringApiGetHistoricalJobRequest, SecurityMonitoringApiGetIndicatorOfCompromiseRequest, SecurityMonitoringApiGetInvestigationLogQueriesMatchingSignalRequest, + SecurityMonitoringApiGetMatchingSignalsRequest, SecurityMonitoringApiGetResourceEvaluationFiltersRequest, SecurityMonitoringApiGetRuleVersionHistoryRequest, SecurityMonitoringApiGetSBOMRequest, @@ -445,6 +446,10 @@ export { ListHistoricalJobsResponse } from "./models/ListHistoricalJobsResponse" export { ListSecurityFindingsResponse } from "./models/ListSecurityFindingsResponse"; export { ListVulnerabilitiesResponse } from "./models/ListVulnerabilitiesResponse"; export { ListVulnerableAssetsResponse } from "./models/ListVulnerableAssetsResponse"; +export { MatchingSignalAttributes } from "./models/MatchingSignalAttributes"; +export { MatchingSignalData } from "./models/MatchingSignalData"; +export { MatchingSignalsResponse } from "./models/MatchingSignalsResponse"; +export { MatchingSignalType } from "./models/MatchingSignalType"; export { Metadata } from "./models/Metadata"; export { MuteDataType } from "./models/MuteDataType"; export { MuteFindingsMuteAttributes } from "./models/MuteFindingsMuteAttributes"; diff --git a/services/security_monitoring/src/v2/models/MatchingSignalAttributes.ts b/services/security_monitoring/src/v2/models/MatchingSignalAttributes.ts new file mode 100644 index 000000000000..55f7fca38869 --- /dev/null +++ b/services/security_monitoring/src/v2/models/MatchingSignalAttributes.ts @@ -0,0 +1,73 @@ +import { AttributeTypeMap } from "@datadog/datadog-api-client"; + +/** + * Attributes of a matching security signal. + */ +export class MatchingSignalAttributes { + /** + * The tracker ID linking the signal back to the originating event. Distinct from `id`, which identifies the matching signal itself. + */ + "eventTrackerId": string; + /** + * The severity of the signal. + */ + "severity": string; + /** + * The title of the signal. + */ + "title": string; + /** + * The Unix timestamp (in milliseconds) at which the signal was triggered. + */ + "triggerTimeMs": number; + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + "additionalProperties"?: { [key: string]: any }; + /** + * @ignore + */ + "_unparsed"?: boolean; + + /** + * @ignore + */ + static readonly attributeTypeMap: AttributeTypeMap = { + eventTrackerId: { + baseName: "event_tracker_id", + type: "string", + required: true, + }, + severity: { + baseName: "severity", + type: "string", + required: true, + }, + title: { + baseName: "title", + type: "string", + required: true, + }, + triggerTimeMs: { + baseName: "trigger_time_ms", + type: "number", + required: true, + format: "int64", + }, + additionalProperties: { + baseName: "additionalProperties", + type: "{ [key: string]: any; }", + }, + }; + + /** + * @ignore + */ + static getAttributeTypeMap(): AttributeTypeMap { + return MatchingSignalAttributes.attributeTypeMap; + } + + public constructor() {} +} diff --git a/services/security_monitoring/src/v2/models/MatchingSignalData.ts b/services/security_monitoring/src/v2/models/MatchingSignalData.ts new file mode 100644 index 000000000000..692e067ec484 --- /dev/null +++ b/services/security_monitoring/src/v2/models/MatchingSignalData.ts @@ -0,0 +1,66 @@ +import { AttributeTypeMap } from "@datadog/datadog-api-client"; + +import { MatchingSignalAttributes } from "./MatchingSignalAttributes"; +import { MatchingSignalType } from "./MatchingSignalType"; + +/** + * A security signal that matches the queried event. + */ +export class MatchingSignalData { + /** + * Attributes of a matching security signal. + */ + "attributes": MatchingSignalAttributes; + /** + * The ID of the matching signal. + */ + "id": string; + /** + * The type of the resource. The value should always be `matching_signal`. + */ + "type": MatchingSignalType; + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + "additionalProperties"?: { [key: string]: any }; + /** + * @ignore + */ + "_unparsed"?: boolean; + + /** + * @ignore + */ + static readonly attributeTypeMap: AttributeTypeMap = { + attributes: { + baseName: "attributes", + type: "MatchingSignalAttributes", + required: true, + }, + id: { + baseName: "id", + type: "string", + required: true, + }, + type: { + baseName: "type", + type: "MatchingSignalType", + required: true, + }, + additionalProperties: { + baseName: "additionalProperties", + type: "{ [key: string]: any; }", + }, + }; + + /** + * @ignore + */ + static getAttributeTypeMap(): AttributeTypeMap { + return MatchingSignalData.attributeTypeMap; + } + + public constructor() {} +} diff --git a/services/security_monitoring/src/v2/models/MatchingSignalType.ts b/services/security_monitoring/src/v2/models/MatchingSignalType.ts new file mode 100644 index 000000000000..1e16f1dad07b --- /dev/null +++ b/services/security_monitoring/src/v2/models/MatchingSignalType.ts @@ -0,0 +1,7 @@ +import { UnparsedObject } from "@datadog/datadog-api-client"; + +/** + * The type of the resource. The value should always be `matching_signal`. + */ +export type MatchingSignalType = typeof MATCHING_SIGNAL | UnparsedObject; +export const MATCHING_SIGNAL = "matching_signal"; diff --git a/services/security_monitoring/src/v2/models/MatchingSignalsResponse.ts b/services/security_monitoring/src/v2/models/MatchingSignalsResponse.ts new file mode 100644 index 000000000000..b585ce0618c4 --- /dev/null +++ b/services/security_monitoring/src/v2/models/MatchingSignalsResponse.ts @@ -0,0 +1,47 @@ +import { AttributeTypeMap } from "@datadog/datadog-api-client"; + +import { MatchingSignalData } from "./MatchingSignalData"; + +/** + * Response containing the list of security signals matching an event. + */ +export class MatchingSignalsResponse { + /** + * Array of matching signals. + */ + "data": Array; + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + "additionalProperties"?: { [key: string]: any }; + /** + * @ignore + */ + "_unparsed"?: boolean; + + /** + * @ignore + */ + static readonly attributeTypeMap: AttributeTypeMap = { + data: { + baseName: "data", + type: "Array", + required: true, + }, + additionalProperties: { + baseName: "additionalProperties", + type: "{ [key: string]: any; }", + }, + }; + + /** + * @ignore + */ + static getAttributeTypeMap(): AttributeTypeMap { + return MatchingSignalsResponse.attributeTypeMap; + } + + public constructor() {} +} diff --git a/services/security_monitoring/src/v2/models/TypingInfo.ts b/services/security_monitoring/src/v2/models/TypingInfo.ts index bcf0b1ab4294..54db40fdf2e8 100644 --- a/services/security_monitoring/src/v2/models/TypingInfo.ts +++ b/services/security_monitoring/src/v2/models/TypingInfo.ts @@ -240,6 +240,9 @@ import { ListHistoricalJobsResponse } from "./ListHistoricalJobsResponse"; import { ListSecurityFindingsResponse } from "./ListSecurityFindingsResponse"; import { ListVulnerabilitiesResponse } from "./ListVulnerabilitiesResponse"; import { ListVulnerableAssetsResponse } from "./ListVulnerableAssetsResponse"; +import { MatchingSignalAttributes } from "./MatchingSignalAttributes"; +import { MatchingSignalData } from "./MatchingSignalData"; +import { MatchingSignalsResponse } from "./MatchingSignalsResponse"; import { Metadata } from "./Metadata"; import { MuteFindingsMuteAttributes } from "./MuteFindingsMuteAttributes"; import { MuteFindingsRequest } from "./MuteFindingsRequest"; @@ -673,6 +676,7 @@ export const TypingInfo: ModelTypingInfo = { IoCTriageState: ["not_reviewed", "reviewed"], JiraIssuesDataType: ["jira_issues"], LinearIssuesDataType: ["linear_issues"], + MatchingSignalType: ["matching_signal"], MuteDataType: ["mute"], MuteFindingsReason: [ "PENDING_FIX", @@ -1447,6 +1451,9 @@ export const TypingInfo: ModelTypingInfo = { ListSecurityFindingsResponse: ListSecurityFindingsResponse, ListVulnerabilitiesResponse: ListVulnerabilitiesResponse, ListVulnerableAssetsResponse: ListVulnerableAssetsResponse, + MatchingSignalAttributes: MatchingSignalAttributes, + MatchingSignalData: MatchingSignalData, + MatchingSignalsResponse: MatchingSignalsResponse, Metadata: Metadata, MuteFindingsMuteAttributes: MuteFindingsMuteAttributes, MuteFindingsRequest: MuteFindingsRequest,