mcpField
V
verify-login-logout

verify-login-logout

github.com/reticlehq测试与质量1.3k49 次安装今天更新
代码仓库

关于 verify-login-logout

使用 Reticle 验证 Web 应用登录、登出及会话过期行为的 skill。它要求同时检查界面、路由和网络证据,并对无法验证的步骤报告未知或受阻。

主要功能

  1. 01验证登录请求与登录后状态
  2. 02登出后检查受保护页面访问
  3. 03模拟会话过期并检查处理结果
  4. 04区分通过、未知与受阻状态
  5. 05清除模拟并避免泄露凭据

适用场景

  1. 01检查登录是否获服务器接受
  2. 02验证登出后服务器拒绝旧会话
  3. 03测试会话过期时是否返回登录页

MCP 相关

依赖 Reticle 工具操作并检查运行中的 Web 应用,包括页面、路由、网络请求和网络模拟。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/reticlehq/reticle --skill verify-login-logout

安装后在 Claude Code 中输入/verify-login-logout确认已加载。

详细介绍

屏幕上显示已登录,不代表真的已登录

登录表单提交后显示仪表盘,只能证明页面发生了变化。它不能证明服务器接受了凭据,也不能证明退出登录会结束会话,或会话过期的用户看到的是登录页面,而不是空白屏幕和一连串 401。

Reticle 可以在运行中的应用里检查这三点。尚未安装?运行 RETICLE_INSTALL_SOURCE=npx_skill npx @reticlehq/server@latest init,然后使用 install-and-verify 技能。

开始之前

  • 使用项目的测试账号。 从种子数据、测试夹具或 .env.example 中获取凭据,或询问用户。绝不要输入真实用户的密码。
  • 确认会话可以观测: reticle_session({ action: "list" })。隐藏或受限流的标签页可能会接受点击,却不渲染任何内容。如果会话不健康,请将运行结果报告为受阻,而不是通过。
  • 步骤 3 需要由 Reticle 控制的浏览器。 强制会话过期需要使用 reticle_network_mock,它适用于租用的标签页(reticle_run({ tool: "reticle_lease", args: { action: "acquire", url } }))或受驱动的标签页,但不适用于常驻 SDK。若没有这样的浏览器,请执行步骤 1 和 2,并将步骤 3 报告为未知。

1. 登录,并说明登录后应前往何处

使用 reticle_look({ action: "page", sessionId, mode: "interactive" }) 获取表单的 refs,用 reticle_act 填写字段,然后提交,并在点击前说明预期结果:

reticle_act_and_wait({ sessionId, ref: "<sign-in button>", action: "click", until: { kind: "allOf", predicates: [
  { kind: "net", urlContains: "/api/login", ok: true },
  { kind: "route", pathname: "/dashboard" },
  { kind: "element", query: { role: "button", name: "Account" } },
]}})

三项检查都要做,因为每一项单独来看都可能有误:客户端可能在服务器响应前就切换路由,用户菜单也可能从过期缓存中渲染。全程使用应用的真实来源、端点、落地路由和已登录标记:这里展示的只是示例。

reticle_run({ tool: "reticle_storage", sessionId, args: {} }) 会显示应用存储的内容。敏感键会被脱敏,httpOnly Cookie 按设计不会对页面可见,因此这些信息只能证明会话存放在哪里,不能作为判定依据。

2. 退出登录,然后尝试返回

reticle_act_and_wait({ sessionId, ref: "<sign-out control>", action: "click", until: { kind: "allOf", predicates: [
  { kind: "route", contains: "/login" },
  { kind: "element", query: { role: "button", name: "Account" }, absent: true },
]}})

进入登录页面只能证明 UI 发生了变化,不能证明会话已结束。直接导航到受保护页面,并使用应用的真实来源和路由,证明访问遭到拒绝:

reticle_navigate({ sessionId, url: "<the app's origin>/dashboard" })
reticle_assert({ sessionId, predicate: { kind: "allOf", predicates: [
  { kind: "route", contains: "/login" },
  { kind: "text", contains: "Sign in" },
  { kind: "net", urlContains: "/api/me", status: 401 },
]}})

net 子句才能证明会话已由服务器结束。客户端保护机制可能会根据已清除的标记跳转到 /login,而服务器仍接受旧会话;这样一来,路由和文本检查都会通过。页面用于确认当前登录用户的请求现在必须遭到拒绝。如果页面没有发出此类请求,请如实说明,并将服务器端验证结果报告为未知。

退出登录后仍能渲染受保护页面,是这项技能可能发现的最重要问题。即使其他检查都通过,也要报告这一发现。

3. 让会话过期,并验证应用会重新要求登录

再次登录(步骤 1)。然后让服务器将会话视为已过期,方法是让应用用于检查会话的调用失败:

reticle_run({ tool: "reticle_network_mock", sessionId, args: {
  mocks: [{ urlContains: "/api/me", status: 401 }],
}})

重新加载受保护页面,并说明预期结果:

reticle_navigate({ sessionId, url: "<the app's origin>/dashboard" })
reticle_assert({ sessionId, timeout_ms: 5000, predicate: { kind: "allOf", predicates: [
  { kind: "net", urlContains: "/api/me", status: 401 },
  { kind: "route", contains: "/login" },
  { kind: "console", level: "error", absent: true },
]}})

net 子句能证明应用响应的是模拟的 401。如果应用从未调用模拟的端点,那么无论如何都会发生的跳转只是巧合,并非对过期会话的处理;因此,在模拟它实际调用的接口之前,结论应为未知。控制台子句也很重要:如果应用通过抛出未捕获错误并显示空白页面来“处理”会话过期,那就不算处理了。完成后,使用 { clear: true } 清除模拟。

如实报告

只有 verified: "yes" 才算通过。步骤 3 是替代测试:Reticle 无法删除 httpOnly Cookie 或修改存储,因此通过模拟 401 来强制触发过期。请说明你模拟了哪个端点。交还前清除所有模拟并退出登录;绝不要报告你看到的任何密码、令牌或 Cookie 值。


能力参考:curl https://docs.reticle.sh/capabilities.md。其他所有内容:curl https://docs.reticle.sh/llms.txt。

评论

支持 Markdown