wormclaude 1.0.144 → 1.0.146
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/cmdsec.js +24 -5
- package/dist/inlinetools.js +85 -4
- package/dist/theme.js +1 -1
- package/dist/tools.js +1 -1
- package/dist/tui.js +4 -1
- package/package.json +1 -1
package/dist/cmdsec.js
CHANGED
|
@@ -167,6 +167,23 @@ const DANGER = [
|
|
|
167
167
|
{ re: /\bmv\s+[^\n]*\s+\/dev\/null\b/, reason: 'Veriyi /dev/null\'a taşıma' },
|
|
168
168
|
{ re: />\s*\/dev\/null\s+2>&1\s*;\s*rm/, reason: 'Gizli silme' },
|
|
169
169
|
];
|
|
170
|
+
// ── Windows tehlikeli komutlar (HARD DENY) — kullanıcı Windows'ta ──────────
|
|
171
|
+
// Yalnız SİSTEM / sürücü-kökü hedefleri engellenir; proje altı (C:\Users\...\build)
|
|
172
|
+
// silmeleri meşrudur → onay akışına düşer, hard-deny EDİLMEZ.
|
|
173
|
+
const WIN_SYS = '(?:[a-zA-Z]:\\\\?(?:windows\\b|program\\s?files(?:\\s?\\(x86\\))?\\b|programdata\\b)' + // C:\Windows, C:\Program Files, C:\ProgramData
|
|
174
|
+
'|[a-zA-Z]:\\\\?(?:["\'\\s]|$)' + // C:\ (sürücü kökü)
|
|
175
|
+
'|system32\\b|%(?:systemroot|windir|systemdrive)%|\\$env:(?:windir|systemroot|systemdrive))';
|
|
176
|
+
const WIN_DANGER = [
|
|
177
|
+
// del / erase / rd / rmdir /s → sistem ya da sürücü kökü
|
|
178
|
+
{ re: new RegExp('\\b(?:del|erase|rd|rmdir)\\b(?=[\\s\\S]*\\s/s\\b)[\\s\\S]*?' + WIN_SYS, 'i'),
|
|
179
|
+
reason: 'Windows sistem/sürücü kökünde özyinelemeli silme (del/rd /s)' },
|
|
180
|
+
// Remove-Item (alias ri/rm/del) -Recurse [-Force] → sistem ya da sürücü kökü
|
|
181
|
+
{ re: new RegExp('\\b(?:remove-item|ri|rm|rmdir|rd|del)\\b(?=[\\s\\S]*-(?:recurse|r)\\b)[\\s\\S]*?' + WIN_SYS, 'i'),
|
|
182
|
+
reason: 'Windows kök/sistem yolunda Remove-Item -Recurse' },
|
|
183
|
+
{ re: /\bformat\b\s+[a-zA-Z]:/i, reason: 'Disk biçimlendirme (format)' },
|
|
184
|
+
{ re: /\bFormat-Volume\b/i, reason: 'Disk biçimlendirme (Format-Volume)' },
|
|
185
|
+
{ re: /\b(?:Stop-Computer|Restart-Computer|shutdown(?:\.exe)?\b|Clear-Disk|Remove-Partition)\b/i, reason: 'Sistemi kapatma/disk temizleme' },
|
|
186
|
+
];
|
|
170
187
|
// ── Read-only komut tespiti (shellReadOnlyChecker.js'ten — sağlam) ──────────
|
|
171
188
|
const READONLY_ROOTS = new Set([
|
|
172
189
|
'awk', 'basename', 'cat', 'cd', 'column', 'cut', 'df', 'dirname', 'du', 'echo', 'env', 'find',
|
|
@@ -278,15 +295,17 @@ export function isShellCommandReadOnly(command) {
|
|
|
278
295
|
return segs.length > 0 && segs.every(cmdIsReadOnly);
|
|
279
296
|
}
|
|
280
297
|
// ── Asıl güvenlik motoru ───────────────────────────────────────────────────
|
|
281
|
-
export function checkCommand(rawCommand) {
|
|
298
|
+
export function checkCommand(rawCommand, opts) {
|
|
282
299
|
const command = stripShellWrapper(String(rawCommand || ''));
|
|
283
300
|
const roots = getCommandRoots(command);
|
|
284
|
-
|
|
285
|
-
|
|
301
|
+
const shell = opts?.shell;
|
|
302
|
+
// 1) Command substitution -> HARD DENY (yalnız bash/sh için; PowerShell'de `$()` ve backtick
|
|
303
|
+
// NORMAL sözdizimidir — alt-ifade / escape — bash komut-ikamesi değil → atla).
|
|
304
|
+
if (shell !== 'powershell' && detectCommandSubstitution(command)) {
|
|
286
305
|
return { decision: 'deny', reason: 'Komut ikamesi ($(), <(), backtick) güvenlik nedeniyle engellendi', roots };
|
|
287
306
|
}
|
|
288
|
-
// 2) Tehlikeli blocklist -> HARD DENY
|
|
289
|
-
for (const d of DANGER) {
|
|
307
|
+
// 2) Tehlikeli blocklist (POSIX + Windows) -> HARD DENY
|
|
308
|
+
for (const d of [...DANGER, ...WIN_DANGER]) {
|
|
290
309
|
try {
|
|
291
310
|
if (d.re instanceof RegExp && d.re.test(command))
|
|
292
311
|
return { decision: 'deny', reason: d.reason, roots };
|
package/dist/inlinetools.js
CHANGED
|
@@ -128,6 +128,72 @@ function toToolCall(obj, i) {
|
|
|
128
128
|
function looksLikeCall(o) {
|
|
129
129
|
return !!o && typeof o === 'object' && (typeof o.name === 'string' || typeof o.tool === 'string' || (o.function && typeof o.function === 'object'));
|
|
130
130
|
}
|
|
131
|
+
// Sarmasız (wrapper'sız) üst-düzey JSON için SIKI kontrol: yalnız adı BİLİNEN bir araç olan
|
|
132
|
+
// veya `function`/`arguments` yapısı taşıyan nesne çağrıdır. Aksi halde `package.json` gibi
|
|
133
|
+
// `"name"` içeren GERÇEK dosya içerikleri yanlışlıkla araç çağrısı sanılırdı (latent bug).
|
|
134
|
+
const _KNOWN_TOOL_RE = /^(?:Bash|PowerShell|Read|Write|Edit|Glob|Grep|WebFetch|WebSearch|Sleep|TaskOutput|AskUserQuestion)$/;
|
|
135
|
+
function looksLikeKnownCall(o) {
|
|
136
|
+
if (!o || typeof o !== 'object')
|
|
137
|
+
return false;
|
|
138
|
+
if (o.function && typeof o.function === 'object')
|
|
139
|
+
return true;
|
|
140
|
+
const nm = o.name || o.tool || o.tool_name;
|
|
141
|
+
if (typeof nm !== 'string' || !_KNOWN_TOOL_RE.test(nm))
|
|
142
|
+
return false;
|
|
143
|
+
// Bilinen ad + bir argüman anahtarı (çıplak `{"name":"Read"}` da geçerli olabilir → ad yeter).
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
// ---- DOSYA-BLOĞU KURTARMA (build paste-vs-write) -----------------------------
|
|
147
|
+
// Model bir build görevinde Write çağırmak yerine ```kod bloğu YAPIŞTIRABİLİR ve
|
|
148
|
+
// dosya hiç oluşmaz. Bunu kurtarıyoruz — AMA yalnız blok AÇIKÇA bir dosya adıyla
|
|
149
|
+
// etiketliyse (`index.html`, **app.py**, `# server.py`, `Dosya: x.css`…). Etiket
|
|
150
|
+
// yoksa (saf "kod göster" / örnek) DOKUNMAYIZ. Recovered Write yine onay ister →
|
|
151
|
+
// kullanıcı sadece görmek istediyse reddeder (güvenli).
|
|
152
|
+
const _CODE_EXT = 'py|js|jsx|ts|tsx|html?|htm|css|scss|sass|less|json|jsonc|sh|bash|zsh|go|rs|java|kt|c|cc|cpp|cxx|h|hpp|rb|php|sql|ya?ml|toml|ini|env|xml|svg|vue|svelte|astro|md|markdown|txt|dockerfile|makefile|cs|swift|dart|lua|pl|r|m|mjs|cjs|tf|gradle|bat|ps1';
|
|
153
|
+
const _ECHO_FILE_LANGS = /^(plaintext|plain|text|txt|http|https|console|output|log|raw|diff|patch)$/i;
|
|
154
|
+
// Dosya-adı token'ı: `path/to/file.ext` veya `file.ext` (gerçek kod uzantısı şart).
|
|
155
|
+
const _FNAME = '(?:[\\w./\\\\-]+\\.(?:' + _CODE_EXT + ')|Dockerfile|Makefile|\\.(?:env|gitignore|babelrc)[\\w.-]*)';
|
|
156
|
+
// Etiket biçimleri — fence'ten HEMEN önce (≤2 satır), aynı paragraf:
|
|
157
|
+
// `file.ext` | **file.ext** | ## file.ext | File:/Dosya:/Filename: file.ext | file.ext:
|
|
158
|
+
const _LABEL_RE = new RegExp('(?:^|\\n)[ \\t>*#`\\-]*' +
|
|
159
|
+
'(?:(?:file|dosya|filename|dosya adı|path|yol)\\s*[:=]\\s*)?' +
|
|
160
|
+
'["`*\\[(]*\\s*(' + _FNAME + ')\\s*["`*\\])]*\\s*:?[ \\t]*' + // **bold**/`tick`/[bracket] çoklu sarmal
|
|
161
|
+
'(?:\\([^)\\n]*\\))?[ \\t]*' + // "index.html (ana sayfa)" gibi parantez-not
|
|
162
|
+
'\\r?\\n(?:[ \\t]*\\r?\\n)?' + // araya en çok bir boş satır
|
|
163
|
+
'```([a-zA-Z0-9_+-]*)[ \\t]*\\r?\\n([\\s\\S]*?)```', 'gi');
|
|
164
|
+
/** Metindeki dosya-adı etiketli kod bloklarını bulur (span'larıyla). Konservatif:
|
|
165
|
+
* gerçek kod uzantısı + echo-olmayan dil + ≥2 satır/≥40 karakter gövde şart. */
|
|
166
|
+
export function findFileBlocks(text) {
|
|
167
|
+
const t = text || '';
|
|
168
|
+
const out = [];
|
|
169
|
+
const seen = new Set();
|
|
170
|
+
let m;
|
|
171
|
+
_LABEL_RE.lastIndex = 0;
|
|
172
|
+
while ((m = _LABEL_RE.exec(t)) !== null) {
|
|
173
|
+
const file = m[1].trim();
|
|
174
|
+
const lang = (m[2] || '').trim();
|
|
175
|
+
const body = m[3] || '';
|
|
176
|
+
if (_ECHO_FILE_LANGS.test(lang))
|
|
177
|
+
continue; // komut/çıktı bloğu — dosya değil
|
|
178
|
+
const nonEmpty = body.split('\n').filter((l) => l.trim()).length;
|
|
179
|
+
if (body.trim().length < 15 && nonEmpty < 2)
|
|
180
|
+
continue; // önemsiz snippet (`a=1` gibi) — etiket asıl sinyal
|
|
181
|
+
const key = file + '|' + body.length;
|
|
182
|
+
if (seen.has(key))
|
|
183
|
+
continue;
|
|
184
|
+
seen.add(key);
|
|
185
|
+
out.push({ file, content: body.replace(/\s+$/, '') + '\n', start: m.index, end: m.index + m[0].length });
|
|
186
|
+
}
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
189
|
+
/** Dosya-bloklarını Write çağrılarına çevirir (yapısal/inline tool-call YOKKEN son çare). */
|
|
190
|
+
export function recoverFileBlocks(text) {
|
|
191
|
+
return findFileBlocks(text).map((b, i) => ({
|
|
192
|
+
id: `fileblock_${i}`,
|
|
193
|
+
name: 'Write',
|
|
194
|
+
args: JSON.stringify({ file_path: b.file, content: b.content }),
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
131
197
|
/** Metindeki üst-düzey {…} JSON nesnelerini (string/escape duyarlı) çıkarır.
|
|
132
198
|
* Model çağrıları sarmasız ve BİRDEN FAZLA nesne olarak dökebilir: {…}{…} veya {…}\n{…}
|
|
133
199
|
* (abliterated coder davranışı). Her üst-düzey nesneyi ayrı parça olarak döndürür. */
|
|
@@ -169,7 +235,8 @@ function extractTopLevelJsonObjects(text) {
|
|
|
169
235
|
export function recoverInlineToolCalls(text) {
|
|
170
236
|
const t = (text || '').trim();
|
|
171
237
|
// JSON/prose/ToolName{…}/ToolName(…) çağrısı yoksa erken çık.
|
|
172
|
-
|
|
238
|
+
// (Kod fence'i varsa çıkma — dosya-adı etiketli blok olabilir, adım 7 karar verir.)
|
|
239
|
+
if (!t || (!t.includes('"name"') && !t.includes('"tool"') && !t.includes('"function"') && !/AskUserQuestion/i.test(t) && !TOOL_PREFIX_RE.test(t) && !TOOL_PAREN_RE.test(t) && !t.includes('```')))
|
|
173
240
|
return [];
|
|
174
241
|
const out = [];
|
|
175
242
|
const seen = new Set();
|
|
@@ -193,7 +260,9 @@ export function recoverInlineToolCalls(text) {
|
|
|
193
260
|
if (!out.length) {
|
|
194
261
|
for (const m of t.matchAll(/```(?:json|tool_call|tool|function)?\s*([\s\S]*?)```/gi)) {
|
|
195
262
|
const o = safeJsonParse(m[1].trim(), null);
|
|
196
|
-
|
|
263
|
+
// SIKI: ```json içine konmuş GERÇEK dosya (`{"name":"app",...}`) tool-call sanılmasın;
|
|
264
|
+
// sarmalı tool-call'lar zaten bilinen araç adı (Write/Bash…) kullanır → step 7 dosyayı alır.
|
|
265
|
+
if (looksLikeKnownCall(o))
|
|
197
266
|
push(o);
|
|
198
267
|
}
|
|
199
268
|
}
|
|
@@ -202,8 +271,8 @@ export function recoverInlineToolCalls(text) {
|
|
|
202
271
|
if (!out.length) {
|
|
203
272
|
for (const frag of extractTopLevelJsonObjects(t)) {
|
|
204
273
|
const o = safeJsonParse(frag, null);
|
|
205
|
-
if (
|
|
206
|
-
push(o);
|
|
274
|
+
if (looksLikeKnownCall(o))
|
|
275
|
+
push(o); // SIKI: package.json gibi dosyaları çağrı sanma
|
|
207
276
|
}
|
|
208
277
|
}
|
|
209
278
|
// 4) AskUserQuestion'ı JSON yerine DÜZ-METİN (prose) yazan model (abliterated):
|
|
@@ -265,6 +334,18 @@ export function recoverInlineToolCalls(text) {
|
|
|
265
334
|
}
|
|
266
335
|
}
|
|
267
336
|
}
|
|
337
|
+
// 7) SON ÇARE: hiç çağrı kurtarılamadıysa, dosya-adı etiketli ```kod bloklarını
|
|
338
|
+
// Write'a çevir (model build'de Write yerine kod yapıştırınca dosya kaybolmasın).
|
|
339
|
+
// Adım 7, gerçek tool-call YOKKEN çalışır → "kod göster"i bozmaz (etiket gerekir).
|
|
340
|
+
if (!out.length) {
|
|
341
|
+
for (const tc of recoverFileBlocks(t)) {
|
|
342
|
+
const k = tc.name + '|' + tc.args;
|
|
343
|
+
if (!seen.has(k)) {
|
|
344
|
+
seen.add(k);
|
|
345
|
+
out.push(tc);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
268
349
|
return out;
|
|
269
350
|
}
|
|
270
351
|
/** Modelin komut/çıktıyı TEKRAR yazdığı kod-bloklarını siler (```plaintext/bash/http/console…).
|
package/dist/theme.js
CHANGED
package/dist/tools.js
CHANGED
|
@@ -694,7 +694,7 @@ async function execOne(call, hooks) {
|
|
|
694
694
|
}
|
|
695
695
|
// 3.5) Komut güvenliği (Bash/PowerShell) — cmdsec: deny→blokla, allow→izinsiz, confirm→izin akışı
|
|
696
696
|
if ((call.name === 'Bash' || call.name === 'PowerShell') && args && args.command) {
|
|
697
|
-
const chk = checkCommand(String(args.command));
|
|
697
|
+
const chk = checkCommand(String(args.command), { shell: call.name === 'PowerShell' ? 'powershell' : 'bash' });
|
|
698
698
|
if (chk.decision === 'deny') {
|
|
699
699
|
return { ok: false, output: `⛔ Güvenlik: komut engellendi — ${chk.reason || 'tehlikeli komut'}`, args };
|
|
700
700
|
}
|
package/dist/tui.js
CHANGED
|
@@ -445,7 +445,10 @@ export async function runTui() {
|
|
|
445
445
|
if (allowAll.has(c.name))
|
|
446
446
|
return resolve('allow');
|
|
447
447
|
// Claude tarzı: Write/Edit'te YAZMADAN ÖNCE dosya içeriğini göster, sonra onay iste.
|
|
448
|
-
|
|
448
|
+
// İSTİSNA: kurtarılan dosya-bloğu (id `fileblock_`) içeriği zaten akışta prose olarak
|
|
449
|
+
// gösterildi → fileprev'i ATLA, yoksa aynı kod iki kez basılır.
|
|
450
|
+
const _recoveredBlock = typeof c.id === 'string' && c.id.startsWith('fileblock_');
|
|
451
|
+
if (!_recoveredBlock && ((c.name === 'Write' && args?.content != null) || (c.name === 'Edit' && args?.new_string != null))) {
|
|
449
452
|
const _content = String(c.name === 'Write' ? args.content : args.new_string);
|
|
450
453
|
printItem({ kind: 'fileprev', file: relWs(args.file_path), lines: _content.split('\n') });
|
|
451
454
|
}
|