Skip to content

Commit

Permalink
[backend] Add relatedRestrictions in stream (#9251)
Browse files Browse the repository at this point in the history
  • Loading branch information
marieflorescontact authored Jan 10, 2025
1 parent 335766c commit af13f58
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 4 deletions.
4 changes: 3 additions & 1 deletion opencti-platform/opencti-graphql/src/database/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
buildPagination,
computeAverage,
extractIdsFromStoreObject,
extractObjectsRestrictionsFromInputs,
fillTimeSeries,
INDEX_DRAFT_OBJECTS,
INDEX_INFERRED_RELATIONSHIPS,
Expand Down Expand Up @@ -2200,7 +2201,8 @@ export const updateAttributeMetaResolved = async (context, user, initial, inputs
message: opts.commitMessage,
external_references: references.map((ref) => convertExternalReferenceToStix(ref))
} : undefined;
const event = await storeUpdateEvent(context, user, initial, updatedInstance, message, { ...opts, commit });
const relatedRestrictions = extractObjectsRestrictionsFromInputs(updatedInputs, initial.entity_type);
const event = await storeUpdateEvent(context, user, initial, updatedInstance, message, { ...opts, commit, related_restrictions: relatedRestrictions });
return { element: updatedInstance, event, isCreation: false };
}
// Return updated element after waiting for it.
Expand Down
3 changes: 2 additions & 1 deletion opencti-platform/opencti-graphql/src/database/redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,8 @@ export const buildStixUpdateEvent = (user: AuthUser, previousStix: StixCoreObjec
commit: opts.commit,
context: {
patch,
reverse_patch: previousPatch
reverse_patch: previousPatch,
related_restrictions: opts.related_restrictions,
}
};
};
Expand Down
20 changes: 18 additions & 2 deletions opencti-platform/opencti-graphql/src/database/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import { Promise } from 'bluebird';
import { DatabaseError, UnsupportedError } from '../config/errors';
import { isHistoryObject, isInternalObject } from '../schema/internalObject';
import { isStixMetaObject } from '../schema/stixMetaObject';
import { isStixDomainObject } from '../schema/stixDomainObject';
import { isStixDomainObject, isStixDomainObjectContainer } from '../schema/stixDomainObject';
import { isStixCyberObservable } from '../schema/stixCyberObservable';
import { isInternalRelationship } from '../schema/internalRelationship';
import { isStixCoreRelationship } from '../schema/stixCoreRelationship';
import { isStixSightingRelationship } from '../schema/stixSightingRelationship';
import conf from '../config/conf';
import { now } from '../utils/format';
import { isStixRefRelationship } from '../schema/stixRefRelationship';
import { isStixRefRelationship, RELATION_OBJECT_MARKING } from '../schema/stixRefRelationship';
import { schemaAttributesDefinition } from '../schema/schema-attributes';
import { getDraftContext } from '../utils/draftContext';
import { INPUT_OBJECTS } from '../schema/general';

export const ES_INDEX_PREFIX = conf.get('elasticsearch:index_prefix') || 'opencti';
const rabbitmqPrefix = conf.get('rabbitmq:queue_prefix');
Expand Down Expand Up @@ -326,6 +327,21 @@ export const extractIdsFromStoreObject = (instance) => {
return ids;
};

export const extractObjectsRestrictionsFromInputs = (inputs, entityType) => {
const markings = [];
if (isStixDomainObjectContainer(entityType)) {
inputs.forEach((input) => {
if (input && input.key === INPUT_OBJECTS && input.value?.length > 0) {
const objectMarking = input.value.flatMap((value) => value[RELATION_OBJECT_MARKING] ?? []);
markings.push(...objectMarking);
}
});
}
return {
markings
};
};

export const isObjectPathTargetMultipleAttribute = (instance, object_path) => {
const preparedPath = object_path.startsWith('/') ? object_path : `/${object_path}`;
const pathArray = preparedPath.split('/').filter((p) => isNotEmptyField(p));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ export const buildHistoryElementsFromEvents = async (context:AuthContext, events
.map((stixId) => markingsById.get(stixId)?.internal_id)
.filter((o) => isNotEmptyField(o)) as string[];
eventMarkingRefs.push(...previousMarkingRefs);
// Get related restrictions (e.g. markings of added objects in a container)
if (updateEvent.context.related_restrictions) {
const relatedMarkings = updateEvent.context.related_restrictions.markings ?? [];
eventMarkingRefs.push(...relatedMarkings);
}
}
if (stix.type === STIX_TYPE_RELATION) {
const rel: StixRelation = stix as StixRelation;
Expand Down
2 changes: 2 additions & 0 deletions opencti-platform/opencti-graphql/src/types/event.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface CreateEventOpts extends EventOpts {

interface UpdateEventOpts extends EventOpts {
commit?: CommitContext | undefined;
related_restrictions?: { markings: string[] };
}

interface RelationCreation {
Expand Down Expand Up @@ -52,6 +53,7 @@ interface UpdateEvent extends StreamDataEvent {
context: {
patch: Array<Operation>;
reverse_patch: Array<Operation>;
related_restrictions?: { markings: string[] };
};
}

Expand Down
153 changes: 153 additions & 0 deletions opencti-platform/opencti-graphql/tests/01-unit/database/utils-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { describe, expect, it } from 'vitest';
import { extractObjectsRestrictionsFromInputs } from '../../../src/database/utils';
import { ENTITY_TYPE_CONTAINER_REPORT, ENTITY_TYPE_MALWARE } from '../../../src/schema/stixDomainObject';

const inputs = [
{
key: 'objects',
operation: 'add',
value: [
{
_id: '4c688965-fd97-40ea-9af0-967030eb06a5',
_index: 'opencti_stix_domain_objects-000001',
aliases: [],
base_type: 'ENTITY',
confidence: 100,
created: '2024 -11-08T10:30:44.343Z',
'created-by': 'bc9fe33d-e694-4604-abc1-82f2e99cd00a',
created_at: '2024-12-02T13:55:39.981Z',
creator_id: [
'549e078a-41df-43aa-8e0f-ba961b16d0c8'
],
description: '',
entity_type: 'Intrusion-Set',
'external-reference': [],
first_seen: '1970-01-01T00:00:00.000Z',
goals: ['Military Advantage'],
i_aliases_ids: [],
id: '4c688965-fd97-40ea-9af0-967030eb06a5',
internal_id: '4c688965-fd97-40ea-9af0-967030eb06a5',
lang: 'en',
last_seen: '5138-11-16T09:46:40.000Z',
modified: '2024-12-02T13:55:40.064Z',
name: 'APT29',
'object-label': [
'debcc53e-9515-4107-bbdc-8eb8084f7527'
],
'object-marking': [
'fa7fa933-7b65-463f-ac5e-aa33b2a36ce8',
'056276ff-26dc-4774-a439-a36253a96939'
],
parent_types: [
'Basic-Object',
'Stix-Object',
'Stix-Core-Object',
'Stix-Domain-Object'
],
primary_motivation: 'Espionage',
'rel_created-by.internal_id': [
'bc9fe33d-e694-4604-abc1-82f2e99cd00a'
],
'rel_external-reference.internal_id': [],
'rel_object-label.internal_id': [
'debcc53e-9515-4107-bbdc-8eb8084f7527'
],
'rel_object-marking.internal_id': [
'fa7fa933-7b65-463f-ac5e-aa33b2a36ce8',
'056276ff-26dc-4774-a439-a36253a96939'
],
resource_level: null,
revoked: false,
'secondary_motivation s': [
'Military/Security/Diplomatic',
'Ethnic/nationalist',
'Ideological/Religious'
],
sort: [
1733147739981
],
standard_id: 'intrusion-set--36319194-19e1-50ac-9163-778b56a1bf12',
updated_at: '2024-12-02T13:55:40.064Z',
x_opencti_stix_ids: [],
x_opencti_workflow_id: null
}
]
}
];

const relInputs = [
{
key: 'objects',
operation: 'add',
value: [
{
_id: '23c0c086-afee-45e5-b276-872948997816',
_index: 'opencti_stix_core_relationships-000001',
base_type: 'RELATION',
confidence: 100,
created: '2024-12-0 6T08:41:59.270Z',
created_at: '2024-12-06T08:41:59.270Z',
creator_id: [
'88ec0c6a-13ce-5e39-b486-354fe4a7084f'
],
description: '',
entity_type: 'related-to',
from: null,
fromId: 'fd3259cb-f219-4cd6-85fe-0df16ffef185',
fromName: 'AlienVault',
fromRole: 'related-to_from',
fromType: 'Organization',
id: '23c0c086-afee-45e5-b276-872948997816',
internal_id: '23c0c086-afee-45e5-b276-872948997816',
lang: 'en',
modified: '2024-12-06T08:41:59.290Z',
'object-marking': [
'eaccd139-ec2e-48d9-b2ef-a17ba6e7e938'
],
parent_types: [
'basic-relationship',
'stix-relationship',
'stix-core-relationship'
],
'rel_object-marking.internal_id': [
'eaccd139-ec2e-48d9-b2ef-a17ba6e7e938'
],
relationship_type: 'related-to',
revoked: false,
sort: [
1733474519270
],
source_ref: 'identity--temporary',
standard_id: 'relationship--54af1a95-b0e8-53d6-8c0c-074f57e9d58c',
start_time: '2024-12-06T08:40:55.000Z',
stop_time: '2024-12-06T08:41:55.000Z',
target_ref: 'malware--temporary',
to: null,
toId: 'd9162b45-55dd-403b-906b-a16edf74ebff',
toName: 'HAMMERTOSS',
toRole: 'related-to_to',
toType: 'Malware',
updated_at: '2024-12-06T08:41:59.290Z',
x_opencti_stix_ids: []
}
]
}
];

describe('extractObjectsRestrictionsFromInputs testing', () => {
it('should add inputs object-marking in stream when adding entity to a report', () => {
const relatedRestrictions = extractObjectsRestrictionsFromInputs(inputs, ENTITY_TYPE_CONTAINER_REPORT);
const expected = { markings: ['fa7fa933-7b65-463f-ac5e-aa33b2a36ce8', '056276ff-26dc-4774-a439-a36253a96939'] };
expect(relatedRestrictions).toEqual(expected);
});
it('should add inputs object-marking in stream when adding relationship to a report', () => {
const relatedRestrictions = extractObjectsRestrictionsFromInputs(relInputs, ENTITY_TYPE_CONTAINER_REPORT);
const expected = { markings: ['eaccd139-ec2e-48d9-b2ef-a17ba6e7e938'] };
expect(relatedRestrictions).toEqual(expected);
});
it('should not add inputs object-marking in stream if entity is not container', () => {
const relatedRestrictions = extractObjectsRestrictionsFromInputs(inputs, ENTITY_TYPE_MALWARE);
const expected = { markings: [] };
expect(relatedRestrictions).toEqual(expected);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,94 @@ const eventWithGrantedRefsOnly = {
}
};

const eventWithRelatedRestriction = {
id: '1733757875310-0',
event: 'update',
data: {
version: '4',
type: 'update',
scope: 'external',
message: 'adds `AlienVault` in `Contains`',
origin: {
socket: 'query',
ip: '::1',
user_id: '88ec0c6a-13ce-5e39-b486-354fe4a7084f',
group_ids: [
'eeacced8-cf03-4fd3-bfdb-7398839ef015'
],
organization_ids: [],
user_metadata: {},
referer: 'http://localhost:3000/dashboard'
},
data: {
id: 'report--50ddc6fe-2a84-5c9a-904d-f964a94d1ff7',
spec_version: '2.1',
type: 'report',
extensions: {
'extension-definition--ea279b3e-5c71-4632-ac08-831c66a786ba': {
extension_type: 'property-extension',
id: 'e6babfee-aa64-4e3a-9c67-1a163c178ca0',
type: 'Report',
created_at: '2024-11-18T09:43:49.623Z',
updated_at: '2024-12-09T15:24:28.686Z',
is_inferred: false,
creator_ids: [
'88ec0c6a-13ce-5e39-b486-354fe4a7084f'
],
assignee_ids: [
'71432b4c-34d3-42dc-9b3d-5622b02b4954'
],
workflow_id: 'a4b90e6f-06ae-461a-8dac-666cdb4a5ae7'
}
},
created: '2024-11-18T09:43:40.000Z',
modified: '2024-12-09T14:18:43.125Z',
revoked: false,
confidence: 100,
lang: 'en',
object_marking_refs: [
'marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9'
],
name: 'Report test',
description: 'description',
report_types: [
'threat-report'
],
published: '2024-11-18T09:43:40.000Z',
object_refs: [
'relationship--aefe7518-a66e-5412-a26d-8a5f5eb74fe9',
'campaign--bce98eb5-25a9-5ba7-b4a0-b160a79d0de7',
'grouping--a5c0cace-a530-58ae-907f-eb0d8e41913f',
'threat-actor--fd6b0e6f-96e0-568d-ba24-8a140d0428cd',
'malware--3b0ff6e4-58fc-5312-aa59-ae755dc10189',
'intrusion-set--36319194-19e1-50ac-9163-778b56a1bf12',
'malware--4cc56e92-fc59-500e-966d-bcd32538c248',
'identity--e52b2fa3-2af0-5e53-ad38-17d54b3d61cb'
]
},
context: {
patch: [
{
op: 'add',
path: '/object_refs/7',
value: 'identity--e52b2fa3-2af0-5e53-ad38-17d54b3d61cb'
}
],
reverse_patch: [
{
op: 'remove',
path: '/object_refs/7'
}
],
related_restrictions: {
markings: [
'4584aeee-10b6-47a7-808e-603440642285'
]
},
}
}
};

describe('History manager test resolveGrantedRefsIds', () => {
it('should return empty map if granted refs ids are present', async () => {
const organizationByIdsMap = await resolveGrantedRefsIds(testContext, [eventWithGrantedRefIds]);
Expand Down Expand Up @@ -158,4 +246,18 @@ describe('history manager test buildHistoryElementsFromEvents', () => {
expect(historyElement.organization_ids).toEqual(eventWithGrantedRefsOnly.data.origin.organization_ids);
expect(historyElement['rel_granted.internal_id'].length).toEqual(1);
});
it('should build history with markins for related entities', async () => {
const historyElements = await buildHistoryElementsFromEvents(testContext, [eventWithRelatedRestriction]);

expect(historyElements.length).toEqual(1);
const historyElement = historyElements[0];
expect(historyElement.internal_id).toEqual(eventWithRelatedRestriction.id);
expect(historyElement._index).toEqual(INDEX_HISTORY);
expect(historyElement.entity_type).toEqual(ENTITY_TYPE_HISTORY);
expect(historyElement.event_type).toEqual('mutation');
expect(historyElement.event_scope).toEqual(eventWithRelatedRestriction.event);
expect(historyElement.user_id).toEqual(eventWithRelatedRestriction.data.origin.user_id);
expect(historyElement.group_ids).toEqual(eventWithRelatedRestriction.data.origin.group_ids);
expect(historyElement['rel_object-marking.internal_id'].length).toEqual(2);
});
});

0 comments on commit af13f58

Please sign in to comment.