natureco-cli 5.42.0 → 5.43.1

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.
@@ -1,4 +1,4 @@
1
- const { spawn, execSync } = require("child_process");
1
+ const { spawn, execFileSync } = require("child_process");
2
2
  const os = require("os");
3
3
 
4
4
  const IS_MAC = os.platform() === "darwin";
@@ -63,7 +63,7 @@ function getOpenBrowser() {
63
63
  ];
64
64
  for (const browser of browsers) {
65
65
  try {
66
- const result = execSync(`pgrep -x "${browser.name}"`, { encoding: "utf8" });
66
+ const result = execFileSync("pgrep", ["-x", browser.name], { encoding: "utf8" }); // v5.43: shell yok
67
67
  if (result.trim()) return browser.cmd;
68
68
  } catch (e) {}
69
69
  }
@@ -2,7 +2,7 @@
2
2
  * youtube_ac - YouTube videosunu mevcut tarayıcıda yeni sekmede açar
3
3
  */
4
4
 
5
- const { spawn, execSync } = require("child_process");
5
+ const { spawn, execFileSync } = require("child_process");
6
6
  const os = require("os");
7
7
 
8
8
  const IS_MAC = os.platform() === "darwin";
@@ -16,7 +16,7 @@ function getOpenBrowser() {
16
16
 
17
17
  for (const browser of browsers) {
18
18
  try {
19
- const result = execSync(`pgrep -x "${browser.name}"`, { encoding: "utf8" });
19
+ const result = execFileSync("pgrep", ["-x", browser.name], { encoding: "utf8" }); // v5.43: shell yok
20
20
  if (result.trim()) {
21
21
  return browser.cmd;
22
22
  }
@@ -118,16 +118,27 @@ function requiresApproval({ command, agentId, security, ask }) {
118
118
  return { required: true, reason: 'unknown' };
119
119
  }
120
120
 
121
- // Built-in safe commands that never need approval
121
+ // Built-in safe commands that never need approval.
122
+ // v5.43 GÜVENLİK: 'node -e' KALDIRILDI — inline eval (`node -e "require('fs').rmSync..."`)
123
+ // keyfi kod çalıştırır, asla "safe" olamaz. Sadece salt-okunur/versiyon komutları kalır.
122
124
  const SAFE_COMMANDS = new Set([
123
125
  'ls', 'cat', 'head', 'tail', 'echo', 'pwd', 'date', 'whoami',
124
- 'node -e', 'node -v', 'npm -v', 'git status', 'git diff', 'git log',
126
+ 'node -v', 'npm -v', 'git status', 'git diff', 'git log',
125
127
  ]);
126
128
 
129
+ // Shell metakarakterleri: komut zincirleme / substitution / yönlendirme.
130
+ // Bunlardan biri varsa komut ASLA "safe" sayılmaz (ör. "echo hi; rm -rf ~").
131
+ const SHELL_METACHARS = /[;&|`$(){}<>\n\r]|\|\||&&/;
132
+
127
133
  function isSafeCommand(command) {
128
- if (SAFE_COMMANDS.has(command.trim())) return true;
134
+ const trimmed = (command || '').trim();
135
+ if (!trimmed) return false;
136
+ // v5.43 GÜVENLİK: metakarakter içeren hiçbir komut safe değil — prefix bypass'ı kapatır.
137
+ if (SHELL_METACHARS.test(trimmed)) return false;
138
+ if (SAFE_COMMANDS.has(trimmed)) return true;
139
+ // Prefix eşleşmesi ama SADECE kelime sınırında: "echo" → "echo hi" evet, "echoevil" hayır.
129
140
  for (const safe of SAFE_COMMANDS) {
130
- if (command.trim().startsWith(safe)) return true;
141
+ if (trimmed === safe || trimmed.startsWith(safe + ' ')) return true;
131
142
  }
132
143
  return false;
133
144
  }
@@ -34,9 +34,13 @@ function getProfileDir() {
34
34
  const ACTIVE_CONFIG_DIR = getProfileDir();
35
35
  const ACTIVE_CONFIG_FILE = path.join(ACTIVE_CONFIG_DIR, 'config.json');
36
36
 
37
+ // v5.43 GÜVENLİK: config.json API anahtarları tutar; dizin/dosya dünya-okunabilir
38
+ // (0755/0644) olmamalı — ssh anahtarları gibi 0700/0600. chmod fallback eski kurulumlar için.
37
39
  function ensureConfigDir() {
38
40
  if (!fs.existsSync(ACTIVE_CONFIG_DIR)) {
39
- fs.mkdirSync(ACTIVE_CONFIG_DIR, { recursive: true });
41
+ fs.mkdirSync(ACTIVE_CONFIG_DIR, { recursive: true, mode: 0o700 });
42
+ } else {
43
+ try { fs.chmodSync(ACTIVE_CONFIG_DIR, 0o700); } catch { /* best-effort */ }
40
44
  }
41
45
  }
42
46
 
@@ -48,11 +52,15 @@ function createBackup() {
48
52
  if (!fs.existsSync(ACTIVE_CONFIG_FILE)) return;
49
53
  ensureConfigDir();
50
54
  if (!fs.existsSync(CONFIG_BACKUP_DIR)) {
51
- fs.mkdirSync(CONFIG_BACKUP_DIR, { recursive: true });
55
+ fs.mkdirSync(CONFIG_BACKUP_DIR, { recursive: true, mode: 0o700 });
56
+ } else {
57
+ try { fs.chmodSync(CONFIG_BACKUP_DIR, 0o700); } catch { /* best-effort */ }
52
58
  }
53
59
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
54
60
  const backupFile = path.join(CONFIG_BACKUP_DIR, `config-${timestamp}.json`);
55
61
  fs.copyFileSync(ACTIVE_CONFIG_FILE, backupFile);
62
+ // Yedek de API anahtarları içerir → 0600.
63
+ try { fs.chmodSync(backupFile, 0o600); } catch { /* best-effort */ }
56
64
  const backups = fs.readdirSync(CONFIG_BACKUP_DIR)
57
65
  .filter(f => f.startsWith('config-') && f.endsWith('.json'))
58
66
  .sort()
@@ -91,7 +99,9 @@ function saveConfig(data, options = {}) {
91
99
  if (!skipValidation) validateConfig(data);
92
100
  if (!skipBackup) createBackup();
93
101
  const content = JSON.stringify(data, null, 2);
94
- fs.writeFileSync(ACTIVE_CONFIG_FILE, content, 'utf8');
102
+ fs.writeFileSync(ACTIVE_CONFIG_FILE, content, { encoding: 'utf8', mode: 0o600 });
103
+ // mode yalnızca dosya YENİ oluşturulunca uygulanır; mevcut dosya için chmod şart.
104
+ try { fs.chmodSync(ACTIVE_CONFIG_FILE, 0o600); } catch { /* best-effort */ }
95
105
  _configCache = data;
96
106
  _configHash = computeHash(data);
97
107
  }
@@ -203,7 +213,10 @@ function restoreConfig(backupFile) {
203
213
  const data = parseConfigContent(content);
204
214
  validateConfig(data);
205
215
  createBackup();
206
- fs.writeFileSync(ACTIVE_CONFIG_FILE, JSON.stringify(data, null, 2), 'utf8');
216
+ // v5.43.1 GÜVENLİK: saveConfig gibi 0600 — restore, API key'li config.json'ı
217
+ // dünya-okunabilir hale getirmemeli. mode yalnızca yeni dosyaya uygulanır; chmod şart.
218
+ fs.writeFileSync(ACTIVE_CONFIG_FILE, JSON.stringify(data, null, 2), { encoding: 'utf8', mode: 0o600 });
219
+ try { fs.chmodSync(ACTIVE_CONFIG_FILE, 0o600); } catch { /* best-effort */ }
207
220
  _configCache = data;
208
221
  _configHash = computeHash(data);
209
222
  return { path: backupPath, timestamp: path.basename(backupPath).replace(/^config-|\.json$/g, '') };
@@ -99,9 +99,11 @@ async function executeTool(toolName, params, opts = {}) {
99
99
 
100
100
  // ── Onay mekanizması (write_file ve tehlikeli bash) ───────────────────────
101
101
  if (agentMode) {
102
+ // v5.43 GÜVENLİK: shell_command da bash gibi onay tetikler (aksi halde onay/güvenlik
103
+ // akışını atlayan ikinci bir arbitrary-shell yolu olurdu).
102
104
  const needsConfirm =
103
105
  toolName === 'write_file' ||
104
- (toolName === 'bash' && /\b(rm|mv|cp|chmod|chown|dd|mkfs|truncate)\b/.test(safeParams.command || ''));
106
+ ((toolName === 'bash' || toolName === 'shell_command') && /\b(rm|mv|cp|chmod|chown|dd|mkfs|truncate)\b/.test(safeParams.command || ''));
105
107
 
106
108
  if (needsConfirm) {
107
109
  if (toolName === 'write_file') {