@optimuslabs/harness-map-staging 1.5.16

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 (88) hide show
  1. package/README.md +46 -0
  2. package/dist/apply_deferred_vscdb.js +38 -0
  3. package/dist/bootstrap_constants.js +15 -0
  4. package/dist/cli/bash_script_generator.js +95 -0
  5. package/dist/cli.js +144 -0
  6. package/dist/cli_invocation_match.js +30 -0
  7. package/dist/compliance_check_runner.js +57 -0
  8. package/dist/compliance_prompt_gate.js +396 -0
  9. package/dist/dialog_prefs_cli.js +109 -0
  10. package/dist/endpoint_client/compress_payload.js +25 -0
  11. package/dist/endpoint_client/http_transport.js +90 -0
  12. package/dist/endpoint_client/index.js +3 -0
  13. package/dist/endpoint_client/registry_api.js +41 -0
  14. package/dist/endpoint_client/startup_api.js +44 -0
  15. package/dist/endpoint_client/types.js +4 -0
  16. package/dist/execute_trusted_restarts.js +60 -0
  17. package/dist/log_config_files/auth/auth_flow.js +22 -0
  18. package/dist/log_config_files/auth/auth_key_store.js +14 -0
  19. package/dist/log_config_files/collection/claude_desktop_extensions_collector.js +248 -0
  20. package/dist/log_config_files/collection/claude_known_projects_collector.js +42 -0
  21. package/dist/log_config_files/collection/claude_token_usage_collector.js +203 -0
  22. package/dist/log_config_files/collection/codex_token_usage_collector.js +342 -0
  23. package/dist/log_config_files/collection/config_collector.js +245 -0
  24. package/dist/log_config_files/collection/copilot_token_usage_collector.js +167 -0
  25. package/dist/log_config_files/collection/cowork_desktop_version_collector.js +60 -0
  26. package/dist/log_config_files/collection/cowork_session_whitelist.js +89 -0
  27. package/dist/log_config_files/collection/cursor_project_mcp_collector.js +230 -0
  28. package/dist/log_config_files/collection/cursor_project_workspace_activity.js +73 -0
  29. package/dist/log_config_files/collection/cursor_token_usage_collector.js +192 -0
  30. package/dist/log_config_files/collection/directory_collector.js +126 -0
  31. package/dist/log_config_files/collection/enrichment_helpers.js +53 -0
  32. package/dist/log_config_files/collection/ensure_cursor_user_settings_snapshot.js +50 -0
  33. package/dist/log_config_files/collection/file_type_rules.js +47 -0
  34. package/dist/log_config_files/collection/grok_cli_version_collector.js +53 -0
  35. package/dist/log_config_files/collection/hermes_token_usage_collector.js +241 -0
  36. package/dist/log_config_files/collection/mcp_tool_collector.js +106 -0
  37. package/dist/log_config_files/collection/metadata_merge.js +43 -0
  38. package/dist/log_config_files/collection/openclaw_helpers.js +55 -0
  39. package/dist/log_config_files/collection/openclaw_token_usage_collector.js +223 -0
  40. package/dist/log_config_files/collection/opencode_token_usage_collector.js +177 -0
  41. package/dist/log_config_files/collection/pi_token_usage_collector.js +239 -0
  42. package/dist/log_config_files/collection/plugin_collector.js +223 -0
  43. package/dist/log_config_files/collection/plugin_version_helpers.js +37 -0
  44. package/dist/log_config_files/collection/skills_cli_collector.js +162 -0
  45. package/dist/log_config_files/index.js +19 -0
  46. package/dist/log_config_files/paths/path_constants_helpers.js +71 -0
  47. package/dist/log_config_files/paths/pattern_resolver.js +326 -0
  48. package/dist/log_config_files/readers/composer_shadow_merge.js +67 -0
  49. package/dist/log_config_files/readers/cursor_shadow_merge_ruleset.js +46 -0
  50. package/dist/log_config_files/readers/file_readers.js +196 -0
  51. package/dist/log_config_files/readers/vscdb_config_builder.js +148 -0
  52. package/dist/log_config_files/readers/vscdb_reactive_storage.js +62 -0
  53. package/dist/log_config_files/readers/vscdb_reader.js +337 -0
  54. package/dist/log_config_files/runtime/client_event_reporter.js +347 -0
  55. package/dist/log_config_files/runtime/compliance_check.js +1013 -0
  56. package/dist/log_config_files/runtime/compliance_session_log.js +378 -0
  57. package/dist/log_config_files/runtime/dialog_preferences.js +31 -0
  58. package/dist/log_config_files/runtime/hardware_uuid.js +36 -0
  59. package/dist/log_config_files/runtime/hook_logger.js +267 -0
  60. package/dist/log_config_files/runtime/hook_type_for_request.js +30 -0
  61. package/dist/log_config_files/runtime/log_metadata.js +122 -0
  62. package/dist/log_config_files/runtime/main_runner.js +376 -0
  63. package/dist/log_config_files/runtime/management_storage.js +144 -0
  64. package/dist/log_config_files/runtime/ops_target_path.js +18 -0
  65. package/dist/log_config_files/runtime/remediation_apply_tracking.js +152 -0
  66. package/dist/log_config_files/runtime/remediation_config_path.js +140 -0
  67. package/dist/log_config_files/runtime/remediation_sync.js +1705 -0
  68. package/dist/log_config_files/runtime/secret_regex_scan.js +126 -0
  69. package/dist/log_config_files/runtime/sqlite_binary.js +124 -0
  70. package/dist/log_config_files/runtime/trusted_restarts.js +113 -0
  71. package/dist/log_config_files/runtime/workspace_repo.js +170 -0
  72. package/dist/log_config_files/runtime/worktree_absent.js +50 -0
  73. package/dist/log_config_files/runtime/worktree_scanner.js +137 -0
  74. package/dist/log_config_files/sender/batch_sender.js +283 -0
  75. package/dist/log_config_files/sender/endpoint_config.js +24 -0
  76. package/dist/log_config_files/sender/signing.js +1 -0
  77. package/dist/log_sensitive_paths_audit.js +89 -0
  78. package/dist/log_uuid/auth_key_store.js +37 -0
  79. package/dist/log_uuid/hardware_uuid.js +35 -0
  80. package/dist/log_uuid/index.js +11 -0
  81. package/dist/log_uuid/log_uuid_helper.js +30 -0
  82. package/dist/log_uuid/startup_sender.js +131 -0
  83. package/dist/log_uuid/user_profile.js +192 -0
  84. package/dist/remediation_change_preview.js +182 -0
  85. package/dist/tofu.js +22 -0
  86. package/dist/tofu_environment.js +36 -0
  87. package/dist/types/config_file_types.js +1 -0
  88. package/package.json +67 -0
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Tracks remediation apply attempts and post-apply verification (next compliance check)
3
+ * so we do not restart-loop or retry forever when an agent config format changed and
4
+ * autofix can never stick. Applies to every remediation on Cursor and Claude.
5
+ */
6
+ import { existsSync, readFileSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { atomicWriteJson, getDeferredVscdbApplyPath, getRemediationStateDir, } from './management_storage.js';
9
+ import { hookRunLog } from './hook_logger.js';
10
+ export const REMEDIATION_APPLY_TRACKING_BASENAME = 'remediation_apply_tracking.json';
11
+ /** Consecutive post-restart verification failures before we stop autofix for a UUID. */
12
+ export const MAX_CONSECUTIVE_VERIFY_FAILURES = 1;
13
+ export function getRemediationApplyTrackingPath() {
14
+ return join(getRemediationStateDir(), REMEDIATION_APPLY_TRACKING_BASENAME);
15
+ }
16
+ function emptyTrackingFile() {
17
+ return { entries: {} };
18
+ }
19
+ export function readRemediationApplyTrackingFile() {
20
+ const path = getRemediationApplyTrackingPath();
21
+ if (!existsSync(path))
22
+ return emptyTrackingFile();
23
+ try {
24
+ const parsed = JSON.parse(readFileSync(path, 'utf8'));
25
+ if (!parsed || typeof parsed !== 'object' || !parsed.entries || typeof parsed.entries !== 'object') {
26
+ return emptyTrackingFile();
27
+ }
28
+ return parsed;
29
+ }
30
+ catch {
31
+ return emptyTrackingFile();
32
+ }
33
+ }
34
+ export function writeRemediationApplyTrackingFile(file) {
35
+ atomicWriteJson(getRemediationApplyTrackingPath(), file);
36
+ }
37
+ function defaultEntry() {
38
+ return { consecutive_verify_failures: 0, quarantined: false };
39
+ }
40
+ export function getRemediationApplyTrackingEntry(uuid) {
41
+ const file = readRemediationApplyTrackingFile();
42
+ return file.entries[uuid] ?? defaultEntry();
43
+ }
44
+ export function isRemediationQuarantined(uuid) {
45
+ return getRemediationApplyTrackingEntry(uuid).quarantined === true;
46
+ }
47
+ /** Admin retry: reset quarantine counters so autofix can run again on next prompt. */
48
+ export function clearRemediationApplyQuarantine(uuid) {
49
+ const file = readRemediationApplyTrackingFile();
50
+ const prev = file.entries[uuid];
51
+ if (!prev)
52
+ return;
53
+ file.entries[uuid] = {
54
+ consecutive_verify_failures: 0,
55
+ quarantined: false,
56
+ pending_post_restart_verify: false,
57
+ };
58
+ writeRemediationApplyTrackingFile(file);
59
+ hookRunLog(`remediation_tracking: quarantine_cleared uuid=${uuid}`);
60
+ }
61
+ export function hasPendingPostRestartVerification() {
62
+ const file = readRemediationApplyTrackingFile();
63
+ return Object.values(file.entries).some((e) => e.pending_post_restart_verify === true);
64
+ }
65
+ export function markRemediationApplyPendingVerification(uuid) {
66
+ const file = readRemediationApplyTrackingFile();
67
+ const prev = file.entries[uuid] ?? defaultEntry();
68
+ file.entries[uuid] = {
69
+ ...prev,
70
+ pending_post_restart_verify: true,
71
+ last_apply_at: new Date().toISOString(),
72
+ server_verified_at: undefined,
73
+ };
74
+ writeRemediationApplyTrackingFile(file);
75
+ hookRunLog(`remediation_tracking: pending_post_restart_verify uuid=${uuid}`);
76
+ }
77
+ export function markRemediationApplyVerified(uuid) {
78
+ const file = readRemediationApplyTrackingFile();
79
+ const prev = file.entries[uuid] ?? defaultEntry();
80
+ const next = {
81
+ ...prev,
82
+ pending_post_restart_verify: false,
83
+ consecutive_verify_failures: 0,
84
+ quarantined: false,
85
+ last_failure_reason: undefined,
86
+ quarantined_at: undefined,
87
+ server_verified_at: new Date().toISOString(),
88
+ };
89
+ file.entries[uuid] = next;
90
+ writeRemediationApplyTrackingFile(file);
91
+ hookRunLog(`remediation_tracking: verified uuid=${uuid}`);
92
+ return next;
93
+ }
94
+ export function recordRemediationVerificationFailure(uuid, reason) {
95
+ const file = readRemediationApplyTrackingFile();
96
+ const prev = file.entries[uuid] ?? defaultEntry();
97
+ const failures = (prev.consecutive_verify_failures ?? 0) + 1;
98
+ const quarantined = failures >= MAX_CONSECUTIVE_VERIFY_FAILURES;
99
+ const next = {
100
+ ...prev,
101
+ pending_post_restart_verify: false,
102
+ consecutive_verify_failures: failures,
103
+ quarantined,
104
+ last_failure_reason: reason,
105
+ ...(quarantined ? { quarantined_at: new Date().toISOString() } : {}),
106
+ };
107
+ file.entries[uuid] = next;
108
+ writeRemediationApplyTrackingFile(file);
109
+ hookRunLog(`remediation_tracking: verification_failed uuid=${uuid} failures=${failures} quarantined=${quarantined}`);
110
+ return { entry: next, newlyQuarantined: quarantined && !prev.quarantined };
111
+ }
112
+ /**
113
+ * For each UUID awaiting post-restart verification, decide pass/fail.
114
+ *
115
+ * Prefer a per-UUID probe (re-runs that remediation's checks on disk). A Set of UUIDs is
116
+ * supported for unit tests only — do not use a global violation list from compliance_check
117
+ * in production: shadow-key merge bugs once made that list empty while the setting was still
118
+ * wrong, which incorrectly marked remediations verified.
119
+ *
120
+ * If the deferred vscdb queue file is still on disk, Cursor restart / apply_deferred_vscdb has
121
+ * not completed — leave pending and do not quarantine (setting unchanged is expected).
122
+ */
123
+ export function processPendingPostRestartVerifications(violationProbe) {
124
+ const isStillViolating = typeof violationProbe === 'function'
125
+ ? violationProbe
126
+ : (uuid) => violationProbe.has(uuid);
127
+ const file = readRemediationApplyTrackingFile();
128
+ const outcomes = [];
129
+ const deferredQueuePending = existsSync(getDeferredVscdbApplyPath());
130
+ for (const [uuid, entry] of Object.entries(file.entries)) {
131
+ if (!entry.pending_post_restart_verify)
132
+ continue;
133
+ if (!isStillViolating(uuid)) {
134
+ markRemediationApplyVerified(uuid);
135
+ outcomes.push({ uuid, status: 'verified', consecutive_failures: 0 });
136
+ continue;
137
+ }
138
+ if (deferredQueuePending) {
139
+ hookRunLog(`remediation_tracking: defer verify uuid=${uuid} — deferred vscdb queue still pending (restart/apply not done yet)`);
140
+ continue;
141
+ }
142
+ const reason = 'Setting unchanged after apply — the agent may have changed its config format and auto-fix cannot apply this policy.';
143
+ const { entry: updated, newlyQuarantined } = recordRemediationVerificationFailure(uuid, reason);
144
+ outcomes.push({
145
+ uuid,
146
+ status: newlyQuarantined || updated.quarantined ? 'quarantined' : 'verification_failed',
147
+ reason,
148
+ consecutive_failures: updated.consecutive_verify_failures,
149
+ });
150
+ }
151
+ return outcomes;
152
+ }
@@ -0,0 +1,140 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ /**
5
+ * Portable key stored by the server for Cursor User globalStorage state.vscdb
6
+ * (see optimus_security/endpoint/log_config_file/handler._canonical_cursor_user_state_vscdb_path).
7
+ */
8
+ export const PORTABLE_CURSOR_USER_STATE_VSCDB = 'Cursor/User/globalStorage/state.vscdb';
9
+ /** Portable key for Cursor User settings.json (globalCursorIgnoreList). */
10
+ export const PORTABLE_CURSOR_USER_SETTINGS = 'Cursor/User/settings.json';
11
+ function expandLeadingTilde(configFilePath) {
12
+ const t = configFilePath.trim();
13
+ if (t.startsWith('~/')) {
14
+ return join(homedir(), t.slice(2));
15
+ }
16
+ return configFilePath;
17
+ }
18
+ export function cursorUserSettingsAbsolutePaths() {
19
+ const h = homedir();
20
+ if (process.platform === 'darwin') {
21
+ const support = join(h, 'Library', 'Application Support');
22
+ const variants = ['Cursor', 'Cursor - Insiders', 'Cursor Nightly', 'Cursor Next'];
23
+ return variants.map((name) => join(support, name, 'User', 'settings.json'));
24
+ }
25
+ if (process.platform === 'win32') {
26
+ const appData = process.env.APPDATA;
27
+ if (appData)
28
+ return [join(appData, 'Cursor', 'User', 'settings.json')];
29
+ return [join(h, 'AppData', 'Roaming', 'Cursor', 'User', 'settings.json')];
30
+ }
31
+ return [join(h, '.config', 'Cursor', 'User', 'settings.json')];
32
+ }
33
+ function normalizeSlashes(p) {
34
+ return p.trim().replace(/\\/g, '/');
35
+ }
36
+ /**
37
+ * Canonical `file_path` for Cursor User globalStorage state.vscdb uploads, matching
38
+ * `optimus_security.endpoint.log_config_file.handler._canonical_cursor_user_state_vscdb_path`.
39
+ * Always use before sending log-config-file(s) so findings dedupe regardless of absolute vs portable input.
40
+ */
41
+ export function canonicalCursorUserStateVscdbPath(filePath) {
42
+ const s = normalizeSlashes(filePath);
43
+ const lower = s.toLowerCase();
44
+ const needle = 'cursor/user/globalstorage/state.vscdb';
45
+ const idx = lower.indexOf(needle);
46
+ if (idx >= 0) {
47
+ const tail = s.slice(idx + needle.length);
48
+ if (tail.startsWith('#')) {
49
+ return `${PORTABLE_CURSOR_USER_STATE_VSCDB}${tail}`;
50
+ }
51
+ if (tail === '') {
52
+ return PORTABLE_CURSOR_USER_STATE_VSCDB;
53
+ }
54
+ return filePath.trim();
55
+ }
56
+ // Per-workspace vscdbs live under workspaceStorage/<hash>/state.vscdb and must NOT be
57
+ // normalized to the global path — each workspace is a distinct ConfigurationFile row.
58
+ if (lower.includes('workspacestorage')) {
59
+ return filePath.trim();
60
+ }
61
+ const vscdb = 'state.vscdb';
62
+ const vlen = vscdb.length;
63
+ const slashKey = '/' + vscdb;
64
+ let start;
65
+ const pos = lower.lastIndexOf(slashKey);
66
+ if (pos >= 0) {
67
+ start = pos + 1;
68
+ }
69
+ else if (lower.startsWith(vscdb)) {
70
+ start = 0;
71
+ }
72
+ else {
73
+ return filePath.trim();
74
+ }
75
+ const tail = s.slice(start + vlen);
76
+ if (tail.startsWith('#') || tail === '') {
77
+ return `${PORTABLE_CURSOR_USER_STATE_VSCDB}${tail}`;
78
+ }
79
+ return filePath.trim();
80
+ }
81
+ function cursorStateVscdbAbsoluteBasePaths() {
82
+ const h = homedir();
83
+ if (process.platform === 'darwin') {
84
+ const support = join(h, 'Library', 'Application Support');
85
+ const variants = ['Cursor', 'Cursor - Insiders', 'Cursor Nightly', 'Cursor Next'];
86
+ return variants.map((name) => join(support, name, 'User', 'globalStorage', 'state.vscdb'));
87
+ }
88
+ if (process.platform === 'win32') {
89
+ const appData = process.env.APPDATA;
90
+ if (appData)
91
+ return [join(appData, 'Cursor', 'User', 'globalStorage', 'state.vscdb')];
92
+ return [join(h, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'state.vscdb')];
93
+ }
94
+ return [join(h, '.config', 'Cursor', 'User', 'globalStorage', 'state.vscdb')];
95
+ }
96
+ /**
97
+ * Expand server-side portable Cursor state.vscdb paths to a local absolute path.
98
+ * No-op for absolute paths and for relative paths that are not the portable vscdb key.
99
+ */
100
+ export function resolveRemediationConfigPath(configFilePath) {
101
+ const expandedTilde = expandLeadingTilde(configFilePath);
102
+ const t = normalizeSlashes(expandedTilde);
103
+ const hash = t.indexOf('#');
104
+ const base = hash >= 0 ? t.slice(0, hash) : t;
105
+ const frag = hash >= 0 ? t.slice(hash) : '';
106
+ const baseNorm = base.replace(/\/+$/, '');
107
+ if (baseNorm.startsWith('/') || /^[a-zA-Z]:\//.test(baseNorm)) {
108
+ return expandedTilde;
109
+ }
110
+ const portableSettings = PORTABLE_CURSOR_USER_SETTINGS;
111
+ if (baseNorm === portableSettings || baseNorm.endsWith('/' + portableSettings)) {
112
+ const candidates = cursorUserSettingsAbsolutePaths();
113
+ for (const abs of candidates) {
114
+ if (existsSync(abs))
115
+ return abs;
116
+ }
117
+ return candidates[0];
118
+ }
119
+ const portable = PORTABLE_CURSOR_USER_STATE_VSCDB;
120
+ if (baseNorm !== portable && !baseNorm.endsWith('/' + portable)) {
121
+ return configFilePath;
122
+ }
123
+ const candidates = cursorStateVscdbAbsoluteBasePaths();
124
+ for (const abs of candidates) {
125
+ if (existsSync(abs))
126
+ return `${abs}${frag}`;
127
+ }
128
+ return `${candidates[0]}${frag}`;
129
+ }
130
+ /** Correct log-config file_type when manifest rows target Cursor User/settings.json. */
131
+ export function resolveRemediationUploadFileType(configFilePath, manifestFileType) {
132
+ const t = normalizeSlashes(configFilePath.trim());
133
+ const portableSettings = PORTABLE_CURSOR_USER_SETTINGS;
134
+ if (t === portableSettings ||
135
+ t.endsWith(`/${portableSettings}`) ||
136
+ t.toLowerCase().includes('cursor/user/settings.json')) {
137
+ return 'vscode_settings';
138
+ }
139
+ return (manifestFileType ?? '').trim();
140
+ }