This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
cyber-narrator-cn-ui/src/components/table/detection/RuleDefinition.vue
2023-10-30 11:14:58 +08:00

542 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<div v-if="mySettingObj.ruleType===detectionRuleType.threshold" style="display: flex;justify-content: space-between;">
<div>
<el-form ref="form" :model="thresholdRuleObj" label-position="top" :rules="rules">
<!--source-->
<el-form-item :label="$t('config.user.source')" prop="dataSource" class="form-setting__block margin-b-20">
<el-select v-model="thresholdRuleObj.dataSource" placeholder=" " size="mini" class="form-setting__select">
<el-option
v-for="item in sourceList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
<!--Dimensions-->
<div class="form-setting__block margin-b-20">
<div class="block-title">{{ $t('detection.create.dimensions') }}</div>
<div class="block-dimension">
<div class="block-dimension-tag" v-for="(ite, ind) in dimensionList" :key="ind">{{ ite.label }}</div>
</div>
</div>
<div class="form-setting__block form-setting__block-key">
<div>Key Selection</div>
<div class="block-key">
<!--todo 图标暂无需要更换-->
<i class="cn-icon cn-icon-shijianjihua"></i>
<span @click="showDrawer=true">History Top Keys</span>
</div>
</div>
<!--Filters模块-->
<div class="form-setting__block margin-b-20">
<div class="block-title1">{{ $t('detections.filters') }}</div>
<div class="definition-filter-block" v-if="showFilter">
<div class="definition-filter-item" v-for="(item, index) in thresholdRuleObj.filterList" :key="index">
<el-select class="filter-item__select margin-r-8" v-model="item.filter" placeholder=" " size="mini">
<el-option
v-for="item in selectList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-input class="filter-item__input margin-r-8" size="mini" disabled placeholder="equal"></el-input>
<el-input
class="filter-item__input margin-r-8"
size="mini"
oninput="value=value.replace(/[^\d]/g,'')"
v-model="item.value"></el-input>
<i class="cn-icon cn-icon-close" @click="delFilterItem(index, item)"></i>
</div>
<div style="height: 10px;"></div>
<div class="filter-block-footer">
<i class="cn-icon cn-icon-add" @click="addFilter"></i>
</div>
</div>
<div v-else class="block-filter-add" @click="addFilter">+</div>
</div>
<!--Condition模块-->
<div class="form-setting__block margin-b-20">
<div class="block-title">{{ $t('detection.create.condition') }}</div>
<el-form ref="form2" :model="thresholdRuleObj" label-position="top">
<div class="definition-condition-block" v-for="(item, index) in thresholdRuleObj.conditionData" :key="index">
<el-form-item :label="$t('detection.level')" :prop="`conditionData.${index}.level`" :rules="rules.level">
<el-select class="condition__select margin-b-20" v-model="item.level" placeholder=" " size="mini">
<template #prefix>
<div
class="condition__select__icon"
:style="{background: eventSeverityColor[item.level]}"></div>
</template>
<el-option
v-for="item in levelList"
:key="item.label"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
/>
</el-select>
</el-form-item>
<div class="condition-metric" v-for="(data, i) in item.list" :key="i">
<div class="condition-metric-item1">
<div class="metric-item1__text">
<span style="margin-right: 9px;">If</span>
<el-form-item :prop="`conditionData.${index}.list.${i}.metric`" :rules="rules.metric">
<el-select v-model="data.metric" placeholder=" " size="mini">
<el-option
v-for="item in metricList"
:key="item.label"
:label="item.label"
:value="item.label"
/>
</el-select>
</el-form-item>
<span style="margin-left: 9px;">of keys</span>
</div>
<div>
<i class="cn-icon cn-icon-add" @click="addConditionItem(index)"></i>
<i
class="cn-icon cn-icon-close"
:class="delConditionDisabled ? 'metric-item1-close-disable' : 'metric-item1-close'"
@click="delConditionItem(index, i)">
</i>
</div>
</div>
<div class="condition-metric-item2">
<div style="height: 24px;line-height: 24px;display: flex;">
<el-form-item :prop="`conditionData.${index}.list.${i}.condition`" :rules="rules.condition">
<el-select class="metric-item2__select" v-model="data.condition" placeholder=" " size="mini">
<el-option
v-for="item in conditionList"
:key="item.label"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :prop="`conditionData.${index}.list.${i}.value`" :rules="rules.value">
<!--todo 应当添加长度限制-->
<el-input
class="metric-item2__input"
v-model.number="data.value"
oninput="value=value.replace(/[^\d]/g,'')"
size="mini"></el-input>
</el-form-item>
</div>
<span>{{ data.metric }}</span>
</div>
<el-divider v-if="item.list.length - 1 > i" class="condition-divider">and</el-divider>
</div>
</div>
</el-form>
<div class="condition-add" @click="addCondition">
<i class="cn-icon cn-icon-add"></i>{{ $t('detection.create.addCondition') }}
</div>
</div>
</div>
<!--History Top Keys弹窗-->
<div class="key-drawer">
<history-top-keys
v-if="showDrawer"
:showDrawer="showDrawer"
:delKeyId="delKeyId"
@closeDrawer="onCloseDrawer"
@keyRowClick="getRowClick"
></history-top-keys>
</div>
</div>
<div v-if="mySettingObj.ruleType===detectionRuleType.indicator">
<el-form ref="form" :model="indicatorRuleObj" label-position="top" :rules="rules">
<!--Source-->
<el-form-item :label="$t('config.user.source')" prop="dataSource" class="form-setting__block margin-b-20">
<el-select v-model="indicatorRuleObj.dataSource" class="form-setting__select" placeholder=" " size="mini" @change="handleParamsComplete">
<el-option
v-for="item in sourceList"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
</el-form-item>
<!--Library-->
<el-form-item :label="$t('detection.library')" prop="knowledgeId" class="form-setting__block margin-b-20">
<el-select v-model="indicatorRuleObj.knowledgeId" class="form-setting__select" filterable placeholder=" " size="mini" @change="handleParamsComplete">
<el-option
v-for="item in libraryList"
:key="item.knowledgeId"
:label="item.name"
:value="item.knowledgeId"
/>
</el-select>
</el-form-item>
<!--Level-->
<el-form-item :label="$t('detection.level')" prop="level" class="form-setting__block">
<el-select v-model="indicatorRuleObj.level" class="condition__select form-setting__select" placeholder=" " size="mini" @change="handleParamsComplete">
<template #prefix>
<div
class="condition__select__icon"
:style="{background: eventSeverityColor[indicatorRuleObj.level]}"></div>
</template>
<el-option
v-for="item in levelList"
:key="item.label"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
</div>
<div class="form-setting__btn">
<el-button @click="onContinue">{{ $t('detection.create.continue') }}</el-button>
</div>
</div>
</template>
<script>
import HistoryTopKeys from '@/components/table/detection/HistoryTopKeys'
import { eventSeverityColor, detectionRuleType, detectionUnitList, securityLevel } from '@/utils/constants'
import axios from 'axios'
import _ from 'lodash'
import { api } from '@/utils/api'
export default {
name: 'RuleDefinition',
props: {
settingObj: {
type: Object
},
editObj: {
type: Object
},
isComplete: {
type: Boolean
}
},
components: {
HistoryTopKeys
},
watch: {
settingObj: {
immediate: true,
deep: true,
handler (newVal) {
if (!newVal.editFlag && newVal.saveFlag) {
this.mySettingObj = JSON.parse(JSON.stringify(newVal))
}
}
},
editObj: {
immediate: true,
deep: true,
handler (newVal) {
if (newVal) {
if (newVal.ruleType === detectionRuleType.indicator) {
this.indicatorRuleObj = this.$_.cloneDeep(newVal.ruleConfigObj)
this.indicatorRuleObj.knowledgeId = newVal.ruleConfigObj.knowledgeBase.knowledgeId
}
if (newVal.ruleType === detectionRuleType.threshold) {
this.thresholdRuleObj = this.$_.cloneDeep(newVal.ruleConfigObj)
}
}
}
},
isComplete (newVal) {
if (!newVal) {
this.onContinue()
}
}
},
data () {
return {
eventSeverityColor,
detectionRuleType,
mySettingObj: {
ruleType: detectionRuleType.indicator
},
dimensionList: [], // Dimensions数据
// ruleType为Indicator时表单数据
indicatorRuleObj: {
dataSource: '',
knowledgeId: '',
level: ''
},
// ruleType为Threshold时表单数据
thresholdRuleObj: {
dataSource: '',
dimensionKeys: '',
filterList: [],
filters: '', // filter提交到接口的数据即filterList转化为字符串
conditionData: [
{
level: '',
list: [
{ metric: '', condition: '', value: '' }
]
}
],
conditions: {} // filter提交到接口的数据即filterList转化为字符串
},
rules: {
dataSource: [
{
required: true,
message: this.$t('validate.required'),
trigger: 'change'
}
],
level: [
{
required: true,
message: this.$t('validate.required'),
trigger: 'change'
}
],
metric: [
{
required: true,
message: this.$t('validate.required'),
trigger: 'change'
}
],
condition: [
{
required: true,
message: this.$t('validate.required'),
trigger: 'change'
}
],
value: [
{
required: true,
message: this.$t('validate.required'),
trigger: 'blur'
}
],
knowledgeId: [
{
required: true,
message: this.$t('validate.required'),
trigger: 'blur'
}
]
},
sourceList: [], // source下拉列表数据
// rule的policy创建信息
showDrawer: false, // 显示History Top Keys抽屉弹框
showFilter: false, // 显示filter筛选框点击add显示
selectList: [], // filter的第一个下拉列表
levelList: [], // condition的level下拉列表
metricList: [],
conditionList: [],
libraryList: [],
delConditionDisabled: true, // condition删除标识true表示禁止删除即只有一个condition时
unitObj: {
than: '>',
less: '<',
equal: '='
},
delKeyId: '' // 删除的keyId
}
},
mounted () {
this.initData()
// todo 调用接口进行赋值
this.dimensionList = [
{ label: 'Destination IP/CIDR', value: 'Destination IP/CIDR' },
{ label: 'Source Port Number', value: 'Source Port Number' }
]
},
methods: {
initData () {
this.sourceList = detectionUnitList.sourceList || []
this.levelList = securityLevel || []
// threshold模式还没确定所以数据暂时静态数据后续根据需要修改
this.conditionList = detectionUnitList.conditionList || []
this.metricList = detectionUnitList.metricList || []
if (this.mySettingObj.ruleType === this.detectionRuleType.indicator) {
axios.get(api.knowledgeBaseList, { params: { pageSize: -1 } }).then(response => {
if (response.status === 200) {
this.libraryList = _.get(response, 'data.data.list', []).filter(l => l.isBuiltIn === 0)
} else {
console.error(response.data.message)
this.libraryList = []
if (response.data.message) {
this.$message.error(response.data.message)
} else {
this.$message.error(this.$t('tip.somethingWentWrong'))
}
}
}).catch(e => {
console.error(e)
this.libraryList = []
this.$message.error(this.errorMsgHandler(e))
})
}
},
/** 单击History Top Keys列表某行filter添加数据 */
getRowClick (data) {
this.addFilter(data)
},
/** 关闭History Top Keys弹框 */
onCloseDrawer () {
this.showDrawer = false
},
/** filter模块点击add按钮 */
addFilter (data) {
this.delKeyId = ''
this.showFilter = true
if (this.selectList.length === 0) {
this.getFilterList()
}
// 添加数据前,先删除空白项
const delIndex = this.thresholdRuleObj.filterList.findIndex(t => t.filter === '')
if (delIndex > -1) {
this.thresholdRuleObj.filterList.splice(delIndex, 1)
}
if (data.metric) {
// 从key弹框添加数据
const obj = this.thresholdRuleObj.filterList.find(t => t.keyId === data.keyId)
if (!obj) {
this.thresholdRuleObj.filterList.push({ keyId: data.keyId, filter: this.selectList[0].label, value: data.metric })
}
} else {
// 手动添加
this.thresholdRuleObj.filterList.push({ filter: '', value: '' })
}
},
/** 添加condition大模块 */
addCondition () {
this.$refs.form2.validate(valid => {
if (valid) {
// 如果选择了level则禁用这一条level不允许再选择除非删除才能选择
this.levelList.forEach(item => {
const obj = this.thresholdRuleObj.conditionData.find(t => t.level === item.value)
if (obj) {
item.disabled = true
}
})
this.thresholdRuleObj.conditionData.push({ level: '', list: [{ metric: '', condition: '', value: '' }] })
this.delConditionDisabled = false
}
})
},
/** 添加condition小模块 */
addConditionItem (index) {
this.$refs.form2.validate(valid => {
if (valid) {
this.thresholdRuleObj.conditionData[index].list.push({ metric: '', condition: '', value: '' })
this.delConditionDisabled = false
}
})
},
/**
* 删除condition小模块只剩一个时点击删除则删除大模块
* 只有一个大模块时,只剩一个小模块则不可删除
* */
delConditionItem (index, i) {
if (!this.delConditionDisabled) {
const dataLen = this.thresholdRuleObj.conditionData.length
const listLen = this.thresholdRuleObj.conditionData[index].list.length
if (dataLen === 1) {
if (listLen >= 2) {
this.thresholdRuleObj.conditionData[index].list.splice(i, 1)
}
if (this.thresholdRuleObj.conditionData[index].list.length === 1) {
this.delConditionDisabled = true
}
} else {
if (listLen === 1) {
this.thresholdRuleObj.conditionData.splice(index, 1)
} else {
this.thresholdRuleObj.conditionData[index].list.splice(i, 1)
}
}
}
},
/** 获取filter模块下拉列表 */
getFilterList () {
// todo 请求接口
this.selectList = [
{ value: 'Destination As Number', label: 'Destination As Number' },
{ value: 'Destination As String', label: 'Destination As String' }
]
},
/** 删除filter某一项 */
delFilterItem (i, item) {
this.delKeyId = item.keyId
this.thresholdRuleObj.filterList.splice(i, 1)
},
/** 点击继续,展开第三步 */
onContinue (e) {
if (e) {
delete this.indicatorRuleObj.ruleNoContinue
}
this.$refs.form.validate(valid => {
if (valid) {
if (this.mySettingObj.ruleType === detectionRuleType.indicator) {
// 第一步模式选择Indicator Match
this.$emit('setRuleObj', this.indicatorRuleObj)
} else {
// 第一步模式选择Threshold
this.$refs.form2.validate(valid2 => {
if (valid2) {
this.getConditions()
this.$emit('setRuleObj', this.thresholdRuleObj)
}
})
}
}
})
},
/** 将condition的数组转换为入参需要的字符串 */
getConditions () {
const conditionData = this.thresholdRuleObj.conditionData
const obj = {}
conditionData.forEach(item => {
obj[item.level] = ''
let str = ''
item.list.forEach(t => {
str = str + t.metric + ' ' + this.unitObj[t.condition] + ' ' + t.value + ' && '
})
str = str.substring(0, str.length - 4)
obj[item.level] = str
})
this.thresholdRuleObj.conditions = obj
},
handleParamsComplete () {
if (this.indicatorRuleObj.dataSource && this.indicatorRuleObj.knowledgeId && this.indicatorRuleObj.level) {
this.indicatorRuleObj.ruleNoContinue = true
this.onContinue()
}
}
}
}
</script>