Skip to content

Commit 197eb82

Browse files
authored
add should_create_single_alert_for_findings field to security-analytics (#757)
Signed-off-by: Subhobrata Dey <[email protected]>
1 parent 0907d1a commit 197eb82

File tree

4 files changed

+86
-4
lines changed

4 files changed

+86
-4
lines changed

src/main/kotlin/org/opensearch/commons/alerting/model/IndexExecutionContext.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ data class IndexExecutionContext(
2121
val updatedIndexNames: List<String>,
2222
val concreteIndexNames: List<String>,
2323
val conflictingFields: List<String>,
24-
val docIds: List<String>? = emptyList()
24+
val docIds: List<String>? = emptyList(),
25+
val findingIds: List<String>? = emptyList()
2526
) : Writeable, ToXContent {
2627

2728
@Throws(IOException::class)
@@ -34,7 +35,8 @@ data class IndexExecutionContext(
3435
updatedIndexNames = sin.readStringList(),
3536
concreteIndexNames = sin.readStringList(),
3637
conflictingFields = sin.readStringList(),
37-
docIds = sin.readOptionalStringList()
38+
docIds = sin.readOptionalStringList(),
39+
findingIds = sin.readOptionalStringList()
3840
)
3941

4042
override fun writeTo(out: StreamOutput?) {
@@ -47,6 +49,7 @@ data class IndexExecutionContext(
4749
out.writeStringCollection(concreteIndexNames)
4850
out.writeStringCollection(conflictingFields)
4951
out.writeOptionalStringCollection(docIds)
52+
out.writeOptionalStringCollection(findingIds)
5053
}
5154

5255
override fun toXContent(builder: XContentBuilder?, params: ToXContent.Params?): XContentBuilder {
@@ -60,6 +63,7 @@ data class IndexExecutionContext(
6063
.field("concrete_index_names", concreteIndexNames)
6164
.field("conflicting_fields", conflictingFields)
6265
.field("doc_ids", docIds)
66+
.field("finding_ids", findingIds)
6367
.endObject()
6468
return builder
6569
}

src/main/kotlin/org/opensearch/commons/alerting/model/Monitor.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ data class Monitor(
4343
val uiMetadata: Map<String, Any>,
4444
val dataSources: DataSources = DataSources(),
4545
val deleteQueryIndexInEveryRun: Boolean? = false,
46+
val shouldCreateSingleAlertForFindings: Boolean? = false,
4647
val owner: String? = "alerting"
4748
) : ScheduledJob {
4849

@@ -112,6 +113,7 @@ data class Monitor(
112113
DataSources()
113114
},
114115
deleteQueryIndexInEveryRun = sin.readOptionalBoolean(),
116+
shouldCreateSingleAlertForFindings = sin.readOptionalBoolean(),
115117
owner = sin.readOptionalString()
116118
)
117119

@@ -172,6 +174,7 @@ data class Monitor(
172174
if (uiMetadata.isNotEmpty()) builder.field(UI_METADATA_FIELD, uiMetadata)
173175
builder.field(DATA_SOURCES_FIELD, dataSources)
174176
builder.field(DELETE_QUERY_INDEX_IN_EVERY_RUN_FIELD, deleteQueryIndexInEveryRun)
177+
builder.field(SHOULD_CREATE_SINGLE_ALERT_FOR_FINDINGS_FIELD, shouldCreateSingleAlertForFindings)
175178
builder.field(OWNER_FIELD, owner)
176179
if (params.paramAsBoolean("with_type", false)) builder.endObject()
177180
return builder.endObject()
@@ -224,6 +227,7 @@ data class Monitor(
224227
out.writeBoolean(dataSources != null) // for backward compatibility with pre-existing monitors which don't have datasources field
225228
dataSources.writeTo(out)
226229
out.writeOptionalBoolean(deleteQueryIndexInEveryRun)
230+
out.writeOptionalBoolean(shouldCreateSingleAlertForFindings)
227231
out.writeOptionalString(owner)
228232
}
229233

@@ -245,6 +249,7 @@ data class Monitor(
245249
const val DATA_SOURCES_FIELD = "data_sources"
246250
const val ENABLED_TIME_FIELD = "enabled_time"
247251
const val DELETE_QUERY_INDEX_IN_EVERY_RUN_FIELD = "delete_query_index_in_every_run"
252+
const val SHOULD_CREATE_SINGLE_ALERT_FOR_FINDINGS_FIELD = "should_create_single_alert_for_findings"
248253
const val OWNER_FIELD = "owner"
249254
val MONITOR_TYPE_PATTERN = Pattern.compile("[a-zA-Z0-9_]{5,25}")
250255

@@ -274,6 +279,7 @@ data class Monitor(
274279
val inputs: MutableList<Input> = mutableListOf()
275280
var dataSources = DataSources()
276281
var deleteQueryIndexInEveryRun = false
282+
var delegateMonitor = false
277283
var owner = "alerting"
278284

279285
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.currentToken(), xcp)
@@ -332,6 +338,11 @@ data class Monitor(
332338
} else {
333339
xcp.booleanValue()
334340
}
341+
SHOULD_CREATE_SINGLE_ALERT_FOR_FINDINGS_FIELD -> delegateMonitor = if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) {
342+
delegateMonitor
343+
} else {
344+
xcp.booleanValue()
345+
}
335346
OWNER_FIELD -> owner = if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) owner else xcp.text()
336347
else -> {
337348
xcp.skipChildren()
@@ -360,6 +371,7 @@ data class Monitor(
360371
uiMetadata,
361372
dataSources,
362373
deleteQueryIndexInEveryRun,
374+
delegateMonitor,
363375
owner
364376
)
365377
}

src/main/kotlin/org/opensearch/commons/alerting/model/WorkflowRunContext.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ data class WorkflowRunContext(
1818
val workflowMetadataId: String,
1919
val chainedMonitorId: String?,
2020
val matchingDocIdsPerIndex: Map<String, List<String>>,
21-
val auditDelegateMonitorAlerts: Boolean
21+
val auditDelegateMonitorAlerts: Boolean,
22+
val findingIds: List<String>? = null
2223
) : Writeable, ToXContentObject {
2324
companion object {
2425
fun readFrom(sin: StreamInput): WorkflowRunContext {
@@ -31,7 +32,8 @@ data class WorkflowRunContext(
3132
sin.readString(),
3233
sin.readOptionalString(),
3334
sin.readMap() as Map<String, List<String>>,
34-
sin.readBoolean()
35+
sin.readBoolean(),
36+
sin.readOptionalStringList()
3537
)
3638

3739
override fun writeTo(out: StreamOutput) {
@@ -40,6 +42,7 @@ data class WorkflowRunContext(
4042
out.writeOptionalString(chainedMonitorId)
4143
out.writeMap(matchingDocIdsPerIndex)
4244
out.writeBoolean(auditDelegateMonitorAlerts)
45+
out.writeOptionalStringCollection(findingIds)
4346
}
4447

4548
override fun toXContent(builder: XContentBuilder, params: ToXContent.Params?): XContentBuilder {
@@ -49,6 +52,7 @@ data class WorkflowRunContext(
4952
.field("chained_monitor_id", chainedMonitorId)
5053
.field("matching_doc_ids_per_index", matchingDocIdsPerIndex)
5154
.field("audit_delegate_monitor_alerts", auditDelegateMonitorAlerts)
55+
.field("finding_ids", findingIds)
5256
.endObject()
5357
return builder
5458
}

src/test/kotlin/org/opensearch/commons/alerting/action/DocLevelMonitorFanOutRequestTests.kt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,66 @@ class DocLevelMonitorFanOutRequestTests {
8989
assertEquals(docLevelMonitorFanOutRequest.shardIds, newDocLevelMonitorFanOutRequest.shardIds)
9090
assertEquals(docLevelMonitorFanOutRequest.workflowRunContext, newDocLevelMonitorFanOutRequest.workflowRunContext)
9191
}
92+
93+
@Test
94+
fun `test doc level monitor fan out request as stream with matching docIds with findings per index`() {
95+
val docQuery = DocLevelQuery(query = "test_field:\"us-west-2\"", fields = listOf(), name = "3")
96+
val docLevelInput = DocLevelMonitorInput("description", listOf("test-index"), listOf(docQuery))
97+
98+
val trigger = randomDocumentLevelTrigger(condition = Script("return true"))
99+
val monitor = randomDocumentLevelMonitor(
100+
inputs = listOf(docLevelInput),
101+
triggers = listOf(trigger),
102+
enabled = true,
103+
schedule = IntervalSchedule(1, ChronoUnit.MINUTES)
104+
)
105+
val monitorMetadata = MonitorMetadata(
106+
"test",
107+
SequenceNumbers.UNASSIGNED_SEQ_NO,
108+
SequenceNumbers.UNASSIGNED_PRIMARY_TERM,
109+
Monitor.NO_ID,
110+
listOf(ActionExecutionTime("", Instant.now())),
111+
mutableMapOf("index" to mutableMapOf("1" to "1")),
112+
mutableMapOf("test-index" to ".opensearch-sap-test_windows-queries-000001")
113+
)
114+
val indexExecutionContext = IndexExecutionContext(
115+
listOf(docQuery),
116+
mutableMapOf("index" to mutableMapOf("1" to "1")),
117+
mutableMapOf("index" to mutableMapOf("1" to "1")),
118+
"test-index",
119+
"test-index",
120+
listOf("test-index"),
121+
listOf("test-index"),
122+
listOf("test-field"),
123+
listOf("1", "2")
124+
)
125+
val workflowRunContext = WorkflowRunContext(
126+
Workflow.NO_ID,
127+
Workflow.NO_ID,
128+
Monitor.NO_ID,
129+
mutableMapOf("index" to listOf("1")),
130+
true,
131+
listOf("finding1")
132+
)
133+
val docLevelMonitorFanOutRequest = DocLevelMonitorFanOutRequest(
134+
monitor,
135+
false,
136+
monitorMetadata,
137+
UUID.randomUUID().toString(),
138+
indexExecutionContext,
139+
listOf(ShardId("test-index", UUID.randomUUID().toString(), 0)),
140+
listOf("test-index"),
141+
workflowRunContext
142+
)
143+
val out = BytesStreamOutput()
144+
docLevelMonitorFanOutRequest.writeTo(out)
145+
val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes)
146+
val newDocLevelMonitorFanOutRequest = DocLevelMonitorFanOutRequest(sin)
147+
assertEquals(docLevelMonitorFanOutRequest.monitor, newDocLevelMonitorFanOutRequest.monitor)
148+
assertEquals(docLevelMonitorFanOutRequest.executionId, newDocLevelMonitorFanOutRequest.executionId)
149+
assertEquals(docLevelMonitorFanOutRequest.monitorMetadata, newDocLevelMonitorFanOutRequest.monitorMetadata)
150+
assertEquals(docLevelMonitorFanOutRequest.indexExecutionContext, newDocLevelMonitorFanOutRequest.indexExecutionContext)
151+
assertEquals(docLevelMonitorFanOutRequest.shardIds, newDocLevelMonitorFanOutRequest.shardIds)
152+
assertEquals(docLevelMonitorFanOutRequest.workflowRunContext, newDocLevelMonitorFanOutRequest.workflowRunContext)
153+
}
92154
}

0 commit comments

Comments
 (0)