@a4anthony/proctorkit-types 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/integrity.d.ts +20 -0
- package/dist/integrity.d.ts.map +1 -0
- package/dist/integrity.js +103 -0
- package/dist/integrity.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/integrity.ts +122 -0
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type IntegrityFindingSeverity = "violation" | "concern" | "info";
|
|
2
|
+
export interface IntegrityCategoryRule {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
tone: string;
|
|
6
|
+
weight: number;
|
|
7
|
+
cap: number;
|
|
8
|
+
severity: IntegrityFindingSeverity;
|
|
9
|
+
match: (kind: string) => boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Canonical integrity-score rules shared by the API, dashboard, and reports.
|
|
13
|
+
* Keep all score consumers on this table so list ordering cannot disagree
|
|
14
|
+
* with the score shown in session review.
|
|
15
|
+
*/
|
|
16
|
+
export declare const INTEGRITY_CATEGORIES: ReadonlyArray<IntegrityCategoryRule>;
|
|
17
|
+
export declare function isIntegrityFlag(kind: string): boolean;
|
|
18
|
+
export declare function computeIntegrityScoreFromKinds(kinds: readonly string[]): number;
|
|
19
|
+
export declare function computeIntegrityScoreFromKindCounts(counts: ReadonlyMap<string, number>): number;
|
|
20
|
+
//# sourceMappingURL=integrity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.d.ts","sourceRoot":"","sources":["../src/integrity.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC;AAExE,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,wBAAwB,CAAC;IACnC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAClC;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,aAAa,CAAC,qBAAqB,CAiFrE,CAAC;AAEF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAM/E;AAED,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAS/F"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical integrity-score rules shared by the API, dashboard, and reports.
|
|
3
|
+
* Keep all score consumers on this table so list ordering cannot disagree
|
|
4
|
+
* with the score shown in session review.
|
|
5
|
+
*/
|
|
6
|
+
export const INTEGRITY_CATEGORIES = [
|
|
7
|
+
{
|
|
8
|
+
key: "identity",
|
|
9
|
+
label: "Identity mismatch",
|
|
10
|
+
tone: "#dc2626",
|
|
11
|
+
weight: 40,
|
|
12
|
+
cap: 40,
|
|
13
|
+
severity: "violation",
|
|
14
|
+
match: (kind) => kind.startsWith("face.identity-mismatch"),
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: "another-person",
|
|
18
|
+
label: "Another person / device",
|
|
19
|
+
tone: "#ef4444",
|
|
20
|
+
weight: 25,
|
|
21
|
+
cap: 35,
|
|
22
|
+
severity: "violation",
|
|
23
|
+
match: (kind) => kind === "face.multiple" ||
|
|
24
|
+
kind === "face.multiple-at-snapshot" ||
|
|
25
|
+
kind.startsWith("webcam.second-device-detected") ||
|
|
26
|
+
kind.startsWith("webcam.second-screen-detected"),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
key: "prohibited-object",
|
|
30
|
+
label: "Prohibited object",
|
|
31
|
+
tone: "#f97316",
|
|
32
|
+
weight: 18,
|
|
33
|
+
cap: 30,
|
|
34
|
+
severity: "violation",
|
|
35
|
+
match: (kind) => kind.startsWith("webcam.phone-detected") || kind.startsWith("webcam.book-detected"),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: "evasion",
|
|
39
|
+
label: "Monitoring evasion",
|
|
40
|
+
tone: "#dc2626",
|
|
41
|
+
weight: 12,
|
|
42
|
+
cap: 30,
|
|
43
|
+
severity: "violation",
|
|
44
|
+
match: (kind) => kind === "fullscreen.exited" ||
|
|
45
|
+
kind === "screen-share.declined" ||
|
|
46
|
+
kind === "screen-share.stopped",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
key: "window-switching",
|
|
50
|
+
label: "Window / app switching",
|
|
51
|
+
tone: "#f59e0b",
|
|
52
|
+
weight: 3,
|
|
53
|
+
cap: 20,
|
|
54
|
+
severity: "concern",
|
|
55
|
+
match: (kind) => kind === "screen.window-switched" || kind === "focus.lost" || kind === "tab.hidden",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
key: "face-gaze",
|
|
59
|
+
label: "Face absence / gaze",
|
|
60
|
+
tone: "#f43f5e",
|
|
61
|
+
weight: 5,
|
|
62
|
+
cap: 18,
|
|
63
|
+
severity: "concern",
|
|
64
|
+
match: (kind) => kind === "face.lost" ||
|
|
65
|
+
kind === "face.lost-at-snapshot" ||
|
|
66
|
+
kind === "face.gaze-off-screen" ||
|
|
67
|
+
kind === "face.gaze-off-screen-at-snapshot",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
key: "clipboard",
|
|
71
|
+
label: "Clipboard / screenshot",
|
|
72
|
+
tone: "#fb923c",
|
|
73
|
+
weight: 2,
|
|
74
|
+
cap: 12,
|
|
75
|
+
severity: "concern",
|
|
76
|
+
match: (kind) => kind.startsWith("clipboard.") ||
|
|
77
|
+
kind === "screenshot.attempted" ||
|
|
78
|
+
kind === "keyboard.blocked" ||
|
|
79
|
+
kind === "contextmenu.opened",
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
export function isIntegrityFlag(kind) {
|
|
83
|
+
return INTEGRITY_CATEGORIES.some((category) => category.match(kind));
|
|
84
|
+
}
|
|
85
|
+
export function computeIntegrityScoreFromKinds(kinds) {
|
|
86
|
+
const counts = new Map();
|
|
87
|
+
for (const kind of kinds) {
|
|
88
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
89
|
+
}
|
|
90
|
+
return computeIntegrityScoreFromKindCounts(counts);
|
|
91
|
+
}
|
|
92
|
+
export function computeIntegrityScoreFromKindCounts(counts) {
|
|
93
|
+
const penalty = INTEGRITY_CATEGORIES.reduce((total, category) => {
|
|
94
|
+
let count = 0;
|
|
95
|
+
for (const [kind, occurrences] of counts) {
|
|
96
|
+
if (category.match(kind))
|
|
97
|
+
count += occurrences;
|
|
98
|
+
}
|
|
99
|
+
return total + Math.min(category.cap, category.weight * count);
|
|
100
|
+
}, 0);
|
|
101
|
+
return Math.max(0, Math.min(100, Math.round(100 - penalty)));
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.js","sourceRoot":"","sources":["../src/integrity.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
package/src/integrity.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
export type IntegrityFindingSeverity = "violation" | "concern" | "info";
|
|
2
|
+
|
|
3
|
+
export interface IntegrityCategoryRule {
|
|
4
|
+
key: string;
|
|
5
|
+
label: string;
|
|
6
|
+
tone: string;
|
|
7
|
+
weight: number;
|
|
8
|
+
cap: number;
|
|
9
|
+
severity: IntegrityFindingSeverity;
|
|
10
|
+
match: (kind: string) => boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Canonical integrity-score rules shared by the API, dashboard, and reports.
|
|
15
|
+
* Keep all score consumers on this table so list ordering cannot disagree
|
|
16
|
+
* with the score shown in session review.
|
|
17
|
+
*/
|
|
18
|
+
export const INTEGRITY_CATEGORIES: ReadonlyArray<IntegrityCategoryRule> = [
|
|
19
|
+
{
|
|
20
|
+
key: "identity",
|
|
21
|
+
label: "Identity mismatch",
|
|
22
|
+
tone: "#dc2626",
|
|
23
|
+
weight: 40,
|
|
24
|
+
cap: 40,
|
|
25
|
+
severity: "violation",
|
|
26
|
+
match: (kind) => kind.startsWith("face.identity-mismatch"),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
key: "another-person",
|
|
30
|
+
label: "Another person / device",
|
|
31
|
+
tone: "#ef4444",
|
|
32
|
+
weight: 25,
|
|
33
|
+
cap: 35,
|
|
34
|
+
severity: "violation",
|
|
35
|
+
match: (kind) =>
|
|
36
|
+
kind === "face.multiple" ||
|
|
37
|
+
kind === "face.multiple-at-snapshot" ||
|
|
38
|
+
kind.startsWith("webcam.second-device-detected") ||
|
|
39
|
+
kind.startsWith("webcam.second-screen-detected"),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: "prohibited-object",
|
|
43
|
+
label: "Prohibited object",
|
|
44
|
+
tone: "#f97316",
|
|
45
|
+
weight: 18,
|
|
46
|
+
cap: 30,
|
|
47
|
+
severity: "violation",
|
|
48
|
+
match: (kind) =>
|
|
49
|
+
kind.startsWith("webcam.phone-detected") || kind.startsWith("webcam.book-detected"),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
key: "evasion",
|
|
53
|
+
label: "Monitoring evasion",
|
|
54
|
+
tone: "#dc2626",
|
|
55
|
+
weight: 12,
|
|
56
|
+
cap: 30,
|
|
57
|
+
severity: "violation",
|
|
58
|
+
match: (kind) =>
|
|
59
|
+
kind === "fullscreen.exited" ||
|
|
60
|
+
kind === "screen-share.declined" ||
|
|
61
|
+
kind === "screen-share.stopped",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: "window-switching",
|
|
65
|
+
label: "Window / app switching",
|
|
66
|
+
tone: "#f59e0b",
|
|
67
|
+
weight: 3,
|
|
68
|
+
cap: 20,
|
|
69
|
+
severity: "concern",
|
|
70
|
+
match: (kind) =>
|
|
71
|
+
kind === "screen.window-switched" || kind === "focus.lost" || kind === "tab.hidden",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
key: "face-gaze",
|
|
75
|
+
label: "Face absence / gaze",
|
|
76
|
+
tone: "#f43f5e",
|
|
77
|
+
weight: 5,
|
|
78
|
+
cap: 18,
|
|
79
|
+
severity: "concern",
|
|
80
|
+
match: (kind) =>
|
|
81
|
+
kind === "face.lost" ||
|
|
82
|
+
kind === "face.lost-at-snapshot" ||
|
|
83
|
+
kind === "face.gaze-off-screen" ||
|
|
84
|
+
kind === "face.gaze-off-screen-at-snapshot",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
key: "clipboard",
|
|
88
|
+
label: "Clipboard / screenshot",
|
|
89
|
+
tone: "#fb923c",
|
|
90
|
+
weight: 2,
|
|
91
|
+
cap: 12,
|
|
92
|
+
severity: "concern",
|
|
93
|
+
match: (kind) =>
|
|
94
|
+
kind.startsWith("clipboard.") ||
|
|
95
|
+
kind === "screenshot.attempted" ||
|
|
96
|
+
kind === "keyboard.blocked" ||
|
|
97
|
+
kind === "contextmenu.opened",
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
export function isIntegrityFlag(kind: string): boolean {
|
|
102
|
+
return INTEGRITY_CATEGORIES.some((category) => category.match(kind));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function computeIntegrityScoreFromKinds(kinds: readonly string[]): number {
|
|
106
|
+
const counts = new Map<string, number>();
|
|
107
|
+
for (const kind of kinds) {
|
|
108
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
109
|
+
}
|
|
110
|
+
return computeIntegrityScoreFromKindCounts(counts);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function computeIntegrityScoreFromKindCounts(counts: ReadonlyMap<string, number>): number {
|
|
114
|
+
const penalty = INTEGRITY_CATEGORIES.reduce((total, category) => {
|
|
115
|
+
let count = 0;
|
|
116
|
+
for (const [kind, occurrences] of counts) {
|
|
117
|
+
if (category.match(kind)) count += occurrences;
|
|
118
|
+
}
|
|
119
|
+
return total + Math.min(category.cap, category.weight * count);
|
|
120
|
+
}, 0);
|
|
121
|
+
return Math.max(0, Math.min(100, Math.round(100 - penalty)));
|
|
122
|
+
}
|