sync: update from private repo (2336949a)
CI / build-and-test (push) Canceled after 0s

This commit is contained in:
oss-sync
2026-09-17 11:29:31 +00:00
parent 28f74c4e37
commit 57702b4f34
2139 changed files with 321270 additions and 17494 deletions
+113
View File
@@ -0,0 +1,113 @@
#!/usr/bin/env node
// Real Settings UI + persisted config + live CSP. Only loopback test services.
import assert from 'node:assert/strict';
import { createServer } from 'node:http';
import { once } from 'node:events';
import { mkdtemp, mkdir, copyFile, writeFile, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { spawn } from 'node:child_process';
import { chromium } from 'playwright';
const root = resolve(import.meta.dirname, '../..');
const temp = await mkdtemp(join(tmpdir(), 'maestro-egress-settings-'));
const delay = ms => new Promise(resolveDelay => setTimeout(resolveDelay, ms));
let worker, browser;
let output = '';
const servers = [];
async function listen(server) { server.listen(0, '127.0.0.1'); await once(server, 'listening'); servers.push(server); return `http://127.0.0.1:${server.address().port}`; }
async function stop() {
if (!worker || worker.exitCode !== null || worker.signalCode !== null) return;
const done = once(worker, 'exit'); worker.kill('SIGTERM');
const timeout = setTimeout(() => worker?.kill('SIGKILL'), 5000);
try { await done; } finally { clearTimeout(timeout); worker = undefined; }
}
try {
await mkdir(join(temp, 'dist/db'), { recursive: true });
await copyFile(join(root, 'dist/db/schema.sql'), join(temp, 'dist/db/schema.sql'));
let hits = 0;
const canary = await listen(createServer((_req, res) => { hits++; res.setHeader('Access-Control-Allow-Origin', '*'); res.end('ok'); }));
const llm = await listen(createServer((_req, res) => { res.setHeader('Content-Type', 'application/json'); res.end('{"data":[{"id":"test-model"}],"models":[]}'); }));
const reserve = createServer(); reserve.listen(0, '127.0.0.1'); await once(reserve, 'listening');
const port = reserve.address().port; await new Promise(resolveClose => reserve.close(resolveClose));
const base = `http://127.0.0.1:${port}`;
const config = join(temp, 'config.yaml');
await writeFile(config, 'safety:\n bash_allow_network: true\n');
const env = { PATH: process.env.PATH, HOME: temp, NODE_ENV: 'test', AAO_MODE: 'worker', AAO_CONFIG: config,
PORT: String(port), DB_PATH: join(temp, 'test.db'), WORKTREE_DIR: join(temp, 'workspaces'),
UI_DIST_PATH: join(root, 'ui/dist'), OLLAMA_BASE_URL: `${llm}/v1`, OLLAMA_MODEL: 'test-model', LOG_LEVEL: 'info' };
async function start(extra = {}) {
output = '';
worker = spawn(process.execPath, [join(root, 'dist/main.js')], { cwd: temp, env: { ...env, ...extra }, stdio: ['ignore', 'pipe', 'pipe'] });
const capture = value => { output += value.toString(); if (output.length > 2_000_000) worker?.kill('SIGTERM'); };
worker.stdout.on('data', capture); worker.stderr.on('data', capture);
worker.on('error', error => { output += error.message; });
for (let i = 0; i < 300; i++) {
if (worker.exitCode !== null) throw new Error(`worker startup failed: ${output.slice(-5000)}`);
try { if ((await fetch(`${base}/ui/`, { signal: AbortSignal.timeout(1000) })).ok) return; } catch {}
await delay(100);
}
throw new Error('worker did not start');
}
await start();
browser = await chromium.launch({ headless: true, args: ['--no-sandbox', '--disable-dev-shm-usage'] });
const context = await browser.newContext({ locale: 'ja-JP', serviceWorkers: 'block' });
context.setDefaultTimeout(15000);
const external = [];
await context.route('**/*', route => {
const origin = new URL(route.request().url()).origin;
if ([base, canary].includes(origin)) return route.continue();
external.push(origin); return route.abort();
});
await context.routeWebSocket('**/*', route => {
if (new URL(route.url()).origin.replace(/^ws/, 'http') === base) route.connectToServer();
else { external.push(new URL(route.url()).origin); route.close(); }
});
const page = await context.newPage();
await page.goto(`${base}/ui/?page=settings&section=egress-policy`);
const mode = page.getByRole('combobox', { name: 'ブラウザの通信', exact: true });
const observe = page.getByRole('checkbox', { name: 'サーバー通信の接続先を記録', exact: true });
const connectOrigins = page.getByRole('textbox', { name: '接続の許可先', exact: true });
await mode.waitFor();
assert.equal(await mode.inputValue(), 'off');
const pid = worker.pid;
async function save() {
const response = page.waitForResponse(r => r.url() === `${base}/api/config` && r.request().method() === 'PUT');
await page.getByRole('button', { name: '保存して反映', exact: true }).click();
assert.equal((await response).status(), 200, 'settings save succeeds');
assert.equal(worker.pid, pid, 'settings apply without restarting worker');
}
async function attempt() { return page.evaluate(url => fetch(url).then(r => r.ok, () => false), canary); }
await mode.selectOption('report-only'); await observe.check(); await save();
assert((await fetch(`${base}/ui/`)).headers.get('content-security-policy-report-only')?.includes('connect-src'));
await page.reload(); assert.equal(await attempt(), true, 'report-only does not block');
assert(output.includes('Node destination observation enabled'), 'Node monitoring enabled live');
await mode.selectOption('enforce'); await save();
await page.reload(); const beforeBlocked = hits;
assert.equal(await attempt(), false, 'enforce blocks unlisted destination');
assert.equal(hits, beforeBlocked, 'blocked request never reaches destination');
await connectOrigins.fill(canary); await save();
await page.reload(); assert.equal(await attempt(), true, 'adding an allowed origin works');
const onDisk = await readFile(config, 'utf8');
assert(onDisk.includes('observe_node: true') && onDisk.includes('mode: enforce'), 'settings persisted to YAML');
await stop(); await start(); await page.reload();
assert.equal(await mode.inputValue(), 'enforce');
assert.equal(await connectOrigins.inputValue(), canary);
assert.equal(await observe.isChecked(), true);
assert.equal(await attempt(), true, 'saved policy survives restart');
const locked = join(temp, 'external.json');
await writeFile(locked, JSON.stringify({ version: 1, observeNode: false, browser: { mode: 'report-only', connectOrigins: [], imageOrigins: [], frameOrigins: [] } }));
await stop(); await start({ AAO_EGRESS_POLICY: locked }); await page.reload();
assert.equal(await mode.inputValue(), 'report-only');
assert.equal(await mode.isDisabled(), true); assert.equal(await observe.isDisabled(), true); assert.equal(await connectOrigins.isDisabled(), true);
const beforeWrite = await readFile(config, 'utf8');
const rejected = await fetch(`${base}/api/config`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ egress: { browser: { mode: 'off' } } }) });
assert.equal(rejected.status, 400, 'generic API cannot bypass external lock');
assert.equal(await readFile(config, 'utf8'), beforeWrite);
assert.deepEqual(external, []);
await context.close();
console.log('Settings UI: live report/enforce/allowlist, Node monitoring, persistence, external lock passed');
} finally {
await stop(); await browser?.close();
for (const server of servers) { server.closeAllConnections(); server.close(); }
await rm(temp, { recursive: true, force: true });
}