cyborg-hunter 0.5.0 → 0.7.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/CITATION.cff +29 -0
  3. package/LICENSE +21 -0
  4. package/README.md +78 -21
  5. package/package.json +10 -3
  6. package/src/cli/analyzers/edge-exit.js +4 -1
  7. package/src/cli/analyzers/phase-scope.js +83 -0
  8. package/src/cli/analyzers/summary.js +161 -28
  9. package/src/cli/analyzers/triage.js +59 -27
  10. package/src/cli/config.js +26 -1
  11. package/src/cli/ingest.js +623 -41
  12. package/src/cli/init.js +1 -1
  13. package/src/cli/renderers/event-log.js +18 -19
  14. package/src/cli/renderers/extensions.js +12 -3
  15. package/src/cli/renderers/html-index.js +163 -24
  16. package/src/cli/renderers/replay-assets.js +177 -0
  17. package/src/cli/renderers/replay-viewer.client.js +1022 -0
  18. package/src/cli/renderers/session-timeline.js +917 -0
  19. package/src/cli/renderers/summary-csv.js +5 -0
  20. package/src/cli/renderers/trajectories.js +68 -7
  21. package/src/cli/renderers/triage-md.js +10 -4
  22. package/src/cli/renderers/typing-profile.js +7 -1
  23. package/src/cli/report.js +42 -8
  24. package/src/core/monitor.js +60 -7
  25. package/src/core/scoring.js +11 -2
  26. package/src/core/signals/browser.js +51 -18
  27. package/src/core/signals/clipboard.js +10 -2
  28. package/src/core/signals/dom-protection.js +9 -0
  29. package/src/core/signals/focus.js +16 -2
  30. package/src/jspsych/extension-cyborg-hunter-replay.js +135 -0
  31. package/src/jspsych/extension-cyborg-hunter.js +9 -2
  32. package/src/jspsych/extension-guard-friction.js +32 -12
  33. package/src/jspsych/extension-guard-honeypot.js +25 -1
  34. package/src/replay/capture-dom.js +575 -0
  35. package/src/replay/capture-trace.js +468 -0
  36. package/src/replay/index.js +104 -0
  37. package/src/replay/persistence.js +141 -0
  38. package/src/replay/recorder.js +315 -0
  39. package/src/replay/serializer.js +119 -0
  40. package/src/shared/constants.js +12 -6
  41. package/src/shared/schema.js +5 -0
  42. package/src/shared/validation.js +55 -0
  43. package/dist/cyborg-hunter.esm.js +0 -1527
  44. package/dist/cyborg-hunter.min.js +0 -6
  45. package/dist/extension-cyborg-hunter.js +0 -1
  46. package/dist/extension-guard-friction.js +0 -36
  47. package/dist/extension-guard-honeypot.js +0 -1
  48. package/src/cli/renderers/tab-timeline.js +0 -149
@@ -8,6 +8,7 @@ import { DEFAULT_THRESHOLDS } from './constants.js';
8
8
  // All recognized config keys across both browser library and CLI.
9
9
  // Unknown keys trigger a warning with the closest match suggestion.
10
10
  const ALL_KNOWN_KEYS = [
11
+ 'replayDir',
11
12
  ...Object.keys(DEFAULT_CLI_CONFIG),
12
13
  ...Object.keys(DEFAULT_THRESHOLDS),
13
14
  'preset', 'participantId', 'signals', 'thresholds', 'scoring',
@@ -36,6 +37,60 @@ export function validateConfig(config, knownKeys = ALL_KNOWN_KEYS) {
36
37
  return warnings;
37
38
  }
38
39
 
40
+ /**
41
+ * Validates the SHAPE of a merged scoring config, after preset+user overrides.
42
+ * Returns warning strings for rules whose required numeric field is missing.
43
+ *
44
+ * Why this exists: scoring overrides replace each rule object wholesale
45
+ * (Object.assign at the rule level), and validateConfig() only inspects
46
+ * top-level keys. So a nested typo like `{hard:{paste:{countThreshhold:2}}}`
47
+ * drops the real `countThreshold`, and `count >= undefined` is always false —
48
+ * silently disabling a hard screenout rule with no warning. This catches that
49
+ * class of misconfiguration: every hard rule needs a numeric countThreshold,
50
+ * every soft rule a numeric weight. Valid overrides produce no warnings.
51
+ */
52
+ export function validateScoringShape(scoring) {
53
+ const warnings = [];
54
+ if (!scoring || typeof scoring !== 'object') return warnings;
55
+ // Number.isFinite (not typeof === 'number') so NaN and ±Infinity are also
56
+ // flagged: typeof NaN === 'number', yet `count >= NaN` is always false (hard
57
+ // rule silently off) and a NaN/Infinity weight breaks the soft score.
58
+ // safeStr() is used in the message so a non-stringifiable value (e.g. a
59
+ // Symbol, which template interpolation would throw on) still warns rather than
60
+ // crashing the very validation meant to surface the misconfiguration.
61
+ const hard = scoring.hard || {};
62
+ Object.keys(hard).forEach(key => {
63
+ const rule = hard[key];
64
+ if (!rule || !Number.isFinite(rule.countThreshold)) {
65
+ warnings.push(
66
+ `scoring.hard.${key} has no finite numeric countThreshold (got ` +
67
+ `${rule ? safeStr(rule.countThreshold) : 'no rule'}) — this hard ` +
68
+ `screenout rule can never trigger. A likely cause is a typo in the ` +
69
+ `override key (e.g. "countThreshhold").`
70
+ );
71
+ }
72
+ });
73
+ const soft = scoring.soft || {};
74
+ Object.keys(soft).forEach(key => {
75
+ const rule = soft[key];
76
+ if (!rule || !Number.isFinite(rule.weight)) {
77
+ warnings.push(
78
+ `scoring.soft.${key} has no finite numeric weight (got ` +
79
+ `${rule ? safeStr(rule.weight) : 'no rule'}) — this soft signal ` +
80
+ `contributes nothing (or breaks the soft score). A likely cause is a ` +
81
+ `typo in the override key (e.g. "wieght").`
82
+ );
83
+ }
84
+ });
85
+ return warnings;
86
+ }
87
+
88
+ // Coerce a value to a string for a warning message without ever throwing.
89
+ // String(Symbol()) throws; falling back to the typeof keeps the warning alive.
90
+ function safeStr(v) {
91
+ try { return String(v); } catch (e) { return typeof v; }
92
+ }
93
+
39
94
  /**
40
95
  * Finds the closest matching key using Levenshtein distance.
41
96
  * Returns null if no key is within edit distance 3 (too different to suggest).