Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[backend] Add relatedRestrictions in stream #9251

Merged
merged 9 commits into from
Jan 10, 2025
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);
});
});
Loading