Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export function App({ port }: Props) {
const res: Result<unknown> = 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}`);
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/coreport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
25 changes: 22 additions & 3 deletions crates/archmaker-core/tests/negative_capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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::<String>();
for evil in [
"pacstrap",
"sidecar",
Expand All @@ -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",
Expand Down
15 changes: 11 additions & 4 deletions tools/check_negative_capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading