@coworker-jp/aidr 0.1.303 → 0.1.306
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/package.json +1 -1
- package/src/red-team.mjs +74 -2
package/package.json
CHANGED
package/src/red-team.mjs
CHANGED
|
@@ -272,6 +272,68 @@ export function buildNpxMcpAddArgs(meta, scope) {
|
|
|
272
272
|
];
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
+
/**
|
|
276
|
+
* `fs.mkdir(dir, { recursive: true })` と同じことをして、**このプロセスが
|
|
277
|
+
* 実際に作ったディレクトリだけ**を深い順に返す (issue #2012)。
|
|
278
|
+
*
|
|
279
|
+
* ## なぜ「作ったかどうか」を覚える必要があるか
|
|
280
|
+
*
|
|
281
|
+
* `--scope project` は「いま居るディレクトリに入れる」意味なので、`home` は
|
|
282
|
+
* 利用者のリポジトリそのものである。**403 で拒否された回** ——
|
|
283
|
+
* 何もダウンロードせず、MCP にも何も登録しない回 —— でも
|
|
284
|
+
* `<cwd>/.claude/bin/` が先に作られていたので、**点検を試して断られただけの
|
|
285
|
+
* 人のリポジトリにディレクトリが 2 つ生えて残った。**
|
|
286
|
+
*
|
|
287
|
+
* 失敗経路で畳むなら、**自分が作ったものと、既に在ったものを必ず区別する。**
|
|
288
|
+
* 利用者の既存の `.claude/` を失敗時に消す実装は、元の欠陥よりはるかに重い。
|
|
289
|
+
*/
|
|
290
|
+
export async function mkdirTracked(dir) {
|
|
291
|
+
const created = [];
|
|
292
|
+
let cur = path.resolve(dir);
|
|
293
|
+
for (;;) {
|
|
294
|
+
try {
|
|
295
|
+
await fs.stat(cur);
|
|
296
|
+
break; // ここから上は既に在る = 我々のものではない
|
|
297
|
+
} catch (e) {
|
|
298
|
+
// **ENOENT 以外は「無い」と読まない。** 権限で見えないだけのものを
|
|
299
|
+
// 「自分が作った」と記録すると、後で他人のディレクトリを畳みにいく。
|
|
300
|
+
if (e?.code !== "ENOENT") break;
|
|
301
|
+
}
|
|
302
|
+
created.push(cur); // 深い順に積まれる = そのまま削除の順序
|
|
303
|
+
const parent = path.dirname(cur);
|
|
304
|
+
if (parent === cur) break;
|
|
305
|
+
cur = parent;
|
|
306
|
+
}
|
|
307
|
+
await fs.mkdir(dir, { recursive: true });
|
|
308
|
+
return created;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* `mkdirTracked` が返したディレクトリを、**空である間だけ**深い順に畳む。
|
|
313
|
+
*
|
|
314
|
+
* **`export` しているのは、この 2 つが単体で検査できないと安全保証が固定できない
|
|
315
|
+
* から**である。`fetchAsset` は fail-closed で、**検証を通ってから**一時ファイルを
|
|
316
|
+
* 書いて rename する (`binary-fetcher.mjs`) ので、失敗経路で `bin/` の中に物が
|
|
317
|
+
* 残ることは通常起きない —— つまり install を丸ごと駆動するテストでは、
|
|
318
|
+
* **非再帰であることが効く場面に到達できない**。守っているのは並行して書いた
|
|
319
|
+
* 別プロセス (もう 1 つの `aidr install`、利用者自身) との競合であり、そこは
|
|
320
|
+
* ここを直接呼ばないと測れない。
|
|
321
|
+
*
|
|
322
|
+
* 削除は `fs.rmdir` (非再帰) で行う。これが唯一の安全保証である ——
|
|
323
|
+
* **中身のあるディレクトリに対しては ENOTEMPTY で必ず失敗する**ので、
|
|
324
|
+
* この関数は利用者のファイルを 1 バイトも消せない。1 つでも残ったら親も
|
|
325
|
+
* 空ではないので、そこで止める。
|
|
326
|
+
*/
|
|
327
|
+
export async function pruneEmptyCreatedDirs(dirs) {
|
|
328
|
+
for (const dir of dirs) {
|
|
329
|
+
try {
|
|
330
|
+
await fs.rmdir(dir);
|
|
331
|
+
} catch {
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
275
337
|
/**
|
|
276
338
|
* Red Team バイナリを取得し、Claude Code に MCP サーバーとして登録する。
|
|
277
339
|
*
|
|
@@ -372,8 +434,18 @@ export async function installRedTeam({
|
|
|
372
434
|
return { installed: false, dryRun: true, path: binPath };
|
|
373
435
|
}
|
|
374
436
|
|
|
375
|
-
|
|
376
|
-
const
|
|
437
|
+
// 失敗した回に痕跡を残さないため、作ったディレクトリを覚えておく (#2012)。
|
|
438
|
+
const createdDirs = await mkdirTracked(binDir);
|
|
439
|
+
let res;
|
|
440
|
+
try {
|
|
441
|
+
res = await fetchRedTeamBinary(binPath, { env, accessKey, product });
|
|
442
|
+
} catch (e) {
|
|
443
|
+
// ダウンロードが失敗した = 何も置いていない。**空のままなら畳む。**
|
|
444
|
+
// 部分書き込みが残っていれば `rmdir` が ENOTEMPTY で拒むので、
|
|
445
|
+
// 診断できるものを勝手に消すこともない。
|
|
446
|
+
await pruneEmptyCreatedDirs(createdDirs);
|
|
447
|
+
throw e;
|
|
448
|
+
}
|
|
377
449
|
stdout(
|
|
378
450
|
`binary: ${res.path} (${res.platform}, sha256 verified)`,
|
|
379
451
|
);
|