Skip to content

[finding] AuthInvitation.status 声明成开放 string,而它自己的文档注释只枚举四个成员 —— 未知状态会把 wire 原值当界面文案渲染进邀请徽章 #3879

Description

@yinlianghui

#3846(marketplaceApi.tsruntime 声明成开放 string)同一类:消费方比契约宽松。#3546 切片七枚举 organization.invitations.status. 前缀家族时量出来的。

生产方自己就写了枚举,只是写在注释里

packages/auth/src/types.ts:404
export interface AuthInvitation {
  …
  /** Status: 'pending' | 'accepted' | 'rejected' | 'canceled' */
  status: string;      ← 类型是开放 string
}

createAuthClient.tslistInvitations 直接 as AuthInvitation[],没有运行时校验,所以后端返回什么都进得来。

由此长出的两处宽松

packages/app-shell/src/console/organizations/manage/InvitationsPage.tsx:

  1. 徽章配色default: 兜底 —— 未知状态渲染成 secondary,不报错:
    function statusBadgeVariant(status: string): 'outline' | 'default' | 'destructive' | 'secondary' {
      switch (status) { case 'pending':  default: return 'secondary'; }
    }
  2. 徽章文案是模板 key,defaultValue 取 wire 原值:
    {t(`organization.invitations.status.${inv.status}`, { defaultValue: inv.status })}
    258 个 t() 调用点引用的 key 在任何语言包里都不存在(#3530 守卫首跑实测),其中 8 处直接把 raw key 渲染给用户 #3546 切片七按同文件的封闭联合 type StatusFilter = 'all' | 'pending' | 'accepted' | 'rejected' | 'canceled' 枚举了五个成员并回填十包,并在测试里钉住"key 集合 === StatusFilter",所以第五个后端状态一出现,那条断言会红 —— 这是好事,它逼人正面处理。但在门禁红之前,线上行为是把 wire 字符串(英文、小写、CSS 首字母大写后)当界面文案给用户看,十种语言都是。

为什么现在算 finding 而不是 bug

今天后端的取值域就是这四个,所以没有用户能触发。它是契约方向的问题:枚举写在注释里而不是类型里,于是消费侧只能宽松。

建议修法

把注释里的枚举提到类型里:

export type AuthInvitationStatus = 'pending' | 'accepted' | 'rejected' | 'canceled';
export interface AuthInvitation { ; status: AuthInvitationStatus; }

再让 listInvitations 的边界处对未知值响亮失败或显式降级,而不是让 statusBadgeVariantdefault:defaultValue: inv.status 各自悄悄兜住。这正是 AGENTS.md 的 contract-first 取向:声明即强制,宽松的消费方是 AI 生成的元数据错误藏身的地方。

⚠️#3546 切片七同触 InvitationsPage.tsx 的读取面(切片七零组件改动,只读源码做断言),但改的文件不同(packages/auth/src/types.ts),无冲突。

关联:#3846(同类,marketplace runtime)、#3790 / #3447(同类"未知 status 无兜底/悄悄兜底")、#3546(切片七,发现路径)。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions