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) => ({
|
|
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
|
|
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=${
|
|
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=${
|
|
173
|
+
hookRunLog(`sendConfigFile: request failed path=${uploadPath} err=${err instanceof Error ? err.message : String(err)}`);
|
|
168
174
|
return false;
|
|
169
175
|
}
|
|
170
176
|
}
|