@contextfort-ai/openclaw-secure 0.1.7 → 0.1.9

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.
@@ -81,6 +81,179 @@ if (args[0] === 'enable') {
81
81
  process.exit(0);
82
82
  }
83
83
 
84
+ if (args[0] === 'dashboard') {
85
+ const portArg = args.find(a => a.startsWith('--port='));
86
+ const port = portArg ? parseInt(portArg.split('=')[1]) : 9009;
87
+ const startDashboard = require('../monitor/dashboard/server');
88
+ const server = startDashboard({ port });
89
+
90
+ // Auto-open browser
91
+ const openUrl = `http://localhost:${port}`;
92
+ try {
93
+ const { execSync } = require('child_process');
94
+ if (process.platform === 'darwin') execSync(`open "${openUrl}"`);
95
+ else if (process.platform === 'linux') execSync(`xdg-open "${openUrl}" 2>/dev/null`);
96
+ else if (process.platform === 'win32') execSync(`start "" "${openUrl}"`);
97
+ } catch {}
98
+
99
+ process.on('SIGINT', () => {
100
+ if (server._tunnel) try { server._tunnel.kill(); } catch {}
101
+ server.close();
102
+ process.exit(0);
103
+ });
104
+ // Keep alive — don't fall through
105
+ return;
106
+ }
107
+
108
+ if (args[0] === 'scan' || args[0] === 'solve') {
109
+ const { spawnSync } = require('child_process');
110
+ const readline = require('readline');
111
+ const secretsGuard = require('../monitor/secrets_guard')({
112
+ spawnSync,
113
+ baseDir: packageDir,
114
+ analytics: null,
115
+ });
116
+
117
+ if (!secretsGuard.isTrufflehogInstalled()) {
118
+ console.error('\n trufflehog is not installed.\n');
119
+ console.error(' Install it with: brew install trufflehog');
120
+ console.error(' Or see: https://github.com/trufflesecurity/trufflehog#installation\n');
121
+ process.exit(1);
122
+ }
123
+
124
+ const onlyVerified = !args.includes('--all');
125
+ const cwd = args.find(a => !a.startsWith('-') && a !== 'scan' && a !== 'solve') || process.cwd();
126
+
127
+ console.log('\n Running TruffleHog secret scan...');
128
+ if (args[0] === 'scan' && onlyVerified) {
129
+ console.log(' Mode: verified secrets only (use --all to include unverified)\n');
130
+ } else if (args[0] === 'scan') {
131
+ console.log(' Mode: all findings (including unverified)\n');
132
+ } else {
133
+ console.log(' Mode: solve — scanning for live secrets to replace\n');
134
+ }
135
+
136
+ // For solve, always scan with onlyVerified=true (only replace live secrets)
137
+ const scanVerified = args[0] === 'solve' ? true : onlyVerified;
138
+ const result = secretsGuard.scan(cwd, { onlyVerified: scanVerified });
139
+ console.log(secretsGuard.formatResults(result));
140
+
141
+ if (args[0] === 'scan') {
142
+ process.exit(result.findings.filter(f => f.verified).length > 0 ? 1 : 0);
143
+ }
144
+
145
+ // === SOLVE mode ===
146
+ const verified = result.findings.filter(f => f.verified && f.rawFull && f.file);
147
+ if (verified.length === 0) {
148
+ console.log(' Nothing to solve — no live hardcoded secrets found.\n');
149
+ process.exit(0);
150
+ }
151
+
152
+ // Group findings by file
153
+ const fileMap = new Map(); // file → [findings]
154
+ for (const f of verified) {
155
+ if (!fileMap.has(f.file)) fileMap.set(f.file, []);
156
+ fileMap.get(f.file).push(f);
157
+ }
158
+ const fileList = [...fileMap.entries()]; // [[file, [findings]], ...]
159
+
160
+ console.log(` OpenClaw can read these ${fileList.length} file(s) containing ${verified.length} live secret(s).`);
161
+ console.log(' If not replaced, OpenClaw could read and leak them.\n');
162
+ console.log(' Use \u2191\u2193 to move, SPACE to select, A to toggle all, ENTER to confirm, Q to quit.\n');
163
+
164
+ // Build items for checkbox UI
165
+ const items = fileList.map(([filePath, findings]) => {
166
+ const types = [...new Set(findings.map(f => f.detectorName))].join(', ');
167
+ const count = findings.length;
168
+ return {
169
+ label: `${filePath.replace(os.homedir(), '~')} (${count} ${types})`,
170
+ selected: false,
171
+ };
172
+ });
173
+
174
+ let cursor = 0;
175
+
176
+ function render() {
177
+ // Move cursor up to overwrite previous render
178
+ if (items._rendered) {
179
+ process.stdout.write(`\x1b[${items.length + 1}A`);
180
+ }
181
+ for (let i = 0; i < items.length; i++) {
182
+ const check = items[i].selected ? '\x1b[32m\u25c9\x1b[0m' : '\u25cb';
183
+ const pointer = i === cursor ? '\x1b[36m\u276f\x1b[0m ' : ' ';
184
+ const dim = i === cursor ? '' : '\x1b[2m';
185
+ const reset = i === cursor ? '' : '\x1b[0m';
186
+ process.stdout.write(`\x1b[2K ${pointer}${check} ${dim}${items[i].label}${reset}\n`);
187
+ }
188
+ const selectedCount = items.filter(it => it.selected).length;
189
+ process.stdout.write(`\x1b[2K \x1b[2m${selectedCount}/${items.length} selected\x1b[0m\n`);
190
+ items._rendered = true;
191
+ }
192
+
193
+ render();
194
+
195
+ const stdin = process.stdin;
196
+ stdin.setRawMode(true);
197
+ stdin.resume();
198
+ stdin.setEncoding('utf8');
199
+
200
+ stdin.on('data', (key) => {
201
+ if (key === 'q' || key === '\x03') {
202
+ // q or Ctrl+C
203
+ stdin.setRawMode(false);
204
+ console.log('\n\n Aborted. No changes made.\n');
205
+ process.exit(0);
206
+ }
207
+ if (key === '\r' || key === '\n') {
208
+ // Enter — confirm
209
+ stdin.setRawMode(false);
210
+ stdin.pause();
211
+ const selectedFindings = [];
212
+ for (let i = 0; i < items.length; i++) {
213
+ if (items[i].selected) {
214
+ selectedFindings.push(...fileList[i][1]);
215
+ }
216
+ }
217
+ if (selectedFindings.length === 0) {
218
+ console.log('\n\n No files selected. Aborted.\n');
219
+ process.exit(0);
220
+ }
221
+ const selectedFiles = items.filter(it => it.selected).length;
222
+ console.log(`\n\n Replacing secrets in ${selectedFiles} file(s)...`);
223
+ const results = secretsGuard.solve(selectedFindings);
224
+ console.log(secretsGuard.formatSolveResults(results));
225
+ process.exit(0);
226
+ return;
227
+ }
228
+ if (key === ' ') {
229
+ // Space — toggle current item
230
+ items[cursor].selected = !items[cursor].selected;
231
+ render();
232
+ return;
233
+ }
234
+ if (key === 'a' || key === 'A') {
235
+ // A — toggle all
236
+ const allSelected = items.every(it => it.selected);
237
+ for (const it of items) it.selected = !allSelected;
238
+ render();
239
+ return;
240
+ }
241
+ if (key === '\x1b[A' || key === 'k') {
242
+ // Up arrow or k
243
+ cursor = (cursor - 1 + items.length) % items.length;
244
+ render();
245
+ return;
246
+ }
247
+ if (key === '\x1b[B' || key === 'j') {
248
+ // Down arrow or j
249
+ cursor = (cursor + 1) % items.length;
250
+ render();
251
+ return;
252
+ }
253
+ });
254
+ return; // don't fall through — raw mode is async
255
+ }
256
+
84
257
  if (args[0] === 'disable') {
85
258
  try {
86
259
  const original = fs.readFileSync(backupLink, 'utf8').trim();
@@ -9,7 +9,7 @@ const ANALYTICS_DISABLED = ['1', 'true', 'yes'].includes(
9
9
  (process.env.CONTEXTFORT_NO_ANALYTICS || '').toLowerCase()
10
10
  );
11
11
 
12
- module.exports = function createAnalytics({ httpsRequest, readFileSync, baseDir }) {
12
+ module.exports = function createAnalytics({ httpsRequest, readFileSync, baseDir, localLogger }) {
13
13
  if (ANALYTICS_DISABLED || !httpsRequest) {
14
14
  return { track() {} };
15
15
  }
@@ -27,7 +27,7 @@ module.exports = function createAnalytics({ httpsRequest, readFileSync, baseDir
27
27
 
28
28
  function track(event, properties) {
29
29
  if (!installId) return;
30
- const body = JSON.stringify({
30
+ const payload = {
31
31
  api_key: POSTHOG_API_KEY,
32
32
  event,
33
33
  properties: {
@@ -35,7 +35,13 @@ module.exports = function createAnalytics({ httpsRequest, readFileSync, baseDir
35
35
  ...properties,
36
36
  },
37
37
  timestamp: new Date().toISOString(),
38
- });
38
+ };
39
+ const body = JSON.stringify(payload);
40
+
41
+ // Log what we're sending to PostHog
42
+ if (localLogger) {
43
+ try { localLogger.logServerSend({ destination: 'posthog', event, properties: properties || {} }); } catch {}
44
+ }
39
45
 
40
46
  try {
41
47
  const req = httpsRequest({