| skipped 5 lines |
6 | 6 | | createInferredRelation, |
7 | 7 | | deleteInferredRuleElement, |
8 | 8 | | internalFindByIds, |
9 | | - | storeLoadByIdWithRefs, |
| 9 | + | internalLoadById, |
10 | 10 | | } from '../database/middleware'; |
11 | 11 | | |
12 | 12 | | import { RELATION_OBJECT } from '../schema/stixMetaRelationship'; |
13 | | - | import { createRuleContent, RULE_MANAGER_USER } from './rules'; |
14 | | - | import { INPUT_OBJECTS } from '../schema/general'; |
15 | | - | import { generateInternalType } from '../schema/schemaUtils'; |
16 | | - | import type { RuleDefinition, RelationTypes, RuleRuntime } from '../types/rules'; |
| 13 | + | import { createRuleContent } from './rules'; |
| 14 | + | import { generateInternalType, getParentTypes } from '../schema/schemaUtils'; |
| 15 | + | import type { RelationTypes, RuleDefinition, RuleRuntime } from '../types/rules'; |
17 | 16 | | import type { StixId, StixObject } from '../types/stix-common'; |
18 | 17 | | import type { StixReport } from '../types/stix-sdo'; |
19 | 18 | | import type { StixRelation } from '../types/stix-sro'; |
20 | | - | import type { BasicStoreRelation, StoreEntity, StoreObject } from '../types/store'; |
| 19 | + | import type { BasicStoreEntity, BasicStoreRelation, StoreObject } from '../types/store'; |
21 | 20 | | import { STIX_EXT_OCTI } from '../types/stix-extensions'; |
22 | 21 | | import { listAllRelations } from '../database/middleware-loader'; |
23 | 22 | | import type { DependenciesDeleteEvent, Event, RelationCreation, RuleEvent, UpdateEvent } from '../types/event'; |
24 | 23 | | import { |
25 | | - | generateUpdateMessage, |
| 24 | + | EVENT_TYPE_DELETE_DEPENDENCIES, |
26 | 25 | | UPDATE_OPERATION_ADD, |
27 | 26 | | UPDATE_OPERATION_REMOVE, |
28 | 27 | | UPDATE_OPERATION_REPLACE |
29 | 28 | | } from '../database/utils'; |
30 | | - | import { storeUpdateEvent } from '../database/redis'; |
31 | 29 | | import type { AuthContext } from '../types/user'; |
32 | | - | import { executionContext } from '../utils/access'; |
| 30 | + | import { executionContext, RULE_MANAGER_USER } from '../utils/access'; |
| 31 | + | import { buildCreateEvent } from '../database/redis'; |
33 | 32 | | |
34 | | - | const INFERRED_OBJECT_REF_PATH = `/extensions/${STIX_EXT_OCTI}/object_refs_inferred`; |
35 | 33 | | const buildContainerRefsRule = (ruleDefinition: RuleDefinition, containerType: string, relationTypes: RelationTypes): RuleRuntime => { |
36 | 34 | | const { id } = ruleDefinition; |
37 | 35 | | const identityRefFilter = (ref: string) => { |
| skipped 10 lines |
48 | 46 | | ]; |
49 | 47 | | }; |
50 | 48 | | type ArrayRefs = Array<{ partOfFromId: string, partOfId: string, partOfTargetId: string }>; |
51 | | - | const createObjectRefsInferences = async (context: AuthContext, reportId: string, refs: ArrayRefs) => { |
52 | | - | const report = await storeLoadByIdWithRefs(context, RULE_MANAGER_USER, reportId) as StoreEntity; |
53 | | - | const reportObjectRefIds = report[INPUT_OBJECTS].map((r) => r.internal_id); |
| 49 | + | const createObjectRefsInferences = async (context: AuthContext, reportId: string, refs: ArrayRefs): Promise<Array<Event>> => { |
| 50 | + | const events:Array<Event> = []; |
| 51 | + | const report = await internalLoadById(context, RULE_MANAGER_USER, reportId) as unknown as BasicStoreEntity; |
| 52 | + | const reportObjectRefIds = report[RELATION_OBJECT] ?? []; |
54 | 53 | | const opts = { publishStreamEvent: false }; |
55 | | - | const targetIds = refs.map((r) => [r.partOfId, r.partOfTargetId]).flat(); |
56 | | - | const targetsMap = await internalFindByIds(context, RULE_MANAGER_USER, targetIds, { toMap: true }) as any; |
57 | | - | const createdTargets: Array<StoreEntity> = []; |
58 | 54 | | for (let index = 0; index < refs.length; index += 1) { |
59 | 55 | | const { partOfFromId, partOfId, partOfTargetId } = refs[index]; |
60 | 56 | | // When generating inferences, no need to listen internal generated events |
| skipped 4 lines |
65 | 61 | | const inputForRelation = { fromId: reportId, toId: partOfId, relationship_type: RELATION_OBJECT }; |
66 | 62 | | const inferredRelation = await createInferredRelation(context, inputForRelation, ruleRelationContent, opts) as RelationCreation; |
67 | 63 | | if (inferredRelation.isCreation) { |
68 | | - | const target = targetsMap[partOfId]; |
69 | | - | target.i_relation = inferredRelation.element; |
70 | | - | createdTargets.push(target); |
| 64 | + | const event = buildCreateEvent(RULE_MANAGER_USER, inferredRelation.element, ''); |
| 65 | + | events.push(event); |
71 | 66 | | } |
72 | 67 | | } |
73 | 68 | | // ----------------------------------------------------------------------------------------------------------- |
| skipped 2 lines |
76 | 71 | | const inputForIdentity = { fromId: reportId, toId: partOfTargetId, relationship_type: RELATION_OBJECT }; |
77 | 72 | | const inferredTarget = await createInferredRelation(context, inputForIdentity, ruleIdentityContent, opts) as RelationCreation; |
78 | 73 | | if (inferredTarget.isCreation) { |
79 | | - | const target = targetsMap[partOfTargetId]; |
80 | | - | target.i_relation = inferredTarget.element; |
81 | | - | createdTargets.push(target); |
| 74 | + | const event = buildCreateEvent(RULE_MANAGER_USER, inferredTarget.element, ''); |
| 75 | + | events.push(event); |
82 | 76 | | } |
83 | 77 | | } |
84 | 78 | | } |
85 | | - | if (createdTargets.length > 0) { |
86 | | - | const updatedReport = { ...report }; |
87 | | - | const inputs = [{ key: INPUT_OBJECTS, value: createdTargets, operation: UPDATE_OPERATION_ADD }]; |
88 | | - | const message = generateUpdateMessage(inputs); |
89 | | - | updatedReport[INPUT_OBJECTS] = [...(updatedReport[INPUT_OBJECTS] ?? []), ...createdTargets]; |
90 | | - | await storeUpdateEvent(context, RULE_MANAGER_USER, report, updatedReport, message); |
91 | | - | } |
| 79 | + | return events; |
92 | 80 | | }; |
93 | | - | const handleReportCreation = async (context: AuthContext, report: StixReport, addedIdentityRefs: Array<string>) => { |
| 81 | + | const handleReportCreation = async (context: AuthContext, report: StixReport, addedIdentityRefs: Array<string>): Promise<Array<Event>> => { |
94 | 82 | | const objectRefsToCreate: ArrayRefs = []; |
95 | 83 | | const { id: reportId } = report.extensions[STIX_EXT_OCTI]; |
96 | 84 | | const identities = await internalFindByIds(context, RULE_MANAGER_USER, addedIdentityRefs) as Array<StoreObject>; |
| skipped 8 lines |
105 | 93 | | const listFromArgs = { fromId: fromIds, toTypes: [relationTypes.rightType], callback: listFromCallback }; |
106 | 94 | | await listAllRelations(context, RULE_MANAGER_USER, relationTypes.creationType, listFromArgs); |
107 | 95 | | // update the report |
108 | | - | await createObjectRefsInferences(context, reportId, objectRefsToCreate); |
109 | | - | return []; |
| 96 | + | return createObjectRefsInferences(context, reportId, objectRefsToCreate); |
110 | 97 | | }; |
111 | | - | const handlePartOfRelationCreation = async (context: AuthContext, partOfRelation: StixRelation) => { |
| 98 | + | const handlePartOfRelationCreation = async (context: AuthContext, partOfRelation: StixRelation): Promise<Array<Event>> => { |
| 99 | + | const events: Array<Event> = []; |
112 | 100 | | const { id: partOfId, source_ref: partOfFromId, target_ref: partOfTargetId } = partOfRelation.extensions[STIX_EXT_OCTI]; |
113 | 101 | | const listFromCallback = async (relationships: Array<BasicStoreRelation>) => { |
114 | 102 | | for (let objectRefIndex = 0; objectRefIndex < relationships.length; objectRefIndex += 1) { |
115 | 103 | | const { fromId: reportId } = relationships[objectRefIndex]; |
116 | | - | await createObjectRefsInferences(context, reportId, [{ partOfFromId, partOfId, partOfTargetId }]); |
| 104 | + | const inferredEvents = await createObjectRefsInferences(context, reportId, [{ partOfFromId, partOfId, partOfTargetId }]); |
| 105 | + | events.push(...inferredEvents); |
117 | 106 | | } |
118 | 107 | | }; |
119 | 108 | | const listReportArgs = { fromTypes: [containerType], toId: partOfFromId, callback: listFromCallback }; |
120 | 109 | | await listAllRelations(context, RULE_MANAGER_USER, RELATION_OBJECT, listReportArgs); |
121 | | - | return []; |
| 110 | + | return events; |
| 111 | + | }; |
| 112 | + | const handleObjectRelationCreation = async (context: AuthContext, objectRelation: StixRelation): Promise<Array<Event>> => { |
| 113 | + | const events: Array<Event> = []; |
| 114 | + | const { source_ref: reportId, target_type, target_ref: targetId } = objectRelation.extensions[STIX_EXT_OCTI]; |
| 115 | + | if (target_type === relationTypes.leftType || getParentTypes(target_type).includes(relationTypes.leftType)) { |
| 116 | + | const listFromCallback = async (relationships: Array<BasicStoreRelation>) => { |
| 117 | + | // Get the list of interesting part-of |
| 118 | + | for (let objectRefIndex = 0; objectRefIndex < relationships.length; objectRefIndex += 1) { |
| 119 | + | const { fromId: partOfFromId, internal_id: partOfId, toId: partOfTargetId } = relationships[objectRefIndex]; |
| 120 | + | const inferredEvents = await createObjectRefsInferences(context, reportId, [{ partOfFromId, partOfId, partOfTargetId }]); |
| 121 | + | events.push(...inferredEvents); |
| 122 | + | } |
| 123 | + | }; |
| 124 | + | // List all partOf from this indicator to observables |
| 125 | + | const listReportArgs = { fromId: targetId, toTypes: [relationTypes.rightType], callback: listFromCallback }; |
| 126 | + | await listAllRelations(context, RULE_MANAGER_USER, relationTypes.creationType, listReportArgs); |
| 127 | + | } |
| 128 | + | return events; |
122 | 129 | | }; |
123 | 130 | | const applyInsert = async (data: StixObject): Promise<Array<Event>> => { |
124 | | - | const context = executionContext(ruleDefinition.name); |
| 131 | + | const context = executionContext(ruleDefinition.name, RULE_MANAGER_USER); |
125 | 132 | | const events: Array<Event> = []; |
126 | 133 | | const entityType = generateInternalType(data); |
127 | 134 | | if (entityType === containerType) { |
| skipped 10 lines |
138 | 145 | | if (relationType === relationTypes.creationType) { |
139 | 146 | | return handlePartOfRelationCreation(context, upsertRelation); |
140 | 147 | | } |
| 148 | + | if (relationType === RELATION_OBJECT) { // This event can be generated internally |
| 149 | + | return handleObjectRelationCreation(context, upsertRelation); |
| 150 | + | } |
141 | 151 | | return events; |
142 | 152 | | }; |
143 | 153 | | const applyUpdate = async (data: StixObject, event: UpdateEvent): Promise<Array<RuleEvent>> => { |
144 | | - | const context = executionContext(ruleDefinition.name); |
| 154 | + | const context = executionContext(ruleDefinition.name, RULE_MANAGER_USER); |
145 | 155 | | const events: Array<RuleEvent> = []; |
146 | 156 | | const entityType = generateInternalType(data); |
147 | 157 | | if (entityType === containerType) { |
| skipped 1 lines |
149 | 159 | | const operations: Array<Operation> = event.context.patch; |
150 | 160 | | const previousPatch = event.context.reverse_patch; |
151 | 161 | | const previousData = jsonpatch.applyPatch<StixReport>(R.clone(report), previousPatch).newDocument; |
152 | | - | const refOperations = operations.filter((o) => o.path.startsWith('/object_refs') |
153 | | - | || o.path.startsWith(INFERRED_OBJECT_REF_PATH)); |
| 162 | + | const refOperations = operations.filter((o) => o.path.startsWith('/object_refs')); |
154 | 163 | | const addedRefs: Array<StixId> = []; |
155 | 164 | | const removedRefs: Array<StixId> = []; |
156 | 165 | | // Replace operations behavior |
| skipped 6 lines |
163 | 172 | | const removeObjectIndex = R.last(opPath.split('/')); |
164 | 173 | | if (removeObjectIndex) { |
165 | 174 | | const replaceObjectRefIndex = parseInt(removeObjectIndex, 10); |
166 | | - | const isExtension = replaceOperation.path.startsWith(INFERRED_OBJECT_REF_PATH); |
167 | | - | const baseData = isExtension ? previousData.extensions[STIX_EXT_OCTI].object_refs_inferred : previousData.object_refs; |
168 | | - | const removeRefId = baseData[replaceObjectRefIndex]; |
| 175 | + | const removeRefId = (previousData.object_refs ?? [])[replaceObjectRefIndex]; |
169 | 176 | | removedRefs.push(removeRefId); |
170 | 177 | | } |
171 | 178 | | } |
| skipped 9 lines |
181 | 188 | | for (let removeIndex = 0; removeIndex < removeOperations.length; removeIndex += 1) { |
182 | 189 | | const removeOperation = removeOperations[removeIndex]; |
183 | 190 | | // For remove op we need to look into the previous data, the deleted element |
184 | | - | const isExtension = removeOperation.path.startsWith(INFERRED_OBJECT_REF_PATH); |
185 | | - | const baseData = isExtension ? previousData.extensions[STIX_EXT_OCTI].object_refs_inferred : previousData.object_refs; |
| 191 | + | const previousObjectRefs = previousData.object_refs; |
186 | 192 | | const opPath = removeOperation.path.substring(removeOperation.path.indexOf('/object_refs')); |
187 | 193 | | const [,, index] = opPath.split('/'); |
188 | 194 | | if (index) { |
189 | 195 | | const replaceObjectRefIndex = parseInt(index, 10); |
190 | | - | const removeRefId = baseData[replaceObjectRefIndex]; |
| 196 | + | const removeRefId = previousObjectRefs[replaceObjectRefIndex]; |
191 | 197 | | removedRefs.push(removeRefId); |
192 | 198 | | } else { |
193 | | - | const removeRefIds = baseData ?? []; |
| 199 | + | const removeRefIds = previousObjectRefs ?? []; |
194 | 200 | | removedRefs.push(...removeRefIds); |
195 | 201 | | } |
196 | 202 | | } |
| skipped 10 lines |
207 | 213 | | // For meta deletion, generate deletion events |
208 | 214 | | const removedRefIdentities = await internalFindByIds(context, RULE_MANAGER_USER, removedIdentityRefs) as Array<StoreObject>; |
209 | 215 | | const removedIds = removedRefIdentities.map((i) => i.internal_id); |
210 | | - | const deleteEvent: DependenciesDeleteEvent = { type: 'delete-dependencies', ids: removedIds.map((ref) => `${ref}_ref`) }; |
| 216 | + | const deleteEvent: DependenciesDeleteEvent = { |
| 217 | + | type: EVENT_TYPE_DELETE_DEPENDENCIES, |
| 218 | + | data: { ids: removedIds.map((ref) => `${ref}_ref`) } |
| 219 | + | }; |
211 | 220 | | events.push(deleteEvent); |
212 | 221 | | } |
213 | 222 | | return events; |
| skipped 17 lines |