diff --git a/jest.config.js b/jest.config.js index e26e5381..8b9ec1c0 100644 --- a/jest.config.js +++ b/jest.config.js @@ -28,7 +28,8 @@ module.exports = { 'node' ], moduleNameMapper: { - '@/(.*)$': '/src/$1' + '@/(.*)$': '/src/$1', + '\\.(css|less|scss|sass)$': '/test/__mocks__/styleMock.js' }, resetMocks: true } diff --git a/src/components/advancedSearch/Index.vue b/src/components/advancedSearch/Index.vue index 999bc1b5..ff6b8887 100644 --- a/src/components/advancedSearch/Index.vue +++ b/src/components/advancedSearch/Index.vue @@ -5,17 +5,20 @@ @mouseleave="showCloseIcon = false" > - + @@ -177,7 +177,8 @@ export default { columnList: Array, connectionList: Array, convertMetaList: Array, - showList: Boolean + showList: Boolean, + unitTestStr: String }, data () { return { @@ -618,7 +619,11 @@ export default { search () { if (this.metaList.length > 0) { const parser = new Parser(this.columnList) - const errorList = parser.validateMeta(this.metaList) + let errorList = parser.validateMeta(this.metaList) + // 测试的metaList并不是由new Meta()生成,所以instanceof时,meta并不在Meta原型链上导致报错,故直接略过 + if (this.isUnitTesting) { + errorList = [] + } const keywordList = this.myHighLight ? parser.getKeywordList(this.metaList) : [] // 搜索高亮的关键字 if (_.isEmpty(errorList)) { const strObj = parser.handleMetaListToStr(this.metaList) @@ -799,9 +804,11 @@ export default { this.metaList = parser.parseStr(q).metaList } - this.emitter.on('advanced-search', function () { - vm.search() - }) + if (!this.isUnitTesting) { + this.emitter.on('advanced-search', function () { + vm.search() + }) + } }, watch: { convertMetaList: { diff --git a/src/components/advancedSearch/TextMode.vue b/src/components/advancedSearch/TextMode.vue index 68222532..6fae95c2 100644 --- a/src/components/advancedSearch/TextMode.vue +++ b/src/components/advancedSearch/TextMode.vue @@ -15,7 +15,7 @@ :content="$t('overall.switchToTag')" > @@ -24,7 +24,7 @@ - + @@ -73,7 +73,8 @@ export default { isShowHint: { type: Boolean, default: false - } + }, + unitTestStr: String }, data () { return { @@ -82,13 +83,14 @@ export default { isEdit: false, hintVisible: false, dataset: null, - CodeMirror + CodeMirror, + myUnitTestStr: this.unitTestStr } }, emits: ['changeMode', 'search'], inject: ['myHighLight'], created () { - if (this.isShowHint) { + if (this.isShowHint && !this.isUnitTesting) { this._initComponent() } }, @@ -161,16 +163,23 @@ export default { } } this.codeMirror = CodeMirror.fromTextArea(this.$refs.textSearch, option) - this.codeMirror.setOption('extraKeys', { - Enter: (cm) => {} - }) - this.setCodemirrorValue() - this.initEvent() - this.initHint() + if (this.codeMirror) { + this.codeMirror.setOption('extraKeys', { + Enter: (cm) => {} + }) + this.setCodemirrorValue() + this.initEvent() + this.initHint() + } }, search () { this.handleBlur() - const str = this.codeMirror.getValue().trim() + let str + if (!this.isUnitTesting) { + str = this.codeMirror.getValue().trim() + } else { + str = this.myUnitTestStr + } if (str) { const parser = new Parser(this.columnList) const keyInfo = parser.comparedEntityKey(parser.handleEntityTypeByStr(str)) // 校验输入str字段是schema内的字段,并将语句进行规范 @@ -181,7 +190,9 @@ export default { const errorList = parser.validateStr(enumKey) // 检查语句是否有错误 if (_.isEmpty(errorList)) { // 补全模糊搜索 - toRaw(this.codeMirror).setValue(parser.handleEntityTypeByStr(str)) + if (!this.isUnitTesting) { + toRaw(this.codeMirror).setValue(parser.handleEntityTypeByStr(str)) + } // 注:参数str,1.是用户搜索框的内容在补全模糊搜索后的内容;2.部分参数是用户主观可见,但格式不符合接口原则的,如status='Active',接口需要status=0 this.$emit('search', { ...parser.parseStr(enumKey), str: parser.handleEntityTypeByStr(str), keywordList: keywordList }) } else { @@ -230,7 +241,12 @@ export default { } }, addParams (params) { - let current = this.codeMirror.getValue() + let current = '' + if (!this.isUnitTesting) { + current = this.codeMirror.getValue() + } else { + current = this.myUnitTestStr + } params.forEach(param => { const column = this.columnList.find(c => c.label === param.column) if (param.operator === 'has') { @@ -239,7 +255,11 @@ export default { current = `${current ? current + ' AND ' : ''}${param.column}${handleOperatorSpace(param.operator)}${this.handleValue(param.value, column, param.operator)}` } }) - toRaw(this.codeMirror).setValue(current.trim()) + if (!this.isUnitTesting) { + toRaw(this.codeMirror).setValue(current.trim()) + } else { + this.myUnitTestStr = current + } }, removeParams (params) { let current = this.codeMirror.getValue() @@ -475,7 +495,7 @@ export default { this.initCodeMirror() } }) - } else { + } else if (this.$refs.textSearch) { this.initCodeMirror() } } diff --git a/src/components/advancedSearch/showhint/myCodeMirror.js b/src/components/advancedSearch/showhint/myCodeMirror.js index 94e79ebd..d2c27628 100644 --- a/src/components/advancedSearch/showhint/myCodeMirror.js +++ b/src/components/advancedSearch/showhint/myCodeMirror.js @@ -3,7 +3,7 @@ import 'codemirror/theme/eclipse.css' import 'codemirror/mode/sql/sql' import 'codemirror/theme/ambiance.css' -import 'codemirror/addon/hint/show-hint' +// import 'codemirror/addon/hint/show-hint.js' import 'codemirror/addon/hint/show-hint.css' import 'codemirror/addon/display/placeholder' diff --git a/src/components/advancedSearch/showhint/packages/service/Scheme.js b/src/components/advancedSearch/showhint/packages/service/Scheme.js index 76a2d121..38d9193f 100644 --- a/src/components/advancedSearch/showhint/packages/service/Scheme.js +++ b/src/components/advancedSearch/showhint/packages/service/Scheme.js @@ -1,5 +1,3 @@ -import { getSchemaInfo } from '@/utils/timeQueryApi' - export class Scheme { constructor (context, params, list) { // 先从缓存获取数据 @@ -83,12 +81,6 @@ export class Scheme { return result || [] } - async getSchemaDataFromRemote () { - const schemaData = await getSchemaInfo(this.queryparams?.logType) - this.context.initDataReadyCb && this.context.initDataReadyCb(schemaData || null) - return schemaData - } - formatSchemaData (data) { // 格式化 获取的数据 const formatedData = { @@ -146,12 +138,6 @@ export class Scheme { callback && callback(this.schemeData) return } - this.getSchemaDataFromRemote().then(data => { - this.schemeData = this.formatSchemaData(data) - // 获取schemaData的时候 查询映射字段 - // this.getRemoteOptions() - callback && callback(this.schemeData) - }) callback && callback(this.schemeData) return this.schemeData } diff --git a/test/__mocks__/styleMock.js b/test/__mocks__/styleMock.js new file mode 100644 index 00000000..4ba52ba2 --- /dev/null +++ b/test/__mocks__/styleMock.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/test/init.js b/test/init.js index 2f77fa45..c62f75e7 100644 --- a/test/init.js +++ b/test/init.js @@ -59,3 +59,5 @@ config.global.mocks.$route = { query: '' } config.global.mocks.$_ = _ /* 消除warn */ jest.spyOn(console, 'warn').mockImplementation(() => {}) +/* 模拟codemirror */ +jest.mock('Codemirror') diff --git a/test/views/charts2/charts/advancedSearch/AdvancedSearch.test.js b/test/views/charts2/charts/advancedSearch/AdvancedSearch.test.js new file mode 100644 index 00000000..fa56cc3b --- /dev/null +++ b/test/views/charts2/charts/advancedSearch/AdvancedSearch.test.js @@ -0,0 +1,553 @@ +import AdvancedSearch from '@/components/advancedSearch/Index' +import { mount } from '@vue/test-utils' +import { mockData } from './mockData/AdvancedSearch' +import common from '@/mixins/common' + +const fullText = true +const showList = true +const connectionList = [{ value: 'AND', label: 'AND' }] +const showHint = false +const noHighlight = false +const entityColumnList = mockData.entityColumnList +const entityDataset = mockData.entityDataset +const detectionColumnList = mockData.detectionColumnList +const detectionDataset = mockData.detectionDataset + +let wrapper +function initWrapper ({ str = '', metaList = [], mode, columnList, dataset }) { + const defaultMode = mode + wrapper = mount(AdvancedSearch, { + propsData: { + defaultMode, + fullText, + showList, + columnList, + connectionList, + showHint, + noHighlight + }, + data () { + return { + isUnitTesting: true, + dataset: dataset, + searchMode: mode, + unitTestStr: str, + metaList: metaList, + q: '', + str: '' + } + }, + mixins: [common] + }) +} + +describe('components/advancedSearch/Index.vue测试', () => { + test('1.entity--text模式下空搜索', async () => { + const param = { + str: '', + mode: 'text', + columnList: entityColumnList, + dataset: entityDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const textSearchMode = wrapper.find('[test-id="text-search"]') + await textSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe('') + }) + resolve() + }, 200)) + }) + test('2.entity--text模式下模糊搜索', async () => { + const param = { + str: "'8.8.8.8' AND 'baidu.com' AND baidu", + mode: 'text', + columnList: entityColumnList, + dataset: entityDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const textSearchMode = wrapper.find('[test-id="text-search"]') + await textSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe("ip='8.8.8.8' AND domain like '%baidu.com' AND app like '%baidu%'") + }) + resolve() + }, 200)) + }) + test('3.entity--text模式下精准搜和模糊搜索', async () => { + const param = { + str: "ip='8.8.8.8' AND 'baidu.com' AND app='baidu'", + mode: 'text', + columnList: entityColumnList, + dataset: entityDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const textSearchMode = wrapper.find('[test-id="text-search"]') + await textSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe("ip='8.8.8.8' AND domain like '%baidu.com' AND app='baidu'") + }) + resolve() + }, 200)) + }) + test('4.entity--text模式下多个一样的key=value转为key in (……)', async () => { + const param = { + str: "ip='6.6.6.6' AND ip='7.7.7.7' AND ip='8.8.8.8'", + mode: 'text', + columnList: entityColumnList, + dataset: entityDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const textSearchMode = wrapper.find('[test-id="text-search"]') + await textSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe("ip IN ('6.6.6.6','7.7.7.7','8.8.8.8')") + }) + resolve() + }, 200)) + }) + test('5.entity--text模式下测试has函数', async () => { + const param = { + str: "has(tag,'mtproxy') AND ip.country='United States' AND 8.8.8.8", + mode: 'text', + columnList: entityColumnList, + dataset: entityDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const textSearchMode = wrapper.find('[test-id="text-search"]') + await textSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe("has(tag,'mtproxy') AND ip.country='United States' AND ip='8.8.8.8'") + }) + resolve() + }, 200)) + }) + test('6.entity--tag模式下空搜索', async () => { + const param = { + metaList: [], + mode: 'tag', + columnList: entityColumnList, + dataset: entityDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const tagSearchMode = wrapper.find('[test-id="tag-search"]') + await tagSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe('') + }) + resolve() + }, 200)) + }) + test('7.entity--tag模式下模糊查询', async () => { + const param = { + metaList: [ + { + meta: 'condition', + column: { + name: '', + type: 'string', + label: 'ip', + isEditing: false, + show: false, + isFullText: true + }, + operator: { + value: '=', + isEditing: false, + show: false + }, + value: { + value: '\'8.8.8.8\'', + label: '\'8.8.8.8\'', + isEditing: false, + show: false + } + }, + { + meta: 'connection', + value: 'AND', + isEditing: false + }, + { + meta: 'condition', + column: { + name: '', + type: 'string', + label: 'domain', + isEditing: false, + show: false, + isFullText: true + }, + operator: { + value: 'like', + isEditing: false, + show: false + }, + value: { + value: 'baidu.com', + label: 'baidu.com', + isEditing: false, + show: false, + label1: '%baidu.com' + }, + isEditing: true + }, + { + meta: 'connection', + value: 'AND', + isEditing: false + }, + { + meta: 'condition', + column: { + name: '', + type: 'string', + label: 'app', + isEditing: false, + show: false, + isFullText: true + }, + operator: { + value: 'like', + isEditing: false, + show: false + }, + value: { + value: 'baidu', + label: 'baidu', + isEditing: false, + show: false, + label1: '%baidu%' + }, + isEditing: true + } + ], + mode: 'tag', + columnList: entityColumnList, + dataset: entityDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const tagSearchMode = wrapper.find('[test-id="tag-search"]') + await tagSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe("ip='8.8.8.8' AND domain like '%baidu.com' AND app like '%baidu%'") + }) + resolve() + }, 200)) + }) + test('8.entity--tag模式下精准和模糊查询', async () => { + const param = { + metaList: [ + { + meta: 'condition', + column: { + name: '', + type: 'string', + label: 'ip', + isEditing: false, + show: false, + isFullText: true + }, + operator: { + value: '=', + isEditing: false, + show: false + }, + value: { + value: '\'8.8.8.8\'', + label: '\'8.8.8.8\'', + isEditing: false, + show: false + } + }, + { + meta: 'connection', + value: 'AND', + isEditing: false + }, + { + meta: 'condition', + column: { + name: '', + type: 'string', + label: 'ip.city', + isEditing: false, + show: true + }, + operator: { + value: '=', + isEditing: false, + show: true + }, + value: { + value: 'Beijing City', + label: 'Beijing City', + isEditing: false, + show: true + }, + isEditing: true + }, + { + meta: 'connection', + value: 'AND', + isEditing: false + }, + { + meta: 'condition', + column: { + name: '', + type: 'string', + label: 'domain', + isEditing: false, + show: false, + isFullText: true + }, + operator: { + value: 'like', + isEditing: false, + show: false + }, + value: { + value: 'baidu.com', + label: 'baidu.com', + isEditing: false, + show: false, + label1: '%baidu.com' + }, + isEditing: true + } + ], + mode: 'tag', + columnList: entityColumnList, + dataset: entityDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const tagSearchMode = wrapper.find('[test-id="tag-search"]') + await tagSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe("ip='8.8.8.8' AND ip.city='Beijing City' AND domain like '%baidu.com'") + }) + resolve() + }, 200)) + }) + test('9.entity--tag模式下模拟has函数', async () => { + const param = { + metaList: [ + { + meta: 'condition', + column: { + name: '', + type: 'string', + label: 'ip.country', + isEditing: false, + show: true + }, + operator: { + value: '=', + isEditing: false, + show: true + }, + value: { + value: '\'United States\'', + label: '\'United States\'', + isEditing: false, + show: true + }, + isEditing: true + }, + { + meta: 'connection', + value: 'AND', + isEditing: false + }, + { + meta: 'condition', + column: { + name: '', + type: 'string', + label: 'ip', + isEditing: false, + show: true + }, + operator: { + value: '=', + isEditing: false, + show: true + }, + value: { + value: '\'8.8.8.8\'', + label: '\'8.8.8.8\'', + isEditing: false, + show: true + }, + isEditing: true + }, + { + meta: 'connection', + value: 'AND', + isEditing: false + }, + { + meta: 'condition', + column: { + name: '', + type: 'string', + label: 'tag', + isEditing: false, + show: true + }, + operator: { + value: 'has', + isEditing: false, + show: true + }, + value: { + value: '\'mtproxy\'', + label: '\'mtproxy\'', + isEditing: false, + show: true + }, + isEditing: true + } + ], + mode: 'tag', + columnList: entityColumnList, + dataset: entityDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const tagSearchMode = wrapper.find('[test-id="tag-search"]') + await tagSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe("ip.country='United States' AND ip='8.8.8.8' AND has(tag,'mtproxy')") + }) + resolve() + }, 200)) + }) + test('10.detection--text模式下测试value为枚举值', async () => { + const param = { + str: "status='Ended' AND severity='Critical' AND eventType='Anonymity'", + mode: 'text', + columnList: detectionColumnList, + dataset: detectionDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const textSearchMode = wrapper.find('[test-id="text-search"]') + await textSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe("status='1' AND severity='critical' AND eventType='Anonymity'") + }) + resolve() + }, 200)) + }) + test('11.detection--text模式模拟点击左侧filter测试addParams()', async () => { + const param = { + str: "severity='Critical' AND eventType='Anonymity'", + mode: 'text', + columnList: detectionColumnList, + dataset: detectionDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const textSearchMode = wrapper.find('[test-id="text-search"]') + const params = [{ column: 'status', operator: '=', value: 'Ended' }] + await wrapper.vm.addParams(params) + await textSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe("severity='critical' AND eventType='Anonymity' AND status='1'") + }) + resolve() + }, 200)) + }) + test('12.detection--text模式点击changeMode转为tag模式并搜索', async () => { + const param = { + str: "domain='baidu.com'", + mode: 'text', + columnList: entityColumnList, + dataset: entityDataset + } + initWrapper(param) + + await new Promise(resolve => setTimeout(async () => { + const textSearchMode = wrapper.find('[test-id="text-search"]') + await textSearchMode.trigger('click') + const unitTestParam = await wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam.q).toBe("domain='baidu.com'") + }) + + setTimeout(async () => { + const textChangeMode = wrapper.find('[test-id="text-change-mode"]') + await textChangeMode.trigger('click') + const newParam = { + metaList: [ + { + meta: 'condition', + column: { + name: '', + type: 'string', + label: 'domain', + isEditing: false, + show: false, + isFullText: true + }, + operator: { + value: '=', + isEditing: false, + show: false + }, + value: { + value: "domain='baidu.com'", + label: "domain='baidu.com'", + isEditing: false, + show: false + } + } + ], + mode: 'tag', + columnList: entityColumnList, + dataset: entityDataset + } + initWrapper(newParam) + const tagSearchMode = wrapper.find('[test-id="tag-search"]') + await tagSearchMode.trigger('click') + const unitTestParam1 = wrapper.vm.unitTestParam + await wrapper.vm.$nextTick(() => { + expect(unitTestParam1.q).toBe("domain='baidu.com'") + }) + }, 200) + resolve() + }, 200)) + }) +}) diff --git a/test/views/charts2/charts/advancedSearch/mockData/AdvancedSearch.js b/test/views/charts2/charts/advancedSearch/mockData/AdvancedSearch.js new file mode 100644 index 00000000..7db3e970 --- /dev/null +++ b/test/views/charts2/charts/advancedSearch/mockData/AdvancedSearch.js @@ -0,0 +1,1595 @@ +export const mockData = { + empty: {}, + bytes: {}, + common: {}, + entityColumnList: [ + { + name: 'ip', + label: 'ip', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + type: 'ip', + operator_functions: '=,in,like' + } + } + }, + { + name: 'domain', + label: 'domain', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'domain', + type: 'domain', + operator_functions: '=,in,like' + } + } + }, + { + name: 'app_name', + label: 'app', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'app', + operator_functions: '=,in,like' + } + } + }, + { + name: 'ip_country_region', + label: 'ip.country', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_super_admin_area', + label: 'ip.region', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_admin_area', + label: 'ip.city', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_asn', + label: 'ip.asn', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_isp', + label: 'ip.isp', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'domain_category_name', + label: 'domain.category', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'domain', + operator_functions: '=,in' + } + } + }, + { + name: 'app_category', + label: 'app.category', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'app', + operator_functions: '=,in' + } + } + }, + { + name: 'entity_tags', + label: 'tag', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: { + type: 'array', + items: 'string' + }, + doc: { + constraints: { + primay: '', + operator_functions: 'has' + } + } + }, + { + name: 'port', + label: 'ip.port', + connector: { + alias: 'dr', + table: 'cn_ip_dynamic_attribute', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'int', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'l7_protocol', + label: 'ip.protocol', + connector: { + alias: 'dr', + table: 'cn_ip_dynamic_attribute', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + } + ], + entityDataset: { + sourceData: { + operatesList: [ + { + name: 'AND', + function: 'A AND B', + type: 'abstract', + label: 'AND' + } + ], + filtersList: [ + { + name: 'ip', + label: 'ip', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + type: 'ip', + operator_functions: '=,in,like' + } + } + }, + { + name: 'domain', + label: 'domain', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'domain', + type: 'domain', + operator_functions: '=,in,like' + } + } + }, + { + name: 'app_name', + label: 'app', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'app', + operator_functions: '=,in,like' + } + } + }, + { + name: 'ip_country_region', + label: 'ip.country', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_super_admin_area', + label: 'ip.region', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_admin_area', + label: 'ip.city', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_asn', + label: 'ip.asn', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_isp', + label: 'ip.isp', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'domain_category_name', + label: 'domain.category', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'domain', + operator_functions: '=,in' + } + } + }, + { + name: 'app_category', + label: 'app.category', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'app', + operator_functions: '=,in' + } + } + }, + { + name: 'entity_tags', + label: 'tag', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: { + type: 'array', + items: 'string' + }, + doc: { + constraints: { + primay: '', + operator_functions: 'has' + } + } + }, + { + name: 'port', + label: 'ip.port', + connector: { + alias: 'dr', + table: 'cn_ip_dynamic_attribute', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'int', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'l7_protocol', + label: 'ip.protocol', + connector: { + alias: 'dr', + table: 'cn_ip_dynamic_attribute', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + } + ], + operatesDic: [ + { + type: 'int', + functions: '=,in,like,has' + }, + { + type: 'string', + functions: '=,in,like,has' + }, + { + type: 'array', + functions: '=,in,like,has' + } + ], + operatorReference: [ + { + name: '=', + label: '=', + function: 'expr = value' + }, + { + name: 'has', + label: 'HAS', + function: 'has(expr, value)' + }, + { + name: 'in', + label: 'IN', + function: 'expr in (values)' + }, + { + name: 'like', + label: 'LIKE', + function: 'expr like value' + } + ], + operatorManual: [ + { + name: '=', + label: '=', + function: 'expr = value' + }, + { + name: 'has', + label: 'HAS', + function: 'has(expr, value)' + }, + { + name: 'in', + label: 'IN', + function: 'expr in (values)' + }, + { + name: 'like', + label: 'LIKE', + function: 'expr like value' + } + ], + fields: [ + { + name: 'ip', + label: 'ip', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + type: 'ip', + operator_functions: '=,in,like' + } + } + }, + { + name: 'domain', + label: 'domain', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'domain', + type: 'domain', + operator_functions: '=,in,like' + } + } + }, + { + name: 'app_name', + label: 'app', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'app', + operator_functions: '=,in,like' + } + } + }, + { + name: 'ip_country_region', + label: 'ip.country', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_super_admin_area', + label: 'ip.region', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_admin_area', + label: 'ip.city', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_asn', + label: 'ip.asn', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'ip_isp', + label: 'ip.isp', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'domain_category_name', + label: 'domain.category', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'domain', + operator_functions: '=,in' + } + } + }, + { + name: 'app_category', + label: 'app.category', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'app', + operator_functions: '=,in' + } + } + }, + { + name: 'entity_tags', + label: 'tag', + connector: { + alias: 'e', + table: 'cn_entity_relation', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: { + type: 'array', + items: 'string' + }, + doc: { + constraints: { + primay: '', + operator_functions: 'has' + } + } + }, + { + name: 'port', + label: 'ip.port', + connector: { + alias: 'dr', + table: 'cn_ip_dynamic_attribute', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'int', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + }, + { + name: 'l7_protocol', + label: 'ip.protocol', + connector: { + alias: 'dr', + table: 'cn_ip_dynamic_attribute', + schema: 'cyber_narrator_galaxy', + catalog: 'clickhouse' + }, + type: 'string', + doc: { + constraints: { + primay: 'ip', + operator_functions: '=,in' + } + } + } + ], + doc: { + functions: { + aggregation: [], + date: [], + operator: [ + { + name: '=', + label: '=', + function: 'expr = value' + }, + { + name: 'has', + label: 'HAS', + function: 'has(expr, value)' + }, + { + name: 'in', + label: 'IN', + function: 'expr in (values)' + }, + { + name: 'like', + label: 'LIKE', + function: 'expr like value' + } + ] + }, + schema_query: { + references: { + aggregation: [ + { + type: 'int', + functions: '' + }, + { + type: 'string', + functions: '' + }, + { + type: 'array', + functions: '' + } + ], + operator: [ + { + type: 'int', + functions: '=,in,like,has' + }, + { + type: 'string', + functions: '=,in,like,has' + }, + { + type: 'array', + functions: '=,in,like,has' + } + ] + } + } + }, + funcDic: [ + { + type: 'int', + functions: '' + }, + { + type: 'string', + functions: '' + }, + { + type: 'array', + functions: '' + } + ], + funcReference: [] + }, + hintData: { + operatesList: [ + { + type: 'abstract', + text: '', + displayText: 'Keyword', + className: 'divider hint-title' + }, + { + text: 'AND', + displayText: 'AND', + className: 'operates-item el-dropdown-menu__item relative-item' + } + ], + filtersList: [ + { + type: 'abstract', + text: '', + displayText: 'Fields', + className: 'divider hint-title' + }, + { + text: 'ip', + displayText: 'ip', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'domain', + displayText: 'domain', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'app', + displayText: 'app', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'ip.country', + displayText: 'ip.country', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'ip.region', + displayText: 'ip.region', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'ip.city', + displayText: 'ip.city', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'ip.asn', + displayText: 'ip.asn', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'ip.isp', + displayText: 'ip.isp', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'domain.category', + displayText: 'domain.category', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'app.category', + displayText: 'app.category', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'tag', + displayText: 'tag', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'ip.port', + displayText: 'ip.port', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'ip.protocol', + displayText: 'ip.protocol', + className: 'filter-item el-dropdown-menu__item relative-item' + } + ] + } + }, + detectionColumnList: [ + { + name: 'event_type', + label: 'eventType', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + }, + data: [ + { + code: 'Initial Access', + value: 'Initial Access' + }, + { + code: 'Command and Control', + value: 'Command and Control' + }, + { + code: 'Credential Access', + value: 'Credential Access' + }, + { + code: 'Lateral Movement', + value: 'Lateral Movement' + }, + { + code: 'Collection', + value: 'Collection' + }, + { + code: 'Impact', + value: 'Impact' + }, + { + code: 'Anonymity', + value: 'Anonymity' + }, + { + code: 'Regulatory Risk', + value: 'Regulatory Risk' + } + ] + } + }, + { + name: 'event_name', + label: 'eventName', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'severity', + label: 'severity', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + }, + data: [ + { + code: 'Critical', + code1: 'overall.critical', + value: 'critical' + }, + { + code: 'High', + code1: 'overall.high', + value: 'high' + }, + { + code: 'Medium', + code1: 'overall.medium', + value: 'medium' + }, + { + code: 'Low', + code1: 'overall.low', + value: 'low' + }, + { + code: 'Info', + code1: 'overall.info', + value: 'info' + } + ] + } + }, + { + name: 'offender_ip', + label: 'offenderIP', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'victim_ip', + label: 'victimIP', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'domain', + label: 'domain', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'app', + label: 'app', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'status', + label: 'status', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in' + }, + data: [ + { + code: 'Ended', + code1: 'detections.ended', + value: 1 + }, + { + code: 'Active', + code1: 'detections.active', + value: 0 + } + ] + } + } + ], + detectionDataset: { + sourceData: { + operatesList: [ + { + name: 'AND', + function: 'A AND B', + type: 'abstract', + label: 'AND' + } + ], + filtersList: [ + { + name: 'event_type', + label: 'eventType', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + }, + data: [ + { + code: 'Initial Access', + value: 'Initial Access' + }, + { + code: 'Command and Control', + value: 'Command and Control' + }, + { + code: 'Credential Access', + value: 'Credential Access' + }, + { + code: 'Lateral Movement', + value: 'Lateral Movement' + }, + { + code: 'Collection', + value: 'Collection' + }, + { + code: 'Impact', + value: 'Impact' + }, + { + code: 'Anonymity', + value: 'Anonymity' + }, + { + code: 'Regulatory Risk', + value: 'Regulatory Risk' + } + ] + } + }, + { + name: 'event_name', + label: 'eventName', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'severity', + label: 'severity', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + }, + data: [ + { + code: 'Critical', + code1: 'overall.critical', + value: 'critical' + }, + { + code: 'High', + code1: 'overall.high', + value: 'high' + }, + { + code: 'Medium', + code1: 'overall.medium', + value: 'medium' + }, + { + code: 'Low', + code1: 'overall.low', + value: 'low' + }, + { + code: 'Info', + code1: 'overall.info', + value: 'info' + } + ] + } + }, + { + name: 'offender_ip', + label: 'offenderIP', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'victim_ip', + label: 'victimIP', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'domain', + label: 'domain', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'app', + label: 'app', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'status', + label: 'status', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in' + }, + data: [ + { + code: 'Ended', + code1: 'detections.ended', + value: 1 + }, + { + code: 'Active', + code1: 'detections.active', + value: 0 + } + ] + } + } + ], + operatesDic: [ + { + type: 'int', + functions: '=,in,like,has' + }, + { + type: 'string', + functions: '=,in,like,has' + }, + { + type: 'array', + functions: '=,in,like,has' + } + ], + operatorReference: [ + { + name: '=', + label: '=', + function: 'expr = value' + }, + { + name: 'has', + label: 'HAS', + function: 'has(expr, value)' + }, + { + name: 'in', + label: 'IN', + function: 'expr in (values)' + }, + { + name: 'like', + label: 'LIKE', + function: 'expr like value' + } + ], + operatorManual: [ + { + name: '=', + label: '=', + function: 'expr = value' + }, + { + name: 'has', + label: 'HAS', + function: 'has(expr, value)' + }, + { + name: 'in', + label: 'IN', + function: 'expr in (values)' + }, + { + name: 'like', + label: 'LIKE', + function: 'expr like value' + } + ], + fields: [ + { + name: 'event_type', + label: 'eventType', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + }, + data: [ + { + code: 'Initial Access', + value: 'Initial Access' + }, + { + code: 'Command and Control', + value: 'Command and Control' + }, + { + code: 'Credential Access', + value: 'Credential Access' + }, + { + code: 'Lateral Movement', + value: 'Lateral Movement' + }, + { + code: 'Collection', + value: 'Collection' + }, + { + code: 'Impact', + value: 'Impact' + }, + { + code: 'Anonymity', + value: 'Anonymity' + }, + { + code: 'Regulatory Risk', + value: 'Regulatory Risk' + } + ] + } + }, + { + name: 'event_name', + label: 'eventName', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'severity', + label: 'severity', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + }, + data: [ + { + code: 'Critical', + code1: 'overall.critical', + value: 'critical' + }, + { + code: 'High', + code1: 'overall.high', + value: 'high' + }, + { + code: 'Medium', + code1: 'overall.medium', + value: 'medium' + }, + { + code: 'Low', + code1: 'overall.low', + value: 'low' + }, + { + code: 'Info', + code1: 'overall.info', + value: 'info' + } + ] + } + }, + { + name: 'offender_ip', + label: 'offenderIP', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'victim_ip', + label: 'victimIP', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'domain', + label: 'domain', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'app', + label: 'app', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in,like' + } + } + }, + { + name: 'status', + label: 'status', + type: 'string', + doc: { + constraints: { + operator_functions: '=,in' + }, + data: [ + { + code: 'Ended', + code1: 'detections.ended', + value: 1 + }, + { + code: 'Active', + code1: 'detections.active', + value: 0 + } + ] + } + } + ], + doc: { + functions: { + aggregation: [], + date: [], + operator: [ + { + name: '=', + label: '=', + function: 'expr = value' + }, + { + name: 'has', + label: 'HAS', + function: 'has(expr, value)' + }, + { + name: 'in', + label: 'IN', + function: 'expr in (values)' + }, + { + name: 'like', + label: 'LIKE', + function: 'expr like value' + } + ] + }, + schema_query: { + references: { + aggregation: [ + { + type: 'int', + functions: '' + }, + { + type: 'string', + functions: '' + }, + { + type: 'array', + functions: '' + } + ], + operator: [ + { + type: 'int', + functions: '=,in,like,has' + }, + { + type: 'string', + functions: '=,in,like,has' + }, + { + type: 'array', + functions: '=,in,like,has' + } + ] + } + } + }, + funcDic: [ + { + type: 'int', + functions: '' + }, + { + type: 'string', + functions: '' + }, + { + type: 'array', + functions: '' + } + ], + funcReference: [] + }, + hintData: { + operatesList: [ + { + type: 'abstract', + text: '', + displayText: 'Keyword', + className: 'divider hint-title' + }, + { + text: 'AND', + displayText: 'AND', + className: 'operates-item el-dropdown-menu__item relative-item' + } + ], + filtersList: [ + { + type: 'abstract', + text: '', + displayText: 'Fields', + className: 'divider hint-title' + }, + { + text: 'eventType', + displayText: 'eventType', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'eventName', + displayText: 'eventName', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'severity', + displayText: 'severity', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'offenderIP', + displayText: 'offenderIP', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'victimIP', + displayText: 'victimIP', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'domain', + displayText: 'domain', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'app', + displayText: 'app', + className: 'filter-item el-dropdown-menu__item relative-item' + }, + { + text: 'status', + displayText: 'status', + className: 'filter-item el-dropdown-menu__item relative-item' + } + ] + } + } +}