From 1c40ec7364ce78b7699d6f33bad066d573b938f8 Mon Sep 17 00:00:00 2001 From: Valentin Bouzin Date: Wed, 16 Oct 2024 14:22:52 +0200 Subject: [PATCH] squashed --- .../opencti-front/lang/back/de.json | 1 + .../opencti-front/lang/back/en.json | 1 + .../opencti-front/lang/back/es.json | 1 + .../opencti-front/lang/back/fr.json | 1 + .../opencti-front/lang/back/ja.json | 1 + .../opencti-front/lang/back/ko.json | 1 + .../opencti-front/lang/back/zh.json | 1 + .../modules/indicator/indicator-converter.ts | 4 +- .../src/modules/indicator/indicator-domain.ts | 43 ++++++++++--------- .../modules/indicator/indicator-resolver.ts | 4 +- .../src/modules/indicator/indicator.ts | 1 - .../opencti-graphql/src/types/store.d.ts | 6 +++ .../02-resolvers/indicator-test.ts | 43 +++++++++++++++++++ 13 files changed, 85 insertions(+), 23 deletions(-) diff --git a/opencti-platform/opencti-front/lang/back/de.json b/opencti-platform/opencti-front/lang/back/de.json index 11104233dc4f..67f182a06fb1 100644 --- a/opencti-platform/opencti-front/lang/back/de.json +++ b/opencti-platform/opencti-front/lang/back/de.json @@ -470,6 +470,7 @@ "Observable content": "Observierbarer Inhalt", "Observable description": "Observable Beschreibung", "Observables": "Observables", + "Observables values": "Observable Werte", "Opened connection": "Geöffnete Verbindung", "Operating System": "Betriebssystem", "Operation": "Operation", diff --git a/opencti-platform/opencti-front/lang/back/en.json b/opencti-platform/opencti-front/lang/back/en.json index 6dd471dc86ff..7fde3e31abbb 100644 --- a/opencti-platform/opencti-front/lang/back/en.json +++ b/opencti-platform/opencti-front/lang/back/en.json @@ -470,6 +470,7 @@ "Observable content": "Observable content", "Observable description": "Observable description", "Observables": "Observables", + "Observables values": "Observables values", "Opened connection": "Opened connection", "Operating System": "Operating System", "Operation": "Operation", diff --git a/opencti-platform/opencti-front/lang/back/es.json b/opencti-platform/opencti-front/lang/back/es.json index 0dc0947a83a6..687ca422a32b 100644 --- a/opencti-platform/opencti-front/lang/back/es.json +++ b/opencti-platform/opencti-front/lang/back/es.json @@ -470,6 +470,7 @@ "Observable content": "Contenido observable", "Observable description": "Descripción observable", "Observables": "Observables", + "Observables values": "Valores observables", "Opened connection": "Conexión abierta", "Operating System": "Sistema Operativo", "Operation": "Operación", diff --git a/opencti-platform/opencti-front/lang/back/fr.json b/opencti-platform/opencti-front/lang/back/fr.json index e9491dc314ce..30fbba714bb1 100644 --- a/opencti-platform/opencti-front/lang/back/fr.json +++ b/opencti-platform/opencti-front/lang/back/fr.json @@ -470,6 +470,7 @@ "Observable content": "Contenu observable", "Observable description": "Description de l'observable", "Observables": "Observables", + "Observables values": "Valeurs observables", "Opened connection": "Connexion ouverte", "Operating System": "Système d'exploitation", "Operation": "Opération", diff --git a/opencti-platform/opencti-front/lang/back/ja.json b/opencti-platform/opencti-front/lang/back/ja.json index e0d4c0c4cbb4..4934578a333b 100644 --- a/opencti-platform/opencti-front/lang/back/ja.json +++ b/opencti-platform/opencti-front/lang/back/ja.json @@ -470,6 +470,7 @@ "Observable content": "観測可能な内容", "Observable description": "観測可能な記述", "Observables": "観測可能なもの", + "Observables values": "観測値", "Opened connection": "オープン接続", "Operating System": "インストールされているソフトウェア", "Operation": "操作", diff --git a/opencti-platform/opencti-front/lang/back/ko.json b/opencti-platform/opencti-front/lang/back/ko.json index 92e1519868f9..c176a4cb741f 100644 --- a/opencti-platform/opencti-front/lang/back/ko.json +++ b/opencti-platform/opencti-front/lang/back/ko.json @@ -470,6 +470,7 @@ "Observable content": "관찰 가능 내용", "Observable description": "관찰 가능 설명", "Observables": "관찰 가능 항목", + "Observables values": "관측값", "Opened connection": "열린 연결", "Operating System": "운영체제", "Operation": "작업", diff --git a/opencti-platform/opencti-front/lang/back/zh.json b/opencti-platform/opencti-front/lang/back/zh.json index 01822cef3e72..91dc71b71d0b 100644 --- a/opencti-platform/opencti-front/lang/back/zh.json +++ b/opencti-platform/opencti-front/lang/back/zh.json @@ -470,6 +470,7 @@ "Observable content": "可观测内容", "Observable description": "可观察描述", "Observables": "可观测", + "Observables values": "观测值", "Opened connection": "打开的连接", "Operating System": "操作系统", "Operation": "操作", diff --git a/opencti-platform/opencti-graphql/src/modules/indicator/indicator-converter.ts b/opencti-platform/opencti-graphql/src/modules/indicator/indicator-converter.ts index c522e9d776f6..9c4938ca37f4 100644 --- a/opencti-platform/opencti-graphql/src/modules/indicator/indicator-converter.ts +++ b/opencti-platform/opencti-graphql/src/modules/indicator/indicator-converter.ts @@ -1,4 +1,5 @@ import moment from 'moment'; +import { getObservableValuesFromPattern } from 'src/modules/indicator/indicator-domain'; import { buildKillChainPhases, buildMITREExtensions, buildStixDomain, cleanObject, convertToStixDate } from '../../database/stix-converter'; import { STIX_EXT_MITRE, STIX_EXT_OCTI } from '../../types/stix-extensions'; import type { StixIndicator, StoreEntityIndicator } from './indicator-types'; @@ -27,7 +28,8 @@ const convertIndicatorToStix = (instance: StoreEntityIndicator): StixIndicator = ...indicator.extensions[STIX_EXT_OCTI], detection: instance.x_opencti_detection, score: instance.x_opencti_score, - main_observable_type: instance.x_opencti_main_observable_type + main_observable_type: instance.x_opencti_main_observable_type, + observables_values: getObservableValuesFromPattern(instance.pattern), }), [STIX_EXT_MITRE]: buildMITREExtensions(instance) } diff --git a/opencti-platform/opencti-graphql/src/modules/indicator/indicator-domain.ts b/opencti-platform/opencti-graphql/src/modules/indicator/indicator-domain.ts index 1da8c3b8134e..56b50bd57ac7 100644 --- a/opencti-platform/opencti-graphql/src/modules/indicator/indicator-domain.ts +++ b/opencti-platform/opencti-graphql/src/modules/indicator/indicator-domain.ts @@ -212,6 +212,16 @@ export const promoteIndicatorToObservables = async (context: AuthContext, user: export const getObservableValuesFromPattern = (pattern: string) => extractObservablesFromIndicatorPattern(pattern); +const getFormattedPattern = async (context: AuthContext, user: AuthUser, pattern_type: string, pattern: string) => { + const patternType = pattern_type.toLowerCase(); + const formattedPattern = cleanupIndicatorPattern(patternType, pattern); + const check = await checkIndicatorSyntax(context, user, patternType, formattedPattern); + if (check === false) { + throw FunctionalError(`Indicator of type ${pattern_type} is not correctly formatted.`); + } + return formattedPattern; +}; + export const addIndicator = async (context: AuthContext, user: AuthUser, indicator: IndicatorAddInput) => { let observableType: string = isEmptyField(indicator.x_opencti_main_observable_type) ? 'Unknown' : indicator.x_opencti_main_observable_type as string; if (observableType === 'File') { @@ -221,19 +231,15 @@ export const addIndicator = async (context: AuthContext, user: AuthUser, indicat if (isKnownObservable && !isStixCyberObservable(observableType)) { throw FunctionalError(`Observable type ${observableType} is not supported.`); } - // check indicator syntax - const patternType = indicator.pattern_type.toLowerCase(); - const formattedPattern = cleanupIndicatorPattern(patternType, indicator.pattern); - const check = await checkIndicatorSyntax(context, user, patternType, formattedPattern); - if (check === false) { - throw FunctionalError(`Indicator of type ${indicator.pattern_type} is not correctly formatted.`); - } + const indicatorBaseScore = indicator.x_opencti_score ?? 50; const isDecayActivated = await isModuleActivated('INDICATOR_DECAY_MANAGER'); // find default decay rule (even if decay is not activated, it is used to compute default validFrom and validUntil) const decayRule = await findDecayRuleForIndicator(context, observableType); const { validFrom, validUntil, revoked, validPeriod } = await computeValidPeriod(indicator, decayRule.decay_lifetime); - const extractedObservableValues = getObservableValuesFromPattern(formattedPattern); + + const formattedPattern = await getFormattedPattern(context, user, indicator.pattern_type, indicator.pattern); + const indicatorToCreate = R.pipe( R.dissoc('createObservables'), R.dissoc('basedOn'), @@ -244,8 +250,8 @@ export const addIndicator = async (context: AuthContext, user: AuthUser, indicat R.assoc('valid_from', validFrom.toISOString()), R.assoc('valid_until', validUntil.toISOString()), R.assoc('revoked', revoked), - R.assoc('x_opencti_observables_values', extractedObservableValues) )(indicator); + let finalIndicatorToCreate; if (isDecayActivated && !revoked) { const indicatorDecayRule = { @@ -310,17 +316,6 @@ export const indicatorEditField = async (context: AuthContext, user: AuthUser, i throw FunctionalError('Cannot edit the field, Indicator cannot be found.'); } - const foundPattern = finalInput.find((item) => item.key === 'pattern'); - if (!indicator.x_opencti_observables_values || foundPattern) { - const patternType = indicator.pattern_type.toLowerCase(); - const formattedPattern = cleanupIndicatorPattern(patternType, foundPattern?.value ? foundPattern?.value[0] : indicator.pattern); - const check = await checkIndicatorSyntax(context, user, patternType, formattedPattern); - if (check === false) { - throw FunctionalError(`Indicator of type ${indicator.pattern_type} is not correctly formatted.`); - } - const extractedObservableValues = getObservableValuesFromPattern(formattedPattern); - finalInput.push({ key: 'x_opencti_observables_values', value: [...extractedObservableValues] }); - } // validation check because according to STIX 2.1 specification the valid_until must be greater than the valid_from let { valid_from, valid_until } = indicator; input.forEach((e) => { @@ -330,6 +325,14 @@ export const indicatorEditField = async (context: AuthContext, user: AuthUser, i if (new Date(valid_until) <= new Date(valid_from)) { throw ValidationError('The valid until date must be greater than the valid from date', 'valid_from'); } + + const foundPattern = finalInput.find((item) => item.key === 'pattern'); + if (!indicator.x_opencti_observables_values || foundPattern) { + const formattedPattern = await getFormattedPattern(context, user, indicator.pattern_type, foundPattern?.value ? foundPattern?.value[0] : indicator.pattern); + const extractedObservableValues = getObservableValuesFromPattern(formattedPattern); + finalInput.push({ key: 'x_opencti_observables_values', value: [...extractedObservableValues] }); + } + const scoreEditInput = input.find((e) => e.key === 'x_opencti_score'); if (scoreEditInput) { if (indicator.decay_applied_rule && !scoreEditInput.value.includes(indicator.decay_base_score)) { diff --git a/opencti-platform/opencti-graphql/src/modules/indicator/indicator-resolver.ts b/opencti-platform/opencti-graphql/src/modules/indicator/indicator-resolver.ts index 0fe3c095d075..3dc569224815 100644 --- a/opencti-platform/opencti-graphql/src/modules/indicator/indicator-resolver.ts +++ b/opencti-platform/opencti-graphql/src/modules/indicator/indicator-resolver.ts @@ -4,13 +4,14 @@ import { findById, getDecayChartData, getDecayDetails, + getObservableValuesFromPattern, indicatorEditField, indicatorsDistributionByEntity, indicatorsNumber, indicatorsNumberByEntity, indicatorsTimeSeries, indicatorsTimeSeriesByEntity, - observablesPaginated + observablesPaginated, } from './indicator-domain'; import { stixDomainObjectAddRelation, @@ -53,6 +54,7 @@ const indicatorResolvers: Resolvers = { observables: (indicator, args, context) => observablesPaginated(context, context.user, indicator.id, args), decayLiveDetails: (indicator, _, context) => getDecayDetails(context, context.user, indicator), decayChartData: (indicator, _, context) => getDecayChartData(context, context.user, indicator), + x_opencti_observables_values: (indicator) => getObservableValuesFromPattern(indicator.pattern), }, Mutation: { indicatorAdd: (_, { input }, context) => addIndicator(context, context.user, input), diff --git a/opencti-platform/opencti-graphql/src/modules/indicator/indicator.ts b/opencti-platform/opencti-graphql/src/modules/indicator/indicator.ts index 87e607538c2c..fd8cb1409cc8 100644 --- a/opencti-platform/opencti-graphql/src/modules/indicator/indicator.ts +++ b/opencti-platform/opencti-graphql/src/modules/indicator/indicator.ts @@ -32,7 +32,6 @@ const INDICATOR_DEFINITION: ModuleDefinition { pageInfo: PageInfo; } +interface ObservablesValuesType { + type: string; + value: string; +} + interface BasicStoreEntity extends BasicStoreCommon { id: string; name: string; @@ -329,6 +334,7 @@ interface BasicStoreEntity extends BasicStoreCommon { x_opencti_epss_score: number; x_opencti_epss_percentile: number; x_opencti_main_observable_type: string; + x_opencti_observables_values: Array; x_opencti_lastname: string; x_opencti_firstname: string; x_opencti_inferences: Array | undefined; diff --git a/opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/indicator-test.ts b/opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/indicator-test.ts index bdfe27de4535..376642a58c89 100644 --- a/opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/indicator-test.ts +++ b/opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/indicator-test.ts @@ -46,6 +46,10 @@ const READ_QUERY = gql` name description toStix + x_opencti_observables_values { + type + value + } decay_base_score decay_base_score_date decay_applied_rule { @@ -82,6 +86,10 @@ const CREATE_QUERY = gql` id name description + x_opencti_observables_values { + type + value + } observables { edges { node { @@ -119,6 +127,10 @@ describe('Indicator resolver standard behavior', () => { expect(indicator.data?.indicatorAdd).toBeDefined(); expect(indicator.data?.indicatorAdd.name).toEqual(indicatorForTestName); expect(indicator.data?.indicatorAdd.observables.edges.length).toEqual(0); + expect(indicator.data?.indicatorAdd.x_opencti_observables_values).toBeDefined(); + const observablesValues = indicator.data?.indicatorAdd.x_opencti_observables_values; + expect(observablesValues?.[0].type).toEqual('Domain-Name'); + expect(observablesValues?.[0].value).toEqual('www.payah.rest'); firstIndicatorInternalId = indicator.data?.indicatorAdd.id; }); it('should indicator with same name be created also (no upsert) (see issues/5819)', async () => { @@ -138,6 +150,10 @@ describe('Indicator resolver standard behavior', () => { expect(indicator.data?.indicatorAdd).toBeDefined(); expect(indicator.data?.indicatorAdd.name).toEqual(indicatorForTestName); expect(indicator.data?.indicatorAdd.observables.edges.length).toEqual(0); + expect(indicator.data?.indicatorAdd.x_opencti_observables_values).toBeDefined(); + const observablesValues = indicator.data?.indicatorAdd.x_opencti_observables_values; + expect(observablesValues?.[0].type).toEqual('Domain-Name'); + expect(observablesValues?.[0].value).toEqual('www.test2.rest'); expect(indicator.data?.indicatorAdd.id, 'A new indicator should be created, if not it is an upsert and it is a bug').not.toEqual(firstIndicatorInternalId); secondIndicatorInternalId = indicator.data?.indicatorAdd.id; }); @@ -160,6 +176,10 @@ describe('Indicator resolver standard behavior', () => { expect(indicator.data?.indicatorAdd).toBeDefined(); expect(indicator.data?.indicatorAdd.name).toEqual(`New name for ${indicatorForTestName}`); expect(indicator.data?.indicatorAdd.observables.edges.length).toEqual(0); + expect(indicator.data?.indicatorAdd.x_opencti_observables_values).toBeDefined(); + const observablesValues = indicator.data?.indicatorAdd.x_opencti_observables_values; + expect(observablesValues?.[0].type).toEqual('Domain-Name'); + expect(observablesValues?.[0].value).toEqual('www.payah.rest'); expect(indicator.data?.indicatorAdd.id).toEqual(firstIndicatorInternalId); expect(indicator.data?.indicatorAdd.id).toEqual(firstIndicatorInternalId); }); @@ -218,6 +238,29 @@ describe('Indicator resolver standard behavior', () => { }); expect(queryResult.data?.indicatorFieldPatch.name).toEqual('Indicator - test'); }); + // skipped until sync tests fixed + it.skip('should update indicator observables values on pattern edit', async () => { + const UPDATE_QUERY = gql` + mutation IndicatorFieldPatch($id: ID!, $input: [EditInput!]!) { + indicatorFieldPatch(id: $id, input: $input) { + id + name + x_opencti_observables_values { + type + value + } + } + } + `; + const queryResult = await queryAsAdminWithSuccess({ + query: UPDATE_QUERY, + variables: { id: firstIndicatorInternalId, input: { key: 'pattern', value: ['[domain-name:value = \'www.payah.test\']'] } }, + }); + expect(queryResult.data?.indicatorFieldPatch.x_opencti_observables_values).toBeDefined(); + const observablesValues = queryResult.data?.indicatorFieldPatch.x_opencti_observables_values; + expect(observablesValues?.[0].type).toEqual('Domain-Name'); + expect(observablesValues?.[0].value).toEqual('www.payah.test'); + }); it('should context patch indicator', async () => { const CONTEXT_PATCH_QUERY = gql` mutation IndicatorContextPatch($id: ID!, $input: EditContext) {