log-llm-config 1.3.20 → 1.3.21

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.
@@ -110,6 +110,12 @@ export function readVscdbItemTableJson(dbPath, itemKey) {
110
110
  if (typeof parsed === 'number' && !Number.isNaN(parsed)) {
111
111
  return { [itemKey]: { [field]: parsed !== 0 } };
112
112
  }
113
+ if (typeof parsed === 'string') {
114
+ const lower = parsed.trim().toLowerCase();
115
+ return {
116
+ [itemKey]: { [field]: lower === 'true' || lower === '1' || lower === 'yes' },
117
+ };
118
+ }
113
119
  }
114
120
  return { [itemKey]: parsed };
115
121
  }
@@ -14,6 +14,7 @@ import { persistVscdbComposerContractFromPatternsResponse } from '../readers/vsc
14
14
  import { collectConfigFilesFromPatterns, collectMcpToolFiles, collectConfigFilesFromInstalledPlugins, determineFileTypeFromPath } from '../collection/config_collector.js';
15
15
  import { normalizePathSkipPrefixes } from '../paths/pattern_resolver.js';
16
16
  import { sendConfigFile, sendConfigFilesBatch, sendHookRequestCreate, sendHookRequestUpdateManifest, sendIngestSessionStart, sendIngestSessionFinish, BATCH_CHUNK_SIZE } from '../sender/batch_sender.js';
17
+ import { canonicalCursorUserStateVscdbPath } from './remediation_config_path.js';
17
18
  import { createSignature, canonicalizePayload } from '../sender/signing.js';
18
19
  const PROJECT_ROOT = process.cwd();
19
20
  async function collectAllConfigFiles(endpointBase) {
@@ -73,7 +74,7 @@ async function sendAllConfigFiles(configFiles, hardwareUuid, authKey) {
73
74
  const hookTypeRaw = (process.env.OPTIMUS_HOOK_TYPE || 'claude').toLowerCase();
74
75
  const hookType = hookTypeRaw === 'cursor' ? 'cursor' : 'claude';
75
76
  const workspaceRepo = process.env.OPTIMUS_WORKSPACE_REPO || process.env.GITHUB_REPOSITORY || '';
76
- const manifest = configFiles.map((c) => c.file_path);
77
+ const manifest = configFiles.map((c) => canonicalCursorUserStateVscdbPath(c.file_path));
77
78
  const hookRequestId = await sendHookRequestCreate(hardwareUuid, authKey, hookType, workspaceRepo);
78
79
  hookRunLog(`hook-request id=${hookRequestId ?? 'none'}`);
79
80
  const ingestSessionId = await sendIngestSessionStart(hardwareUuid, authKey);
@@ -9,6 +9,46 @@ export const PORTABLE_CURSOR_USER_STATE_VSCDB = 'Cursor/User/globalStorage/state
9
9
  function normalizeSlashes(p) {
10
10
  return p.trim().replace(/\\/g, '/');
11
11
  }
12
+ /**
13
+ * Canonical `file_path` for Cursor User globalStorage state.vscdb uploads, matching
14
+ * `optimus_security.endpoint.log_config_file.handler._canonical_cursor_user_state_vscdb_path`.
15
+ * Always use before sending log-config-file(s) so findings dedupe regardless of absolute vs portable input.
16
+ */
17
+ export function canonicalCursorUserStateVscdbPath(filePath) {
18
+ const s = normalizeSlashes(filePath);
19
+ const lower = s.toLowerCase();
20
+ const needle = 'cursor/user/globalstorage/state.vscdb';
21
+ const idx = lower.indexOf(needle);
22
+ if (idx >= 0) {
23
+ const tail = s.slice(idx + needle.length);
24
+ if (tail.startsWith('#')) {
25
+ return `${PORTABLE_CURSOR_USER_STATE_VSCDB}${tail}`;
26
+ }
27
+ if (tail === '') {
28
+ return PORTABLE_CURSOR_USER_STATE_VSCDB;
29
+ }
30
+ return filePath.trim();
31
+ }
32
+ const vscdb = 'state.vscdb';
33
+ const vlen = vscdb.length;
34
+ const slashKey = '/' + vscdb;
35
+ let start;
36
+ const pos = lower.lastIndexOf(slashKey);
37
+ if (pos >= 0) {
38
+ start = pos + 1;
39
+ }
40
+ else if (lower.startsWith(vscdb)) {
41
+ start = 0;
42
+ }
43
+ else {
44
+ return filePath.trim();
45
+ }
46
+ const tail = s.slice(start + vlen);
47
+ if (tail.startsWith('#') || tail === '') {
48
+ return `${PORTABLE_CURSOR_USER_STATE_VSCDB}${tail}`;
49
+ }
50
+ return filePath.trim();
51
+ }
12
52
  function cursorStateVscdbAbsoluteBasePaths() {
13
53
  const h = homedir();
14
54
  if (process.platform === 'darwin') {
@@ -2,6 +2,7 @@ import { postStartupPayload, patchPayload } from '../../endpoint_client/index.js
2
2
  import { createSignature } from './signing.js';
3
3
  import { loadEndpointBase } from './endpoint_config.js';
4
4
  import { hookRunLog } from '../runtime/hook_logger.js';
5
+ import { canonicalCursorUserStateVscdbPath } from '../runtime/remediation_config_path.js';
5
6
  /** Chunk size per batch request. */
6
7
  export const BATCH_CHUNK_SIZE = 20;
7
8
  const MAX_BATCH_SIZE_BYTES = 500 * 1024; // 500KB
@@ -51,7 +52,11 @@ function buildBatchChunks(configFiles, basePayloadSize) {
51
52
  return chunks;
52
53
  }
53
54
  function buildChunkBody(chunk, hardwareUuid, authKey, hookRequestId, metadata) {
54
- const config_files = chunk.map((c) => ({ file_type: c.file_type, file_path: c.file_path, raw_content: c.raw_content }));
55
+ const config_files = chunk.map((c) => ({
56
+ file_type: c.file_type,
57
+ file_path: canonicalCursorUserStateVscdbPath(c.file_path),
58
+ raw_content: c.raw_content,
59
+ }));
55
60
  const payload = { hardware_uuid: hardwareUuid, metadata, config_files };
56
61
  if (hookRequestId != null)
57
62
  payload.hook_request_id = hookRequestId;
@@ -153,18 +158,19 @@ async function sendIngestSessionFinish(hardwareUuid, authKey, ingestSessionId) {
153
158
  async function sendConfigFile(configFile, hardwareUuid, authKey) {
154
159
  const endpoint = loadEndpointBase();
155
160
  const apiUrl = `${resolveApiBase(endpoint)}/endpoint_security/log-config-file/`;
156
- const payload = { hardware_uuid: hardwareUuid, file_type: configFile.file_type, file_path: configFile.file_path, raw_content: configFile.raw_content };
161
+ const uploadPath = canonicalCursorUserStateVscdbPath(configFile.file_path);
162
+ const payload = { hardware_uuid: hardwareUuid, file_type: configFile.file_type, file_path: uploadPath, raw_content: configFile.raw_content };
157
163
  const signature = createSignature(payload, authKey.key);
158
164
  const body = { ...payload, signature, key_id: authKey.key_id || '', metadata: { org_identifier: process.env.GITHUB_ORG || process.env.GH_ORG || '', repo_identifier: process.env.GITHUB_REPOSITORY || process.env.GH_REPOSITORY || '' } };
159
165
  try {
160
166
  const response = await postStartupPayload(apiUrl, body);
161
167
  if (response.status !== 'accepted') {
162
- hookRunLog(`sendConfigFile: rejected status=${response.status} error=${response.error ?? ''} path=${configFile.file_path}`);
168
+ hookRunLog(`sendConfigFile: rejected status=${response.status} error=${response.error ?? ''} path=${uploadPath}`);
163
169
  }
164
170
  return response.status === 'accepted';
165
171
  }
166
172
  catch (err) {
167
- hookRunLog(`sendConfigFile: request failed path=${configFile.file_path} err=${err instanceof Error ? err.message : String(err)}`);
173
+ hookRunLog(`sendConfigFile: request failed path=${uploadPath} err=${err instanceof Error ? err.message : String(err)}`);
168
174
  return false;
169
175
  }
170
176
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "log-llm-config",
3
- "version": "1.3.20",
3
+ "version": "1.3.21",
4
4
  "description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
5
5
  "type": "module",
6
6
  "bin": {