fix(deeplink): import usage scripts disabled and show their code

An imported usage script is JavaScript that runs whenever usage is
queried. Two things made it possible to acquire one without seeing it:

  - `usage_enabled.unwrap_or(!code.is_empty())` treated the presence of
    code as a decision to run it, so a link that simply carried a script
    got it enabled
  - the confirmation dialog rendered only an enabled/disabled badge; the
    script body was never displayed

Default to disabled. Enabling now requires `usageEnabled=true` in the
link -- which is the link author's request, not the user's consent. The
consent is the user pressing Import after seeing the full script body
and the badge, which is why both displays are load-bearing rather than
decorative.

The badge predicate moves from `!== false` to `=== true` to match the
new backend default. Left alone it would have started rendering "did not
say" as a green "Enabled" -- more optimistic than what would actually
happen.

Extracts the payload decode into `decodeDeeplinkPayload`, which falls
back to the raw string when decoding fails or yields empty. A dialog
whose job is to show what is about to be written must not let a payload
vanish just because it is malformed; empty reads as "there is no
script", which is exactly the wrong impression.
This commit is contained in:
Jason
2026-07-29 00:04:20 +08:00
parent 19bf236e58
commit cfa90f396a
10 changed files with 193 additions and 5 deletions
+21
View File
@@ -211,3 +211,24 @@ export function maskValue(key: string, value: string): string {
export function riskI18nKey(kind: RiskKind): string {
return `deeplink.risk.${kind}`;
}
/**
* 解码 deeplink 携带的 base64 载荷,供确认框展示。
*
* 解码失败时**回落到原始串,绝不返回空串**。确认框的职责是让用户看见即将写入
* 什么;一段解不开的 payload 也必须以原样出现。返回空会让整块内容凭空消失,
* 界面上看起来就像"没有脚本"——那正是攻击者想要的效果。
*/
export function decodeDeeplinkPayload(
encoded: unknown,
decode: (value: string) => string,
): string {
if (typeof encoded !== "string") return "";
try {
const decoded = decode(encoded);
// 解出空串同样可疑:宁可显示原始 base64,也不显示"什么都没有"。
return decoded === "" ? encoded : decoded;
} catch {
return encoded;
}
}