尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Prowler UI 运行时公共配置数据岛(Runtime Public-Config Data Island)E2E 测试实战解析
Prowler UI 运行时公共配置数据岛Runtime Public-Config Data IslandE2E 测试实战解析【免费下载链接】prowlerProwler is the world’s most widely used open-source cloud security platform that automates security and compliance across any cloud environment.项目地址: https://gitcode.com/GitHub_Trending/pr/prowler导读本文围绕 Prowler UINext.js 前端中一套名为「运行时公共配置数据岛」的端到端E2E测试套件展开系统讲解它如何验证通过注入在head中的惰性 JSON 数据岛把允许名单内的公开客户端配置Sentry、GTM、API 地址等在运行时安全地暴露给浏览器这一核心机制。读完本文你将掌握该数据岛从服务端读取、序列化注入、客户端读取到第三方集成按需初始化的完整链路理解 3 个关键 E2E 用例的设计意图并能直接运行或扩展这套测试。一、什么是运行时公共配置数据岛在 Prowler UI 中ui/tests/runtime-config/runtime-config.md明确定义了这套测试套件的核心概念Suite ID:RUNTIME-CONFIG-E2EFeature:通过注入到head中的惰性 JSON 数据岛inert JSON data island在运行时解析公开客户端配置Sentry、GTM、API base/docs URL 及保留键。每个第三方集成仅当其UI_*_ENABLED标志为true时才解析出对应值因此被禁用的集成对外暴露为null。这套机制解决的是同一个预构建镜像在不同部署环境下表现不同的问题Sentry 是否启用、DSN 是什么、GTM ID 是什么、API 地址是什么全部在运行时而非构建时从容器环境变量解析再通过一个typeapplication/json的script标签注入页面浏览器端模块加载时即可无竞态地读取。三个 E2E 用例构成该套件的骨架用例 ID优先级验证主题RUNTIME-CONFIG-E2E-001critical数据岛在客户端 bundle 之前渲染于head仅暴露允许名单形状RUNTIME-CONFIG-E2E-002high浏览器 Sentry 初始化使用的 DSN 与数据岛一致RUNTIME-CONFIG-E2E-003criticalSentry 与 GTM 未配置时第三方遥测零外发二、整体架构从环境变量到head数据岛要读懂这套测试先要理解数据岛的产生与消费链路。其核心代码分布在ui/lib、ui/components/runtime-config、ui/instrumentation-client.ts等文件中共分三环2.1 服务端只读允许名单 按请求强制运行时解析服务端入口是 ui/lib/runtime-config.ts 中的getRuntimePublicConfig()首先调用 Next.js 的await connection()强制每次请求都走运行时读取避免配置在构建时被快照build-snapshotted固化随后逐键读取环境变量构成一个严格允许名单allowlist形状RuntimePublicConfig任何不在名单内的环境变量都不会泄漏到客户端注释明确说明迁移中的键会回退到废弃的NEXT_PUBLIC_*旧名字见readEnv的 legacy 逻辑以保证存量部署平滑迁移。允许名单的完整键集合定义在 ui/lib/runtime-config.shared.tsexport interface RuntimePublicConfig { sentryDsn: string | null; sentryEnvironment: string | null; googleTagManagerId: string | null; apiBaseUrl: string | null; apiDocsUrl: string | null; posthogEnabled: boolean; posthogKey: string | null; posthogIngestionHost: string | null; posthogUiHost: string | null; reoDevClientId: string | null; // reserved cloudEnabled: boolean; cloudBillingEnabled: boolean; stripePublishableKey: string | null; // reserved stripePublishableKeyV2: string | null; // reserved }其中reoDevClientId、stripePublishableKey、stripePublishableKeyV2被标记为reserved保留键——当前客户端尚未消费但允许名单已经为后续功能预留了位置同时避免未来需要时再次扩展暴露面。2.2 渲染惰性 JSON 岛注入headui/components/runtime-config/runtime-public-config.tsx 是一个 async Server Component它把服务端拿到的配置序列化为script id{RUNTIME_CONFIG_SCRIPT_ID} typeapplication/json dangerouslySetInnerHTML{{ __html: serializeForScript(config) }} /要点惰性inerttypeapplication/json意味着浏览器不会把它当作脚本执行天然不受 CSPscript-src约束也不会产生任何副作用id 约定RUNTIME_CONFIG_SCRIPT_ID __PROWLER_RUNTIME_CONFIG__测试、客户端读取、渲染三处共用同一常量杜绝字符串漂移位置该组件被放进head参见 ui/app/(auth)/layout.tsx/layout.tsx) 第 53-55 行且两个根布局都会渲染它因此任何未认证路由都能读到数据岛测试便选择了无需会话的/sign-in页面。2.3 客户端一次读取 全空兜底客户端侧有两层读取ui/lib/runtime-config.shared.ts 的readRuntimeConfigIsland()读取并校验head岛通过pickConfig做逐键显式拷贝而非 spread——这样即使岛 JSON 中混入意外键也不会穿透到运行时配置ui/lib/get-runtime-config.client.ts 的getRuntimeConfigClient()带模块级缓存的读取器岛缺失/损坏时回退到EMPTY_RUNTIME_PUBLIC_CONFIG全空/全关让调用方可以把所有集成安全地视为已禁用。值得注意的是pickConfig的显式拷贝设计它按允许名单逐键提取是只暴露允许名单形状E2E-001 的验证点之二在源码层的实现保障。三、用例详解RUNTIME-CONFIG-E2E-001—— 数据岛在客户端 bundle 之前渲染用例目标这是整个套件中优先级为critical的用例验证的是 jsdom 单元测试无法证明的真实浏览器时序数据岛必须先于客户端 bundle 出现在head中。前置条件与流程前置条件UI 服务已启动且设置了UI_API_BASE_URL由 Playwright 的webServer提供见后文第五节。流程步骤导航到/sign-in未认证数据岛在每个路由都会渲染定位script#__PROWLER_RUNTIME_CONFIG__。预期结果数据岛存在typeapplication/json惰性位于head中数据岛出现在第一个有序非asyncbundlescript src之前——Next.js 在head中的asyncchunk 预加载不按顺序执行且不读取配置因此被排除可解析为 JSON恰好暴露允许名单内的键且apiBaseUrl为真值。源码侧验证逻辑对应实现位于 ui/tests/runtime-config/runtime-config-page.tsverifyIslandInHead()断言岛的type属性为application/json且其父节点标签为headverifyIslandPrecedesClientBundle()在页面上下文中枚举所有script找到岛的下标再找到第一个src存在且!async的 bundle 脚本下标断言岛下标更小。注释点明该有序 bundle 正是调用getRuntimeConfigClient()的客户端入口岛必须先于它存在测试最终用Object.keys(config).sort()与RUNTIME_CONFIG_KEYSruntime-config-page.ts 中硬编码的 14 个键与RuntimePublicConfig接口严格对齐比对确认恰好是允许名单形状无其他 env 泄漏同时断言config.apiBaseUrl为真值——UI_API_BASE_URL是测试运行必需的服务端运行时值。关键验证点head内、bundle 前的顺序保证jsdom 中不可证明仅暴露允许名单形状无其他环境变量泄漏。四、用例详解RUNTIME-CONFIG-E2E-002—— 浏览器 Sentry 初始化与数据岛 DSN 一致用例目标优先级high。该用例验证 Sentry 的浏览器客户端初始化时使用的 DSN 与数据岛携带的 DSN逐字节一致从而证明数据岛无竞态地喂给了Sentry.init。前置条件与流程前置条件UI 服务运行中。Sentry 可能启用UI_SENTRY_ENABLEDtrue且配置UI_SENTRY_DSN也可能关闭。流程步骤导航到/sign-in从数据岛读取sentryDsn读取浏览器 Sentry 客户端初始化所用的 DSN。预期结果若数据岛携带 DSN则浏览器 Sentry 客户端以完全一致的运行时 DSN初始化证明岛在模块加载期喂给了Sentry.init无竞态若数据岛无 DSN则 Sentry不初始化零外发——默认行为。源码侧验证逻辑数据岛的 DSN 究竟如何到达Sentry.init链路在 ui/instrumentation-client.tsNext.js Client Instrumentation运行在 React 水合之前const { sentryDsn, sentryEnvironment } getRuntimeConfigClient(); if (typeof window ! undefined sentryDsn) { const environment sentryEnvironment ?? production; const isDevelopment environment local; Sentry.init({ dsn: sentryDsn, environment, release: process.env.NEXT_PUBLIC_PROWLER_RELEASE_VERSION, debug: isDevelopment, tracesSampleRate: isDevelopment ? 1.0 : 0.5, profilesSampleRate: isDevelopment ? 1.0 : 0.5, integrations: [ Sentry.browserTracingIntegration({ enableLongTask: true, enableInp: true, }), ], ignoreErrors: [/* 浏览器扩展、网络错误、AbortError、NEXT_REDIRECT、ResizeObserver 等噪音 */], beforeSend(event, hint) { /* 打 API 错误标签 applySentryEventPolicy 过滤 */ }, }); }几个值得展开的实现细节未设 DSN ⇒ 永不调用Sentry.initif (typeof window ! undefined sentryDsn)是硬性门槛注释明确这是单个预构建镜像按部署启用/禁用 Sentry的关键未设 DSN 即零外发环境默认值environment默认production避免未设置UI_SENTRY_ENVIRONMENT时浏览器 SDK 进入 dev 模式调试日志、100% 采样、合成 dev 用户只有显式设置为local才启用 dev 行为采样率dev 环境 100% 采样全面测试生产 50%成本与可见性平衡release 来源浏览器只能读取构建期的公开版本号NEXT_PUBLIC_PROWLER_RELEASE_VERSIONSENTRY_RELEASE非公开、永不进入客户端服务端/edge SDK 另读SENTRY_RELEASE保持一致运行时机Client Instrumentation 在 React 水合前执行因此browserTracing的性能观察器尽早挂载global-error.tsx的错误边界也被覆盖。测试侧则通过 runtime-config-page.ts 的sentryInitializedDsn()从window.__SENTRY__内部结构中取出已初始化客户端的getOptions().dsn再与数据岛值比对——无论当前部署是否配置 DSN这个双向断言有 DSN ⇒ 一致无 DSN ⇒ 未初始化都成立。关键验证点运行时 DSN 经由数据岛到达模块加载期的Sentry.initDSN 未设置 ⇒ 不初始化 Sentry隐私保证。五、用例详解RUNTIME-CONFIG-E2E-003—— Sentry 与 GTM 未配置时零第三方遥测用例目标优先级critical。该用例验证企业版默认配置Enterprise default的隐私承诺Sentry 与 Google Tag Manager 都未启用时页面不得向任何第三方遥测主机发出任何请求。前置条件与流程前置条件UI 服务运行且 Sentry 与 Google Tag Manager 均被禁用——UI_SENTRY_ENABLED与UI_GOOGLE_TAG_MANAGER_ENABLED标志未设置企业版默认。若任一集成已启用并配置测试自动跳过。流程步骤导航到/sign-in并读取数据岛配置在记录发往googletagmanager.com、google-analytics.com、sentry.io请求的同时重载页面检查 DOM 中是否存在 Google Tag Manager 脚本。预期结果不向任何 Google 或 Sentry 主机发出请求GoogleTagManager组件不渲染无gtm.js脚本。源码侧验证逻辑测试实现ui/tests/runtime-config/runtime-config.spec.ts先用test.skip(Boolean(config?.sentryDsn) || Boolean(config?.googleTagManagerId), ...)守卫当前部署若真的配置了任一集成用例直接跳过因为此时断言零外发没有意义。然后thirdPartyRequestsOnReload()在页面request事件上挂监听器重载后收集所有 URL 包含上述三个主机片段之一的请求断言为空数组——这是零外发的直接证据verifyGoogleTagManagerNotRendered()断言script[src*googletagmanager.com]数量为 0——这是组件未挂载的 DOM 证据。GTM 的条件渲染逻辑在 ui/app/(auth)/layout.tsx/layout.tsx)const gtmId readGatedEnv( UI_GOOGLE_TAG_MANAGER_ENABLED, UI_GOOGLE_TAG_MANAGER_ID, NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID, ); ... {gtmId GoogleTagManager gtmId{gtmId} /}next/third-parties/google的GoogleTagManager仅在gtmId为真值时渲染。测试文档特别强调空/未设置的 GTM id ⇒ 组件不挂载空的 id 并不是惰性的——即空字符串不等于未配置必须让解析结果落到null才算真正禁用这也是为什么条件渲染写的是{gtmId ...}而非{gtmId ! null ...}之外的多余逻辑。从源码看readGatedEnv在标志未开启时返回null空字符串不可能进入渲染条件。关键验证点企业版默认对任何第三方零错误/分析遥测外发空/未设置的 GTM id ⇒ 组件不挂载空 id 并不惰性。六、数据岛背后的门控机制UI_*_ENABLED与 legacy 兼容理解 002/003 两个用例绕不开 ui/lib/integrations.ts 中的门控gating设计——它是仅当UI_*_ENABLED为true时才解析出值否则为null这条规则的唯一事实来源。export const GATED_INTEGRATIONS: Recordstring, GatedIntegration { sentry: { name: Sentry, enableKey: UI_SENTRY_ENABLED, required: [{ key: UI_SENTRY_DSN, legacy: NEXT_PUBLIC_SENTRY_DSN }], optional: [{ key: UI_SENTRY_ENVIRONMENT, legacy: NEXT_PUBLIC_SENTRY_ENVIRONMENT }], }, googleTagManager: { name: Google Tag Manager, enableKey: UI_GOOGLE_TAG_MANAGER_ENABLED, required: [{ key: UI_GOOGLE_TAG_MANAGER_ID, legacy: NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID }], optional: [], }, posthog: { name: PostHog, enableKey: UI_POSTHOG_ENABLED, required: [{ key: UI_POSTHOG_KEY, legacy: POSTHOG_KEY }, { key: UI_POSTHOG_HOST, legacy: POSTHOG_HOST }], optional: [{ key: UI_POSTHOG_UI_HOST }], }, };三条关键规则新UI_*名字只在标志为true时生效显式选择加入默认关闭 ⇒ 无第三方外发legacy 名字保持向后兼容旧环境变量如NEXT_PUBLIC_SENTRY_DSN只要存在即激活集成与引入启用标志前的行为一致存量部署无需改动即可继续工作启动期快速失败fail-fastassertGatedIntegrations()在 ui/lib/integrations.ts 中保证——标志为true时每个必需变量必须能通过新名字或 legacy 回退解析到值否则启动即抛错legacy 半配置部分旧变量已设置同样要求补齐全套 legacy 必需变量。readGatedEnv的优先级逻辑是legacy 值先读出来备用标志为真时新名字优先、回退 legacy标志为假时只返回 legacy 值这正是禁用集成暴露为null与存量 legacy 部署不破坏两者兼得的实现。isGatedIntegrationEnabled则是标志为真或legacy 配置完整的或逻辑posthogEnabled布尔即由此得出。这张统一的门控映射表同时驱动启动校验ui/lib/shared/env.ts和运行时配置门控ui/lib/runtime-config.ts注释明确这是为了让两者不可能漂移。七、测试基础设施Playwright 配置、页面对象与运行方式7.1 项目注册与webServerE2E 套件在 ui/playwright.config.ts 中注册为独立 project{ name: runtime-config, use: { ...devices[Desktop Chrome] }, testMatch: /runtime-config\/.*\.spec\.ts/, },该 project 无需任何 auth setup 依赖测试运行在未认证路由/sign-in上注释写明Tests run unauthenticated (no auth setup dependency)。前置条件中的UI_API_BASE_URL由webServer注入webServer: { command: process.env.CI ? pnpm run start : pnpm run dev, url: http://localhost:3000, reuseExistingServer: !process.env.CI, timeout: 120 * 1000, env: { UI_API_BASE_URL: process.env.UI_API_BASE_URL || http://localhost:8080/api/v1, AUTH_URL: process.env.AUTH_URL || http://localhost:3000, AUTH_SECRET: process.env.AUTH_SECRET || fallback-ci-secret-for-testing, ... }, },本地开发时以pnpm run dev启动、CI 中以pnpm run start启动reuseExistingServer允许本地复用已在 3000 端口运行的实例。7.2 页面对象模式ui/tests/runtime-config/runtime-config-page.ts 封装了所有可复用断言islandLocatorpage.locator(script#__PROWLER_RUNTIME_CONFIG__)readConfig()在页面上下文中解析岛 JSON缺失/损坏返回nullsentryInitializedDsn()从window.__SENTRY__提取已初始化客户端的 DSNverifyIslandInHead()/verifyIslandPrecedesClientBundle()DOM 位置与顺序断言thirdPartyRequestsOnReload()重载期间收集匹配主机片段的请求 URLverifyGoogleTagManagerNotRendered()断言无googletagmanager.com脚本。测试主体 ui/tests/runtime-config/runtime-config.spec.ts 仅 3 个用例每个用例的 Playwright tag 与文档中的用例 ID 一一对应如RUNTIME-CONFIG-E2E-001便于在 CI 报告与文档之间双向检索。7.3 运行方式在ui/目录下可针对该项目单独运行pnpm exec playwright test --projectruntime-config或带标签过滤pnpm exec playwright test --grep runtime-config三个用例中 001 与 003 为critical、002 为highCI 中process.env.CI为真重试 2 次、串行执行workers: 1本地则并行且不重试。八、设计要点总结与可借鉴之处从这套测试与其背后的实现可以提炼出几条通用工程经验运行时配置与构建时配置分离connection()强制按请求读环境变量使同一个镜像在不同部署Sentry 开/关、不同 API 地址下行为不同这是多租户/企业部署的刚需允许名单 显式逐键拷贝pickConfig用逐键拷贝而非 spread从实现层面杜绝意外键穿透测试再用键集合比对双向锁死暴露面惰性 JSON 岛规避安全与时序问题typeapplication/json不受 CSPscript-src约束、不执行任何代码置于 bundle 前保证模块加载期消费者Sentry init无竞态读取门控标志与 legacy 兼容并存新部署默认零第三方外发隐私优先存量部署靠 legacy 变量无缝续用启动期 fail-fast 兜底错误配置E2E 测试覆盖 jsdom 无法证明的事实文档注释明确指出head内 bundle 前的顺序保证在 jsdom 中不可证明这正是该套件存在的意义——用真实浏览器验证时序、网络与 DOM 渲染网络请求级别的零外发断言也是单元测试难以替代的。若想进一步深入推荐按以下顺序阅读源码数据岛类型与读取 ui/lib/runtime-config.shared.ts → 服务端组装 ui/lib/runtime-config.ts → 门控与 legacy 兼容 ui/lib/integrations.ts → 渲染组件 ui/components/runtime-config/runtime-public-config.tsx → 客户端消费 ui/lib/get-runtime-config.client.ts 与 ui/instrumentation-client.ts → 测试三件套 runtime-config.md、runtime-config.spec.ts、runtime-config-page.ts。【免费下载链接】prowlerProwler is the world’s most widely used open-source cloud security platform that automates security and compliance across any cloud environment.项目地址: https://gitcode.com/GitHub_Trending/pr/prowler创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

Mealie iOS 快捷指令(Apple Shortcut)实战:从网页链接与照片一键导入菜谱

Mealie iOS 快捷指令(Apple Shortcut)实战:从网页链接与照片一键导入菜谱

Mealie iOS 快捷指令(Apple Shortcut)实战:从网页链接与照片一键导入菜谱 【免费下载链接】mealie Mealie is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for…

📅 2026/9/15 15:00:07
从35个Python源码文件拆解自动化测试框架设计

从35个Python源码文件拆解自动化测试框架设计

简介:这是一份面向自动化测试初学者与测试开发工程师的Python自动化测试框架设计源码包,围绕测试用例组织、流程控制、UI操作与数据管理搭建出清晰可扩展的项目骨架,适合用于学习或直接裁剪落地。压缩包共57个文件,包含36个Python…

📅 2026/9/15 14:55:06
speech-to-speech 如何用 Parakeet TDT 开启实时流式转录?

speech-to-speech 如何用 Parakeet TDT 开启实时流式转录?

speech-to-speech 如何用 Parakeet TDT 开启实时流式转录? 【免费下载链接】speech-to-speech Build voice agents with open-source models 项目地址: https://gitcode.com/GitHub_Trending/sp/speech-to-speech speech-to-speech 是一条 VAD -> STT -&g…

📅 2026/9/15 14:55:06
MORE NEWS

更多资讯

📰

使用 Nx 构建 Bitwarden Monorepo:命令速查、项目配置与缓存机制实战指南

使用 Nx 构建 Bitwarden Monorepo:命令速查、项目配置与缓存机制实战指南 【免费下载链接】clients Bitwarden client apps (web, browser extension, desktop, and cli). 项目地址: https://gitcode.com/GitHub_Trending/cl/clients 本指南以 Bitwarden 客户…

📰

Web设计源码仓库建设方法论:结构可追溯、样式可继承、交付可验证

简介:这是一份面向Web前端开发者、UI/UX设计师及开源项目贡献者的综合性设计资源库,聚焦于开放协作式Web开发实践,解决设计素材复用、代码参考与项目结构学习等实际需求。压缩包共187个文件,总计98.27MB,涵盖56个文本文…

📰

Hindsight Obsidian 集成演进全览:从插件首发到无头 CLI 同步引擎

Hindsight Obsidian 集成演进全览:从插件首发到无头 CLI 同步引擎 【免费下载链接】hindsight Hindsight: Agent Memory That Learns 项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight Hindsight 的 Obsidian 插件(npm 包 ve…

📰

原生JavaScript Canvas粒子系统:飘沙与烟雾特效实现

简介:这是一份面向前端开发初学者与进阶者的动态视觉特效实战资源,聚焦于使用原生JavaScript与jQuery实现高表现力的飘沙与粒子烟雾动画效果,适用于网页背景、活动页开场、交互式广告等需要强视觉冲击力的场景。资源包共5个文件,包…

📰

协同过滤电影推荐系统:从算法原理到前后端分离工程实践

简介:运用Python与协同过滤算法构建的电影推荐系统,采用Vue实现前后端分离,并集成Django与MySQL,是一套面向计算机相关专业学生、适用于毕业设计与推荐算法入门实践的完整可运行项目。压缩包共688个文件,约13.01MB&…

📰

Garden Skills CI完整拆解:validate-skills与release-skill双工作流设计指南

Garden Skills CI完整拆解:validate-skills与release-skill双工作流设计指南 【免费下载链接】garden-skills ConardLis open-source Skills collection, featuring web design, knowledge retrieval, image generation, and more. 项目地址: https://gitcode.com…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬