门禁加固与 Windows 可跑性

- 新增 scripts/check-generated-bindings.mjs 并接入 lint 与 ai-game-creator-shell:check:rust:crates:重跑 export_bindings 前后逐字节比对共享契约绑定。
- check:native-shells 的 AGC 边界改为负向门禁:渲染源码不得出现 fetch(/XHR/EventSource/sendBeacon/WebSocket/plugin-http,客户端 capability 不得授予 http:default。
- check-pingora-gateway-smoke 在 Windows 上找 pingora-gateway.exe,并修掉「API 并发保护」先关客户端连接再读响应的竞态。
- mobile-shell 三个 smoke 改成固定命令串启动 npm(绕开 npm.cmd EINVAL 与 DEP0190),desktop-shell 的 stage-release-binary 改用 fileURLToPath 修掉 F:\F:\… 路径拼接。
- vitest.config.ts 白名单补回现役根用例;三份 nginx 模板的 SPA allowlist 补回 /components 与 /design-system。
This commit is contained in:
kdletters
2026-09-28 14:15:50 +08:00
parent e7309ad71b
commit 01394ed238
12 changed files with 169 additions and 56 deletions
+43 -26
View File
@@ -2564,21 +2564,47 @@ function assertAiGameCreatorShellUserDevBoundary() {
!aiGameCreatorClientHttpSource.includes(
"'https://dev.genarrative.world'",
) ||
!aiGameCreatorClientHttpSource.includes("transport: 'tauri-http'") ||
!aiGameCreatorClientHttpSource.includes('target.origin !==')
!aiGameCreatorClientHttpSource.includes(
"'https://www.genarrative.world'",
) ||
/\bfetch\s*\(/u.test(aiGameCreatorClientHttpSource) ||
aiGameCreatorClientHttpSource.includes("transport: 'tauri-http'")
) {
throw new Error(
'AI game creator release dev API transport boundary drifted',
'AI game creator clientHttp must only resolve the server selection, not carry a transport',
);
}
// 渲染层是离线前端:平台接口、OSS 直传、Provider 与更新清单的网络 IO 全部在 Rust 侧
// (`src-tauri/src/account_api.rs` / `auth_session.rs` / `platform_asset_upload.rs` /
// `game_distribution_publish.rs` 等 reqwest facade)。这里对整棵渲染源码加网络原语与
// Tauri HTTP guest 的负向门禁,避免以后再长出第二条传输路径。
const rendererNetworkPrimitivePatterns = [
['fetch(', /\bfetch\s*\(/u],
['XMLHttpRequest', /\bXMLHttpRequest\b/u],
['EventSource', /\bEventSource\b/u],
['sendBeacon(', /\bsendBeacon\s*\(/u],
['new WebSocket(', /\bnew\s+WebSocket\s*\(/u],
['@tauri-apps/plugin-http', /@tauri-apps\/plugin-http/u],
];
for (const rendererFilePath of collectFiles(
'apps/ai-game-creator-shell/src',
(entryPath) => /\.(ts|tsx)$/u.test(entryPath),
)) {
const rendererSource = fs.readFileSync(rendererFilePath, 'utf8');
for (const [label, pattern] of rendererNetworkPrimitivePatterns) {
if (pattern.test(rendererSource)) {
throw new Error(
`AI game creator renderer must stay offline, but ${normalizeScannedFilePath(rendererFilePath)} contains ${label}`,
);
}
}
}
if (
!aiGameCreatorCargoManifestSource.includes(
'tauri-plugin-http = { version = "2.5.9", default-features = false, features = ["charset", "cookies", "http2", "rustls-tls"] }',
) ||
!aiGameCreatorShellTauriSource.includes('tauri_plugin_http::init()')
aiGameCreatorCargoManifestSource.includes('tauri-plugin-http') ||
aiGameCreatorShellTauriSource.includes('tauri_plugin_http::init()')
) {
throw new Error(
'AI game creator release must register the native HTTP plugin without the OS automatic system-proxy feature',
'AI game creator shell must not register a renderer HTTP plugin; native IO goes through the Rust reqwest facades',
);
}
if (
@@ -2588,27 +2614,18 @@ function assertAiGameCreatorShellUserDevBoundary() {
'AI game creator monorepo build must dedupe React runtime packages',
);
}
const httpPermission = (aiGameCreatorMainCapability.permissions ?? []).find(
(permission) =>
typeof permission === 'object' &&
permission?.identifier === 'http:default',
);
if (
!httpPermission ||
JSON.stringify(httpPermission.allow ?? []) !==
JSON.stringify([
{ url: 'https://dev.genarrative.world/api/*' },
{ url: 'https://www.genarrative.world/api/*' },
{ url: 'https://*/api/*' },
{ url: 'http://localhost:*/*' },
{ url: 'http://127.0.0.1:*/*' },
{ url: 'https://*.aliyuncs.com/*' },
])
(aiGameCreatorMainCapability.permissions ?? []).some(
(permission) =>
permission === 'http:default' ||
(typeof permission === 'object' &&
permission?.identifier === 'http:default'),
)
) {
throw new Error(
// 更新清单与安装包下载由 tauri-plugin-updater 在原生侧完成;
// 封面与截图仍通过 webview 的 http 插件向凭证指定的 OSS 地址直传。
'AI game creator native HTTP scope must match the release, dev, custom HTTPS, loopback API, and OSS media upload boundary',
// 客户端窗口只保留剪贴板、图片、资源关闭、opener、updater 与原生对话框权限;
// 更新清单下载由 tauri-plugin-updater 在原生侧完成,素材直传也不再经渲染层。
'AI game creator client capability must not grant the renderer HTTP permission',
);
}