-
Notifications
You must be signed in to change notification settings - Fork 946
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[backend] crop effective confidence level in [0-100] (#5759)
- Loading branch information
Showing
12 changed files
with
363 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
opencti-platform/opencti-graphql/src/utils/confidence-level.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import type { AuthUser } from '../types/user'; | ||
import { cropNumber } from './math'; | ||
|
||
export const computeUserEffectiveConfidenceLevel = (user: AuthUser) => { | ||
// if user has a specific confidence level, it overrides everything and we return it | ||
if (user.user_confidence_level) { | ||
return { | ||
// we make sure the levels are cropped in between 0-100 | ||
// other values were possibly injected in octi <6.0 though API calls | ||
max_confidence: cropNumber(user.user_confidence_level.max_confidence, 0, 100), | ||
overrides: user.user_confidence_level.overrides.map((override) => ({ | ||
max_confidence: cropNumber(override.max_confidence, 0, 100), | ||
entity_type: override.entity_type, | ||
})), | ||
source: user, | ||
}; | ||
} | ||
|
||
// otherwise we get all groups for this user, and select the lowest max_confidence found | ||
let minLevel = null; | ||
let source = null; | ||
if (user.groups) { | ||
for (let i = 0; i < user.groups.length; i += 1) { | ||
// groups were not migrated when introducing group_confidence_level, so group_confidence_level might be null | ||
const groupLevel = user.groups[i].group_confidence_level?.max_confidence ?? null; | ||
if (groupLevel !== null && (minLevel === null || groupLevel < minLevel)) { | ||
minLevel = groupLevel; | ||
source = user.groups[i]; | ||
} | ||
} | ||
} | ||
|
||
if (minLevel !== null) { | ||
return { | ||
max_confidence: cropNumber(minLevel, 0, 100), | ||
// TODO: handle overrides and their sources | ||
overrides: [], | ||
source, | ||
}; | ||
} | ||
|
||
// finally, if this user has no effective confidence level, we can return null | ||
return null; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { FunctionalError } from '../config/errors'; | ||
|
||
export const cropNumber = (value: number, min: number, max: number) => { | ||
if (!Number.isFinite(value) || !Number.isFinite(min) || !Number.isFinite(max)) { | ||
throw FunctionalError('Cannot process non-finite input'); | ||
} | ||
if (min > max) { | ||
throw FunctionalError('min cannot be greater than max'); | ||
} | ||
|
||
return Math.max(Math.min(value, max), min); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
opencti-platform/opencti-graphql/tests/01-unit/utils/confidence-level-test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { describe, expect, it } from 'vitest'; | ||
import { computeUserEffectiveConfidenceLevel } from '../../../src/utils/confidence-level'; | ||
import type { AuthUser } from '../../../src/types/user'; | ||
|
||
describe('Confidence level utilities', () => { | ||
it('computeUserEffectiveConfidenceLevel should correctly compute the effective level', async () => { | ||
const group70 = { | ||
id: 'group70', | ||
group_confidence_level: { | ||
max_confidence: 70, | ||
overrides: [], | ||
} | ||
}; | ||
|
||
const group80 = { | ||
id: 'group80', | ||
group_confidence_level: { | ||
max_confidence: 70, | ||
overrides: [], | ||
} | ||
}; | ||
|
||
const groupNull = { | ||
id: 'groupNull', | ||
group_confidence_level: null | ||
}; | ||
|
||
// minimal subset of a real User | ||
const userA = { | ||
id: 'userA', | ||
user_confidence_level: { | ||
max_confidence: 30, | ||
overrides: [], | ||
}, | ||
groups: [group70, group80], | ||
}; | ||
expect(computeUserEffectiveConfidenceLevel(userA as unknown as AuthUser)).toEqual({ | ||
max_confidence: 30, | ||
overrides: [], | ||
source: userA, | ||
}); | ||
|
||
const userB = { | ||
id: 'userB', | ||
user_confidence_level: null, | ||
groups: [group70, group80], | ||
}; | ||
expect(computeUserEffectiveConfidenceLevel(userB as unknown as AuthUser)).toEqual({ | ||
max_confidence: 70, | ||
overrides: [], | ||
source: group70, | ||
}); | ||
|
||
const userC = { | ||
user_confidence_level: null, | ||
groups: [groupNull, group70, groupNull], | ||
}; | ||
expect(computeUserEffectiveConfidenceLevel(userC as unknown as AuthUser)).toEqual({ | ||
max_confidence: 70, | ||
overrides: [], | ||
source: group70, | ||
}); | ||
|
||
const userD = { | ||
user_confidence_level: null, | ||
groups: [groupNull, groupNull], | ||
}; | ||
expect(computeUserEffectiveConfidenceLevel(userD as unknown as AuthUser)).toBeNull(); | ||
|
||
const userE = { | ||
user_confidence_level: null, | ||
groups: [], | ||
}; | ||
expect(computeUserEffectiveConfidenceLevel(userE as unknown as AuthUser)).toBeNull(); | ||
}); | ||
}); |
20 changes: 20 additions & 0 deletions
20
opencti-platform/opencti-graphql/tests/01-unit/utils/math-test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { describe, expect, it } from 'vitest'; | ||
import { cropNumber } from '../../../src/utils/math'; | ||
import { FunctionalError } from '../../../src/config/errors'; | ||
|
||
describe('Math utilities: cropNumber', () => { | ||
it('should crop value with various min/max inputs', () => { | ||
expect(cropNumber(50, 10, 100)).toEqual(50); | ||
expect(cropNumber(50, 60, 100)).toEqual(60); | ||
expect(cropNumber(50, 10, 30)).toEqual(30); | ||
|
||
expect(() => cropNumber(50, 100, 10)) | ||
.toThrow(FunctionalError('min cannot be greater than max')); | ||
expect(() => cropNumber(NaN, 10, 80)) | ||
.toThrow(FunctionalError('Cannot process non-finite input')); | ||
expect(() => cropNumber(50, NaN, 80)) | ||
.toThrow(FunctionalError('Cannot process non-finite input')); | ||
expect(() => cropNumber(50, 10, NaN)) | ||
.toThrow(FunctionalError('Cannot process non-finite input')); | ||
}); | ||
}); |
Oops, something went wrong.