Skip to content

Commit 7fdf094

Browse files
[frontend/backend] enable adding relationships as representation entity targets (#9395)
Co-authored-by: Landry Trebon <[email protected]>
1 parent 77921df commit 7fdf094

File tree

4 files changed

+54
-11
lines changed

4 files changed

+54
-11
lines changed

opencti-platform/opencti-front/src/private/components/data/csvMapper/representations/attributes/CsvMapperRepresentationAttributeRefForm.tsx

+11-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { isEmptyField } from '../../../../../../utils/utils';
1717
import useAuth from '../../../../../../utils/hooks/useAuth';
1818
import { resolveTypesForRelationship, resolveTypesForRelationshipRef } from '../../../../../../utils/Relation';
1919
import { useFormatter } from '../../../../../../components/i18n';
20-
import { isStixCoreObjects } from '../../../../../../utils/stixTypeUtils';
20+
import { isStixCoreObjects, isStixCoreRelationships } from '../../../../../../utils/stixTypeUtils';
2121

2222
// Deprecated - https://mui.com/system/styles/basics/
2323
// Do not use it for new code.
@@ -59,7 +59,7 @@ CsvMapperRepresentationAttributeRefFormProps
5959

6060
const { name, value } = field;
6161
const { setFieldValue, values } = form;
62-
const { entity_representations } = values;
62+
const { entity_representations, relationship_representations } = values;
6363

6464
const [fromType, fromId] = getInfoForRef(
6565
Object.values(representation.attributes),
@@ -117,8 +117,12 @@ CsvMapperRepresentationAttributeRefFormProps
117117
schema.sdos.map((sdo) => sdo.label).forEach((sdoType) => everyRepresentationTypes.push(sdoType));
118118
schema.scos.map((sco) => sco.label).forEach((scoType) => everyRepresentationTypes.push(scoType));
119119
}
120+
if (isStixCoreRelationships(everyRepresentationTypes)) {
121+
schema.scrs.map((sco) => sco.label).forEach((srcType) => everyRepresentationTypes.push(srcType));
122+
}
123+
const allElements = [...entity_representations, ...relationship_representations];
120124
options = filterOptions(
121-
entity_representations
125+
allElements
122126
.filter((r) => r.target_type && everyRepresentationTypes.includes(r.target_type)),
123127
);
124128
}
@@ -189,7 +193,10 @@ CsvMapperRepresentationAttributeRefFormProps
189193
autoSelect={false}
190194
autoHighlight
191195
multiple
192-
getOptionLabel={(option) => representationLabel(entity_representations.indexOf(option), option, t_i18n)}
196+
getOptionLabel={(option) => {
197+
const optionIndex = entity_representations.indexOf(option) >= 0 ? entity_representations.indexOf(option) : relationship_representations.indexOf(option);
198+
return representationLabel(optionIndex, option, t_i18n);
199+
}}
193200
options={options}
194201
value={getBasedOnRepresentations(value, options) || null}
195202
onChange={(_, val) => onValueChange(val)}

opencti-platform/opencti-front/src/utils/stixTypeUtils.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export const isStixCyberObservables = (stixCoreObjectTypes?: string[]) => stixCoreObjectTypes?.includes('Stix-Cyber-Observable');
22
export const isStixCoreObjects = (stixCoreObjectTypes?: string[]) => !stixCoreObjectTypes || stixCoreObjectTypes.includes('Stix-Core-Object');
3+
export const isStixCoreRelationships = (stixCoreObjectTypes?: string[]) => stixCoreObjectTypes?.includes('stix-core-relationship');
34

45
export const computeTargetStixDomainObjectTypes = (stixCoreObjectTypes: string[]): string[] => {
56
const finalStixCoreObjectTypes = stixCoreObjectTypes || ['Stix-Core-Object'];

opencti-platform/opencti-graphql/src/parser/csv-mapper.ts

+38-5
Original file line numberDiff line numberDiff line change
@@ -381,15 +381,31 @@ export const mappingProcess = async (
381381
// Resolution des representations & markings - refIds = default values for representation attributes
382382
const { representations, user_chosen_markings } = mapper;
383383

384-
const representationEntities = representations
385-
.filter((r) => r.type === CsvMapperRepresentationType.Entity)
386-
.sort((r1, r2) => r1.attributes.filter((attr) => attr.based_on).length - r2.attributes.filter((attr) => attr.based_on).length);
387384
const representationRelationships = representations.filter((r) => r.type === CsvMapperRepresentationType.Relationship);
385+
386+
const representationEntitiesWithoutBasedOnRelationships = representations
387+
.filter((r) => {
388+
const isEntity = r.type === CsvMapperRepresentationType.Entity;
389+
const entityHasRefToRelations = !r.attributes.some((a) => {
390+
// Check for each attribute of entity if it has based_on representations
391+
return a.based_on?.representations?.some((b) => {
392+
// Check if at least one of based_on ref is a relation in CSV Mapper
393+
return representationRelationships.some((rel) => rel.id === b);
394+
});
395+
});
396+
return isEntity && entityHasRefToRelations;
397+
})
398+
.sort((r1, r2) => r1.attributes.filter((attr) => attr.based_on).length - r2.attributes.filter((attr) => attr.based_on).length);
399+
400+
// representations thar are not in representationEntitiesWithoutBasedOnRelationships
401+
const representationEntitiesWithBasedOnRelationships = representations
402+
.filter((r) => r.type === CsvMapperRepresentationType.Entity && !representationEntitiesWithoutBasedOnRelationships.some((r1) => r1.id === r.id));
403+
388404
const results = new Map<string, Record<string, InputType>>();
389405

390406
// 1. entities sort by no based on at first
391-
for (let i = 0; i < representationEntities.length; i += 1) {
392-
const representation = representationEntities[i];
407+
for (let i = 0; i < representationEntitiesWithoutBasedOnRelationships.length; i += 1) {
408+
const representation = representationEntitiesWithoutBasedOnRelationships[i];
393409
const input = await mapRecord(
394410
context,
395411
user,
@@ -420,5 +436,22 @@ export const mappingProcess = async (
420436
results.set(representation.id, input);
421437
}
422438
}
439+
440+
// 3. entities with based on relationships at last
441+
for (let i = 0; i < representationEntitiesWithBasedOnRelationships.length; i += 1) {
442+
const representation = representationEntitiesWithBasedOnRelationships[i];
443+
const input = await mapRecord(
444+
context,
445+
user,
446+
record,
447+
representation,
448+
results,
449+
refEntities,
450+
user_chosen_markings ?? []
451+
);
452+
if (input) {
453+
results.set(representation.id, input);
454+
}
455+
}
423456
return Array.from(results.values());
424457
};

opencti-platform/opencti-graphql/src/schema/stixRefRelationship.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
ABSTRACT_STIX_CORE_OBJECT,
3+
ABSTRACT_STIX_CORE_RELATIONSHIP,
34
ABSTRACT_STIX_CYBER_OBSERVABLE_RELATIONSHIP,
45
ABSTRACT_STIX_META_RELATIONSHIP,
56
ABSTRACT_STIX_REF_RELATIONSHIP,
@@ -15,7 +16,8 @@ import {
1516
INPUT_MARKINGS,
1617
INPUT_OBJECTS,
1718
INPUT_PARTICIPANT,
18-
INPUT_WORKS
19+
INPUT_WORKS,
20+
STIX_TYPE_SIGHTING
1921
} from './general';
2022
import {
2123
ENTITY_TYPE_IDENTITY_INDIVIDUAL,
@@ -712,7 +714,7 @@ export const objects: RefAttribute = {
712714
},
713715
datable: false,
714716
isFilterable: true,
715-
toTypes: [ABSTRACT_STIX_CORE_OBJECT],
717+
toTypes: [ABSTRACT_STIX_CORE_OBJECT, ABSTRACT_STIX_CORE_RELATIONSHIP, ABSTRACT_STIX_REF_RELATIONSHIP, STIX_TYPE_SIGHTING],
716718
};
717719

718720
export const objectOrganization: RefAttribute = {

0 commit comments

Comments
 (0)