task-86(店铺中心): 实现密钥敏感字段掩码

新增 secret-mask.ts:店铺密钥令牌等凭证明文默认掩码展示,主动点击后按可注入
时间短时揭示、超时清空;明文不写入全局状态/持久化存储/地址栏/日志。

TDD: task-86.test.ts 8 用例先 RED 后 GREEN。
This commit is contained in:
2026-09-05 16:40:39 +08:00
parent be6a17d4bd
commit 9060464afd
2 changed files with 128 additions and 0 deletions
@@ -0,0 +1,54 @@
/** 店铺中心敏感字段掩码与短时揭示(任务 86):店铺密钥令牌等凭证明文默认以掩码展示,
* 用户主动点击后才在内存中短时揭示;不写入全局状态、持久化存储、地址栏或日志。纯逻辑,可注入时间便于测试。 */
/** 掩码占位符(默认固定长度,与旧页面 ****** 一致)。 */
export const SECRET_PLACEHOLDER = '******'
/** 未指定时长时的默认揭示窗口(毫秒)。 */
export const DEFAULT_REVEAL_MS = 5_000
export interface SecretRevealState {
/** 当前被揭示的记录行 id;无揭示时为 null。 */
keyId: number | null
/** 揭示中的明文,仅存内存、离开即清空。 */
token?: string
/** 揭示过期时间点(毫秒)。 */
expiresAt?: number
}
/** 空揭示状态(首屏/页面卸载默认)。 */
export function createSecretRevealState(): SecretRevealState {
return { keyId: null, token: undefined, expiresAt: undefined }
}
export function secretPlaceholder(): string {
return SECRET_PLACEHOLDER
}
/** 任何秘密展示一律落为掩码占位符;真正明文只经揭示会话在内存中出现。 */
export function maskSensitive(_value: unknown): string {
return SECRET_PLACEHOLDER
}
/** 开始揭示指定行的秘密:记录明文与过期点(默认短时窗口);返回新状态,不改入参。 */
export function revealSecret(
state: SecretRevealState,
keyId: number,
token: string,
now: number,
expiresAt?: number,
): SecretRevealState {
return {
keyId,
token: typeof token === 'string' ? token : '',
expiresAt: expiresAt === undefined ? now + DEFAULT_REVEAL_MS : expiresAt,
}
}
/** 到达过期点即清空揭示明文,返回新状态;未过期原样返回。 */
export function expireReveal(state: SecretRevealState, now: number): SecretRevealState {
if (state.expiresAt !== undefined && state.expiresAt !== null && now >= state.expiresAt) {
return createSecretRevealState()
}
return state
}