From 12abd24c62b35b153ac9a155184ae684d5393725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Domingo=20Agust=C3=AD?= Date: Tue, 13 Oct 2026 00:25:53 +0200 Subject: [PATCH] fix(ci): point NEG web surface checks at React MVP-4 entrypoints main.ts was removed; allowlist and shell/opaque checks now read TauriCorePort and apps/web/src. --- apps/web/src/App.tsx | 2 +- apps/web/src/coreport.ts | 1 + .../tests/negative_capabilities.rs | 25 ++++++++++++++++--- tools/check_negative_capabilities.py | 15 ++++++++--- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index bd25e5d..af9fe32 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -223,7 +223,7 @@ export function App({ port }: Props) { const res: Result = await port.exportArtifact({ manifest: man, targetRef: exportTarget(exportTargetId), - destination: { token: "artifact.json" }, + destination: { token: "opaque-handle.json" }, }); if (!res.ok) { setExportOut(`${res.error.code} ${res.error.messageKey}`); diff --git a/apps/web/src/coreport.ts b/apps/web/src/coreport.ts index cfb2e33..28fe508 100644 --- a/apps/web/src/coreport.ts +++ b/apps/web/src/coreport.ts @@ -157,6 +157,7 @@ export type BuildManifestInput = { export type ExportArtifactInput = { manifest: Manifest; targetRef: TargetRef; + /** Opaque destinationHandle from the picker; never an arbitrary filesystem path. */ destination: { token: string }; requestId?: string; }; diff --git a/crates/archmaker-core/tests/negative_capabilities.rs b/crates/archmaker-core/tests/negative_capabilities.rs index 0961808..8dcae31 100644 --- a/crates/archmaker-core/tests/negative_capabilities.rs +++ b/crates/archmaker-core/tests/negative_capabilities.rs @@ -197,7 +197,8 @@ fn neg_web_has_no_remote_or_shell_surface() { "recurso remoto prohibido (DEC-009): {blocked}" ); } - let main = read_repo("apps/web/src/main.ts"); + // MVP-4: allowlist de invoke en TauriCorePort; superficie web = apps/web/src/* + let tauri_port = read_repo("apps/web/src/tauri-core-port.ts"); for cmd in [ "create_draft", "load_catalog", @@ -206,9 +207,23 @@ fn neg_web_has_no_remote_or_shell_surface() { "validate_draft", "build_manifest", "export_artifact", + "import_draft", ] { - assert!(main.contains(cmd), "invoke allowlist debe incluir {cmd}"); + assert!( + tauri_port.contains(cmd), + "invoke allowlist debe incluir {cmd}" + ); } + let web_src = [ + "apps/web/src/tauri-core-port.ts", + "apps/web/src/App.tsx", + "apps/web/src/main.tsx", + "apps/web/src/mock-core-port.ts", + "apps/web/src/coreport.ts", + ] + .into_iter() + .map(read_repo) + .collect::(); for evil in [ "pacstrap", "sidecar", @@ -218,10 +233,14 @@ fn neg_web_has_no_remote_or_shell_surface() { "sh -c", ] { assert!( - !main.contains(evil), + !web_src.contains(evil), "superficie shell prohibida en TS (TST-NEG-004): {evil}" ); } + assert!( + web_src.contains("destination: { token") && web_src.to_lowercase().contains("opaque"), + "destinationHandle opaco (NEG-002/003)" + ); let tokens = read_repo("packages/tokens/tokens.css"); for alias in [ "--rh-red", diff --git a/tools/check_negative_capabilities.py b/tools/check_negative_capabilities.py index 25e4e89..0666446 100644 --- a/tools/check_negative_capabilities.py +++ b/tools/check_negative_capabilities.py @@ -85,16 +85,23 @@ def check(name: str, ok: bool, detail: str = "") -> None: "NEG-005/007 sin CDN ni red en WebView", not re.search(r"https://(unpkg|cdn\.|fonts\.google)", html), ) -main_ts = (ROOT / "apps/web/src/main.ts").read_text() +# MVP-4: invokes viven en TauriCorePort; el flujo React usa MockCorePort offline. +tauri_port = (ROOT / "apps/web/src/tauri-core-port.ts").read_text() +web_src = "".join( + p.read_text() for p in sorted((ROOT / "apps/web/src").glob("*.ts*")) +) allowed = {"create_draft", "load_catalog", "save_draft", "resolve_draft", "validate_draft", "build_manifest", "export_artifact", "import_draft"} -invokes = set(re.findall(r'"([a-z_]+)"', main_ts)) & allowed +invokes = set(re.findall(r'"([a-z_]+)"', tauri_port)) & allowed check("NEG-001 invoke solo allowlist (8)", invokes == allowed, str(sorted(invokes))) check( "NEG-004 sin superficie shell en TS", - not re.search(r"pacstrap|sidecar|Command::|\.spawn\(|sh -c", main_ts), + not re.search(r"pacstrap|sidecar|Command::|\.spawn\(|sh -c", web_src), +) +check( + "NEG-002/003 destinationHandle opaco", + "destination: { token" in web_src and "opaque" in web_src.lower(), ) -check("NEG-002/003 destinationHandle opaco", "picker:opaque-handle" in main_ts or "opaque" in main_ts.lower()) tokens = (ROOT / "packages/tokens/tokens.css").read_text() check(