@@ -128,25 +128,46 @@ export const buildUpdatePatchForUpsert = (user, resolvedElement, type, basePatch
128128 if ( type === ENTITY_TYPE_INDICATOR ) {
129129 if ( resolvedElement . decay_applied_rule ) {
130130 // Do not compute decay again when:
131- // - base score does not change
132- // - same userIs has already updated to the same score previously
131+ // - base score does not change (indicator is still at its initial score, decay not started yet)
133132 const isScoreInUpsertSameAsBaseScore = updatePatch . decay_base_score === resolvedElement . decay_base_score && updatePatch . decay_base_score === resolvedElement . x_opencti_score ;
133+ // - same userId has already updated to the same score previously
134134 const hasSameScoreChangedBySameSource = hasSameSourceAlreadyUpdateThisScore ( user . id , updatePatch . x_opencti_score , resolvedElement . decay_history ) ;
135- if ( isScoreInUpsertSameAsBaseScore || hasSameScoreChangedBySameSource ) {
136- logApp . debug ( `[OPENCTI][DECAY] on upsert indicator skip decay, do not change score, keep:${ resolvedElement . x_opencti_score } ` , { elementScore : resolvedElement . x_opencti_score , patchScore : updatePatch . x_opencti_score , isScoreInUpsertSameAsBaseScore, hasSameScoreChangedBySameSource } ) ;
135+ // - the live score in the upsert is identical to the current live score (e.g. a Playbook re-ingesting
136+ // the indicator bundle without any score change — the bundle carries the current decayed score,
137+ // not the original base score, so addIndicator incorrectly computes a new decay start from it)
138+ const isLiveScoreUnchanged = updatePatch . x_opencti_score === resolvedElement . x_opencti_score ;
139+ if ( isScoreInUpsertSameAsBaseScore || hasSameScoreChangedBySameSource || isLiveScoreUnchanged ) {
140+ logApp . debug (
141+ `[OPENCTI][DECAY] on upsert indicator skip decay, do not change score, keep:${ resolvedElement . x_opencti_score } ` ,
142+ {
143+ elementScore : resolvedElement . x_opencti_score ,
144+ patchScore : updatePatch . x_opencti_score ,
145+ isScoreInUpsertSameAsBaseScore,
146+ hasSameScoreChangedBySameSource,
147+ isLiveScoreUnchanged,
148+ } ,
149+ ) ;
137150 // don't reset score, valid_from & valid_until
138151 updatePatch . x_opencti_score = resolvedElement . x_opencti_score ; // don't change the score
139152 updatePatch . valid_from = resolvedElement . valid_from ;
140153 updatePatch . valid_until = resolvedElement . valid_until ;
141154 // don't reset decay attributes
142155 updatePatch . revoked = resolvedElement . revoked ;
156+ updatePatch . decay_base_score = resolvedElement . decay_base_score ; // preserve the original base score
143157 updatePatch . decay_base_score_date = resolvedElement . decay_base_score_date ;
144158 updatePatch . decay_applied_rule = resolvedElement . decay_applied_rule ;
145159 updatePatch . decay_history = [ ] ; // History is multiple, forcing to empty array will prevent any modification
146160 updatePatch . decay_next_reaction_date = resolvedElement . decay_next_reaction_date ;
147161 } else {
148- // As base_score as change, decay will be reset by upsert
149- logApp . debug ( '[OPENCTI][DECAY] Decay is restarted' , { elementScore : resolvedElement . x_opencti_score , initialPatchScore : basePatch . x_opencti_score , updatePatchScore : updatePatch . x_opencti_score } ) ;
162+ // As base_score has changed, decay will be reset by upsert
163+ logApp . debug (
164+ '[OPENCTI][DECAY] Decay is restarted' ,
165+ {
166+ elementScore : resolvedElement . x_opencti_score ,
167+ initialPatchScore : basePatch . x_opencti_score ,
168+ updatePatchScore : updatePatch . x_opencti_score ,
169+ } ,
170+ ) ;
150171 }
151172 }
152173
0 commit comments