log-llm-config 1.3.2 → 1.3.6
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/compliance_prompt_gate.js +5 -20
- package/dist/execute_trusted_restarts.js +56 -0
- package/dist/log_config_files/readers/vscdb_reader.js +5 -0
- package/dist/log_config_files/runtime/compliance_check.js +23 -10
- package/dist/log_config_files/runtime/trusted_restarts.js +22 -0
- package/package.json +3 -2
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Synchronous pre-prompt gate: local compliance only (stdout = single JSON line for IDE hooks).
|
|
3
3
|
* stderr must stay clean; logs go via hookRunLog (file).
|
|
4
|
+
*
|
|
5
|
+
* When autofix returns restart_commands, this process does not spawn them — the shell hook pipes
|
|
6
|
+
* the same JSON line to execute_trusted_restarts (TS allowlist + spawn).
|
|
4
7
|
*/
|
|
5
8
|
import { applyAutofixViolations, pruneSatisfiedOneTimeRemediations, runLocalRemediationComplianceCheck, } from './log_config_files/runtime/compliance_check.js';
|
|
6
9
|
import { existsSync, statSync } from 'node:fs';
|
|
7
|
-
import { spawn } from 'node:child_process';
|
|
8
10
|
import { pathToFileURL } from 'node:url';
|
|
9
11
|
import { resolve } from 'node:path';
|
|
10
12
|
import { getRemediationInstructionsPath } from './log_config_files/runtime/management_storage.js';
|
|
11
|
-
import { hookLogSessionBanner
|
|
13
|
+
import { hookLogSessionBanner } from './log_config_files/runtime/hook_logger.js';
|
|
12
14
|
const MANIFEST_STALE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
13
|
-
/** Set by optimus-compliance-check.sh only for Claude Desktop (dumb TERM). Hook runs allowlisted restart after osascript. */
|
|
14
|
-
function claudeDesktopHookRunsRestart() {
|
|
15
|
-
const v = process.env.OPTIMUS_CLAUDE_DESKTOP_DEFER_GATE_RESTART?.trim().toLowerCase();
|
|
16
|
-
return v === '1' || v === 'true' || v === 'yes';
|
|
17
|
-
}
|
|
18
15
|
function parseIde() {
|
|
19
16
|
const eq = process.argv.find((a) => a.startsWith('--ide='));
|
|
20
17
|
if (eq) {
|
|
@@ -51,13 +48,6 @@ function blockPayload(ide, violationMessage) {
|
|
|
51
48
|
}
|
|
52
49
|
return JSON.stringify({ continue: false, user_message: text });
|
|
53
50
|
}
|
|
54
|
-
function fireRestartCommands(commands) {
|
|
55
|
-
for (const cmd of commands) {
|
|
56
|
-
hookRunLog(`restart: firing command="${cmd}"`);
|
|
57
|
-
const child = spawn('sh', ['-c', cmd], { detached: true, stdio: 'ignore' });
|
|
58
|
-
child.unref();
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
51
|
function getManifestStalenessMs() {
|
|
62
52
|
try {
|
|
63
53
|
const path = getRemediationInstructionsPath();
|
|
@@ -127,12 +117,7 @@ export async function runCompliancePromptGate() {
|
|
|
127
117
|
if (restartCommands.length > 0)
|
|
128
118
|
payload.restart_commands = restartCommands;
|
|
129
119
|
console.log(JSON.stringify(payload));
|
|
130
|
-
//
|
|
131
|
-
// Claude Desktop: same — optimus-compliance-check.sh shows the dialog then evals allowlisted restarts.
|
|
132
|
-
// Claude Code (CLI): no shell restart path; gate must spawn here.
|
|
133
|
-
const fireInGate = ide !== 'cursor' && !(ide === 'claude' && claudeDesktopHookRunsRestart());
|
|
134
|
-
if (fireInGate)
|
|
135
|
-
fireRestartCommands(restartCommands);
|
|
120
|
+
// Restarts: always executed by optimus-compliance-check.sh via execute_trusted_restarts (TS allowlist + spawn).
|
|
136
121
|
return;
|
|
137
122
|
}
|
|
138
123
|
const msg = recheck.violations[0]?.message ?? 'A security policy violation has been detected.';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI: read one JSON line from stdin (same shape as compliance_prompt_gate stdout for __optimus_autofix),
|
|
3
|
+
* parse restart_commands[], allowlist each in TS, spawn detached. Invoked by optimus-compliance-check.sh only.
|
|
4
|
+
* stderr stays quiet; failures logged via hookRunLog.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
import { pathToFileURL } from 'node:url';
|
|
8
|
+
import { resolve } from 'node:path';
|
|
9
|
+
import { hookRunLog } from './log_config_files/runtime/hook_logger.js';
|
|
10
|
+
import { executeTrustedRestartCommands } from './log_config_files/runtime/trusted_restarts.js';
|
|
11
|
+
function isRunAsCliModule() {
|
|
12
|
+
const entry = process.argv[1];
|
|
13
|
+
if (!entry)
|
|
14
|
+
return false;
|
|
15
|
+
try {
|
|
16
|
+
return import.meta.url === pathToFileURL(resolve(entry)).href;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function main() {
|
|
23
|
+
let raw;
|
|
24
|
+
try {
|
|
25
|
+
raw = readFileSync(0, 'utf8');
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
hookRunLog('execute_trusted_restarts: could not read stdin');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const first = raw.trim().split(/\r?\n/)[0]?.trim() ?? '';
|
|
32
|
+
if (!first)
|
|
33
|
+
return;
|
|
34
|
+
let j;
|
|
35
|
+
try {
|
|
36
|
+
j = JSON.parse(first);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
hookRunLog('execute_trusted_restarts: stdin is not valid JSON');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const cmds = j.restart_commands;
|
|
43
|
+
if (!Array.isArray(cmds))
|
|
44
|
+
return;
|
|
45
|
+
const strings = cmds.filter((c) => typeof c === 'string');
|
|
46
|
+
executeTrustedRestartCommands(strings);
|
|
47
|
+
}
|
|
48
|
+
if (isRunAsCliModule()) {
|
|
49
|
+
try {
|
|
50
|
+
main();
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
hookRunLog(`execute_trusted_restarts: ${e instanceof Error ? e.message : String(e)}`);
|
|
54
|
+
}
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
@@ -92,6 +92,11 @@ export function readVscdbItemTableJson(dbPath, itemKey) {
|
|
|
92
92
|
}
|
|
93
93
|
// Bare JSON primitives (e.g. cursor/thirdPartyExtensibilityEnabled stores `true`/`false`).
|
|
94
94
|
if (typeof parsed === 'boolean' || typeof parsed === 'number' || typeof parsed === 'string') {
|
|
95
|
+
const leaf = itemKey.split('/').pop() ?? '';
|
|
96
|
+
// Legacy cursorai/donotchange/privacyMode row is bare true/false; compliance paths use …/privacyMode.privacyMode.
|
|
97
|
+
if (typeof parsed === 'boolean' && leaf === 'privacyMode') {
|
|
98
|
+
return { [itemKey]: { privacyMode: parsed } };
|
|
99
|
+
}
|
|
95
100
|
return { [itemKey]: parsed };
|
|
96
101
|
}
|
|
97
102
|
return { [itemKey]: {} };
|
|
@@ -20,6 +20,14 @@ import { readStoredAuthKey } from '../auth/auth_key_store.js';
|
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
// Helpers
|
|
22
22
|
// ---------------------------------------------------------------------------
|
|
23
|
+
/**
|
|
24
|
+
* ItemTable keys use slashes (e.g. cursorai/donotchange/privacyMode) and do not contain dots.
|
|
25
|
+
* Compliance setting_path is `${itemKey}.${nested.path}` — take the segment before the first `.`.
|
|
26
|
+
*/
|
|
27
|
+
export function itemTableKeyFromSettingPath(settingPath) {
|
|
28
|
+
const i = settingPath.indexOf('.');
|
|
29
|
+
return i === -1 ? settingPath : settingPath.slice(0, i);
|
|
30
|
+
}
|
|
23
31
|
/** Traverse a JSON object using dot-notation path. Returns undefined if any segment is missing. */
|
|
24
32
|
export function getByPath(obj, path) {
|
|
25
33
|
const parts = path.split('.');
|
|
@@ -85,20 +93,25 @@ function verifyOpsApplied(configJson, settingPath, ops) {
|
|
|
85
93
|
}
|
|
86
94
|
return { ok: true, expected: null };
|
|
87
95
|
}
|
|
88
|
-
/** Plain JSON file or virtual `…/state.vscdb#
|
|
89
|
-
function loadRemediationConfigJson(configFilePath) {
|
|
96
|
+
/** Plain JSON file or virtual `…/state.vscdb#itemKey` path for ItemTable-backed settings. */
|
|
97
|
+
function loadRemediationConfigJson(configFilePath, checkSettingPaths = []) {
|
|
90
98
|
const hashIdx = configFilePath.indexOf('#');
|
|
91
99
|
if (hashIdx >= 0) {
|
|
92
100
|
const dbPath = configFilePath.slice(0, hashIdx);
|
|
93
|
-
const
|
|
94
|
-
if (!
|
|
101
|
+
const itemKeyFromPath = configFilePath.slice(hashIdx + 1).trim();
|
|
102
|
+
if (!itemKeyFromPath)
|
|
95
103
|
return { ok: false, reason: 'empty_vscdb_key' };
|
|
96
104
|
if (!existsSync(dbPath))
|
|
97
105
|
return { ok: false, reason: 'db_not_found' };
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
106
|
+
const keys = new Set([itemKeyFromPath, ...checkSettingPaths.map(itemTableKeyFromSettingPath)].filter(Boolean));
|
|
107
|
+
const merged = {};
|
|
108
|
+
for (const k of keys) {
|
|
109
|
+
const wrapped = readVscdbItemTableJson(dbPath, k);
|
|
110
|
+
if (wrapped === null)
|
|
111
|
+
return { ok: false, reason: 'vscdb_read_failed' };
|
|
112
|
+
Object.assign(merged, wrapped);
|
|
113
|
+
}
|
|
114
|
+
return { ok: true, json: merged };
|
|
102
115
|
}
|
|
103
116
|
if (!existsSync(configFilePath))
|
|
104
117
|
return { ok: false, reason: 'file_not_found' };
|
|
@@ -137,7 +150,7 @@ export function runLocalRemediationComplianceCheck() {
|
|
|
137
150
|
const checks = compliance.checks ?? [];
|
|
138
151
|
if (checks.length === 0)
|
|
139
152
|
continue;
|
|
140
|
-
const loaded = loadRemediationConfigJson(entry.config_file_path);
|
|
153
|
+
const loaded = loadRemediationConfigJson(entry.config_file_path, checks.map((c) => c.setting_path));
|
|
141
154
|
if (!loaded.ok) {
|
|
142
155
|
const msg = loaded.reason === 'file_not_found'
|
|
143
156
|
? `compliance_check: config file not found, skipping uuid=${entry.uuid}`
|
|
@@ -374,7 +387,7 @@ export function pruneSatisfiedOneTimeRemediations() {
|
|
|
374
387
|
remaining.push(raw);
|
|
375
388
|
continue;
|
|
376
389
|
}
|
|
377
|
-
const prLoaded = loadRemediationConfigJson(inst.config_file_path);
|
|
390
|
+
const prLoaded = loadRemediationConfigJson(inst.config_file_path, checks.map((c) => c.setting_path));
|
|
378
391
|
if (!prLoaded.ok) {
|
|
379
392
|
remaining.push(raw);
|
|
380
393
|
continue;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execute autofix restart_command strings only if allowlisted (same rules as gate enqueue).
|
|
3
|
+
* Used by the compliance shell hook via execute_trusted_restarts CLI — single place for allowlist + spawn.
|
|
4
|
+
*/
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
6
|
+
import { hookRunLog } from './hook_logger.js';
|
|
7
|
+
import { isTrustedRestartCommandForAutofix } from './remediation_sync.js';
|
|
8
|
+
/** Spawn each trusted command detached (same pattern as former compliance_prompt_gate fireRestartCommands). */
|
|
9
|
+
export function executeTrustedRestartCommands(commands) {
|
|
10
|
+
for (const cmd of commands) {
|
|
11
|
+
const t = cmd.trim();
|
|
12
|
+
if (!t)
|
|
13
|
+
continue;
|
|
14
|
+
if (!isTrustedRestartCommandForAutofix(cmd)) {
|
|
15
|
+
hookRunLog(`execute_trusted_restarts: rejected (not allowlisted) prefix=${t.slice(0, 120)}`);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
hookRunLog(`execute_trusted_restarts: firing command="${t}"`);
|
|
19
|
+
const child = spawn('sh', ['-c', t], { detached: true, stdio: 'ignore' });
|
|
20
|
+
child.unref();
|
|
21
|
+
}
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "log-llm-config",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.6",
|
|
4
4
|
"description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"log_uuid": "dist/log_uuid/index.js",
|
|
9
9
|
"log_config_files": "dist/log_config_files/index.js",
|
|
10
10
|
"log_sensitive_paths_audit": "dist/log_sensitive_paths_audit.js",
|
|
11
|
-
"apply-deferred-vscdb": "dist/apply_deferred_vscdb.js"
|
|
11
|
+
"apply-deferred-vscdb": "dist/apply_deferred_vscdb.js",
|
|
12
|
+
"execute-trusted-restarts": "dist/execute_trusted_restarts.js"
|
|
12
13
|
},
|
|
13
14
|
"scripts": {
|
|
14
15
|
"build": "tsc -p tsconfig.json",
|