Skip to content

Commit 3b945eb

Browse files
[backend] Limit inference explanations (#9814)
1 parent 9631bf9 commit 3b945eb

File tree

1 file changed

+39
-16
lines changed

1 file changed

+39
-16
lines changed

opencti-platform/opencti-graphql/src/database/middleware.js

+39-16
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Promise } from 'bluebird';
55
import { compareUnsorted } from 'js-deep-equals';
66
import { SEMATTRS_DB_NAME, SEMATTRS_DB_OPERATION } from '@opentelemetry/semantic-conventions';
77
import * as jsonpatch from 'fast-json-patch';
8+
import nconf from 'nconf';
89
import {
910
ALREADY_DELETED_ERROR,
1011
AlreadyDeletedError,
@@ -211,7 +212,8 @@ import { getDraftContext } from '../utils/draftContext';
211212
import { getDraftChanges, isDraftSupportedEntity } from './draft-utils';
212213

213214
// region global variables
214-
const MAX_BATCH_SIZE = 300;
215+
const MAX_BATCH_SIZE = nconf.get('elasticsearch:batch_loader_max_size') ?? 300;
216+
const MAX_EXPLANATIONS_PER_RULE = nconf.get('rule_engine:max_explanations_per_rule') ?? 100;
215217
// endregion
216218

217219
// region Loader common
@@ -2332,31 +2334,52 @@ const createRuleDataPatch = (instance) => {
23322334
}
23332335
return patch;
23342336
};
2335-
const upsertEntityRule = async (context, user, instance, input, opts = {}) => {
2336-
logApp.info('Upsert inferred entity', { input });
2337-
const { fromRule } = opts;
2338-
const updatedRule = input[fromRule];
2337+
2338+
const getRuleExplanationsSize = (fromRule, instance) => {
2339+
return (instance[fromRule] ?? []).flat().length;
2340+
};
2341+
2342+
const createUpsertRulePatch = async (instance, input, opts = {}) => {
2343+
const { fromRule, fromRuleDeletion = false } = opts;
2344+
// 01 - Limit the number of element for the rule
2345+
const updatedRule = fromRuleDeletion ? input[fromRule] : (input[fromRule] ?? []).slice(-MAX_EXPLANATIONS_PER_RULE);
23392346
const rulePatch = { [fromRule]: updatedRule };
23402347
const ruleInstance = R.mergeRight(instance, rulePatch);
2348+
// 02 - Create the patch
23412349
const innerPatch = createRuleDataPatch(ruleInstance);
2342-
const patch = { ...rulePatch, ...innerPatch };
2350+
return { ...rulePatch, ...innerPatch };
2351+
};
2352+
const upsertEntityRule = async (context, user, instance, input, opts = {}) => {
2353+
const { fromRule } = opts;
2354+
// 01. If relation already have max explanation, don't do anything
2355+
// Strict equals to clean existing element with too many explanations
2356+
const ruleExplanationsSize = getRuleExplanationsSize(fromRule, instance);
2357+
if (ruleExplanationsSize === MAX_EXPLANATIONS_PER_RULE) {
2358+
return instance;
2359+
}
2360+
logApp.debug('Upsert inferred entity', { input });
2361+
const patch = await createUpsertRulePatch(instance, input, opts);
23432362
const element = await storeLoadByIdWithRefs(context, user, instance.internal_id, { type: instance.entity_type });
23442363
return await patchAttributeFromLoadedWithRefs(context, RULE_MANAGER_USER, element, patch, opts);
23452364
};
23462365
const upsertRelationRule = async (context, user, instance, input, opts = {}) => {
2347-
const { fromRule, ruleOverride = false } = opts;
2348-
// 01 - Update the rule
2366+
const { fromRule, fromRuleDeletion = false } = opts;
2367+
// 01. If relation already have max explanation, don't do anything
2368+
// Strict equals to clean existing element with too many explanations
2369+
const ruleExplanationsSize = getRuleExplanationsSize(fromRule, instance);
2370+
if (!fromRuleDeletion && ruleExplanationsSize === MAX_EXPLANATIONS_PER_RULE) {
2371+
return instance;
2372+
}
2373+
logApp.debug('Upsert inferred relation', { input });
2374+
// 02 - Update the rule
23492375
const updatedRule = input[fromRule];
2350-
if (!ruleOverride) {
2376+
if (!fromRuleDeletion) {
23512377
const keepRuleHashes = input[fromRule].map((i) => i.hash);
23522378
const instanceRuleToKeep = (instance[fromRule] ?? []).filter((i) => !keepRuleHashes.includes(i.hash));
23532379
updatedRule.push(...instanceRuleToKeep);
23542380
}
2355-
const rulePatch = { [fromRule]: updatedRule };
2356-
const ruleInstance = R.mergeRight(instance, rulePatch);
2357-
const innerPatch = createRuleDataPatch(ruleInstance);
2358-
const patch = { ...rulePatch, ...innerPatch };
2359-
logApp.info('Upsert inferred relation', { id: instance.id, relation: patch });
2381+
// 03 - Create the patch
2382+
const patch = await createUpsertRulePatch(instance, input, opts);
23602383
const element = await storeLoadByIdWithRefs(context, user, instance.internal_id, { type: instance.entity_type });
23612384
return await patchAttributeFromLoadedWithRefs(context, RULE_MANAGER_USER, element, patch, opts);
23622385
};
@@ -3316,13 +3339,13 @@ export const deleteInferredRuleElement = async (rule, instance, deletedDependenc
33163339
// If not we need to clean the rule and keep the element for other rules.
33173340
logApp.info('Cleanup inferred element', { rule, id: instance.id });
33183341
const input = { [completeRuleName]: null };
3319-
const upsertOpts = { fromRule, ruleOverride: true };
3342+
const upsertOpts = { fromRule, fromRuleDeletion: true };
33203343
await upsertRelationRule(context, RULE_MANAGER_USER, instance, input, upsertOpts);
33213344
} else {
33223345
logApp.info('Upsert inferred element', { rule, id: instance.id });
33233346
// Rule still have other explanation, update the rule
33243347
const input = { [completeRuleName]: rebuildRuleContent };
3325-
const ruleOpts = { fromRule, ruleOverride: true };
3348+
const ruleOpts = { fromRule, fromRuleDeletion: true };
33263349
await upsertRelationRule(context, RULE_MANAGER_USER, instance, input, ruleOpts);
33273350
}
33283351
} catch (err) {

0 commit comments

Comments
 (0)