task-86(店铺中心): 实现密钥敏感字段掩码
新增 secret-mask.ts:店铺密钥令牌等凭证明文默认掩码展示,主动点击后按可注入 时间短时揭示、超时清空;明文不写入全局状态/持久化存储/地址栏/日志。 TDD: task-86.test.ts 8 用例先 RED 后 GREEN。
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user