mirror of
https://github.com/Comfy-Org/ComfyUI-Manager.git
synced 2026-05-09 08:32:30 +08:00
Defense-in-depth over GET→POST alone: reject the three CORS-safelisted simple-form Content-Types (x-www-form-urlencoded, multipart/form-data, text/plain) on 16 no-body POST handlers (glob + legacy) to block <form method=POST> CSRF that bypasses method-only gating. Move comfyui_switch_version to a JSON body so the preflight requirement applies. Split db_mode/policy/update/channel_url_list into GET(read) + POST(write). Tighten do_fix (high → high+) and gate three previously-ungated config setters at middle. Resynchronize openapi.yaml (27 paths, 30 operations, ComfyUISwitchVersionParams as a shared $ref component). Add E2E harness variants, Playwright config, CSRF/secgate suites, 39-endpoint coverage, and a CHANGELOG. Breaking: legacy per-op POST routes (install/uninstall/fix/disable/update/ reinstall/abort_current) are removed; callers already use queue/batch. Legacy /manager/notice (v1) is removed; /v2/manager/notice is retained. Reported-by: XlabAI Team of Tencent Xuanwu Lab CVSS: 8.1 (AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H)
64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
/**
|
|
* E2E tests: Dialog navigation and lifecycle.
|
|
*
|
|
* Tests opening/closing dialogs, nested dialog navigation, and
|
|
* verifies no duplicate instances are created.
|
|
*
|
|
* Requires ComfyUI running with --enable-manager-legacy-ui on PORT.
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
import { waitForComfyUI, openManagerMenu, clickMenuButton, closeDialog, assertManagerMenuVisible } from './helpers';
|
|
|
|
test.describe('Dialog Navigation', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/');
|
|
await waitForComfyUI(page);
|
|
});
|
|
|
|
test('Manager menu → Custom Nodes → close → Manager still visible', async ({ page }) => {
|
|
await openManagerMenu(page);
|
|
await assertManagerMenuVisible(page);
|
|
|
|
await clickMenuButton(page, 'Custom Nodes Manager');
|
|
await page.waitForSelector('#cn-manager-dialog', {
|
|
timeout: 15_000,
|
|
});
|
|
|
|
// Close the Custom Nodes dialog
|
|
await closeDialog(page);
|
|
await page.waitForTimeout(500);
|
|
|
|
// Manager menu should still be accessible (reopen if needed)
|
|
await openManagerMenu(page);
|
|
await assertManagerMenuVisible(page);
|
|
});
|
|
|
|
test('Manager menu → Model Manager → close → reopen', async ({ page }) => {
|
|
await openManagerMenu(page);
|
|
|
|
await clickMenuButton(page, 'Model Manager');
|
|
await page.waitForSelector('#cmm-manager-dialog', {
|
|
timeout: 15_000,
|
|
});
|
|
|
|
// Close the Model Manager dialog via its close button (p-dialog-close-button)
|
|
const mmMask = page.locator('.p-dialog-mask:has(#cmm-manager-dialog)');
|
|
const mmCloseBtn = mmMask.locator('button[aria-label="Close"], .p-dialog-close-button').first();
|
|
if (await mmCloseBtn.isVisible({ timeout: 2_000 }).catch(() => false)) {
|
|
await mmCloseBtn.click();
|
|
} else {
|
|
await page.keyboard.press('Escape');
|
|
}
|
|
await page.waitForTimeout(1_000);
|
|
|
|
// Reopen: need to open Manager menu first, then Model Manager
|
|
await openManagerMenu(page);
|
|
await clickMenuButton(page, 'Model Manager');
|
|
await page.waitForSelector('#cmm-manager-dialog', {
|
|
timeout: 15_000,
|
|
});
|
|
});
|
|
|
|
});
|