mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-04 11:43:57 +08:00
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:
@@ -3,6 +3,7 @@ import {
|
||||
classifyCommand,
|
||||
classifyEndpoint,
|
||||
classifyEnvKey,
|
||||
decodeDeeplinkPayload,
|
||||
maskValue,
|
||||
} from "./deeplinkRisk";
|
||||
|
||||
@@ -158,6 +159,38 @@ describe("classifyCommand", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("decodeDeeplinkPayload", () => {
|
||||
const ok = (v: string) => `decoded:${v}`;
|
||||
const boom = () => {
|
||||
throw new Error("bad base64");
|
||||
};
|
||||
|
||||
it("returns the decoded payload on success", () => {
|
||||
expect(decodeDeeplinkPayload("abc", ok)).toBe("decoded:abc");
|
||||
});
|
||||
|
||||
it("falls back to the raw string when decoding throws", () => {
|
||||
// 确认框必须展示即将写入的东西。解不开也要原样显示——返回空串会让整块
|
||||
// 内容消失,界面看起来像"没有脚本",那正是攻击者要的效果。
|
||||
expect(decodeDeeplinkPayload("!!!not-base64!!!", boom)).toBe(
|
||||
"!!!not-base64!!!",
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to the raw string when decoding yields empty", () => {
|
||||
// 解出空串同样可疑:不能让 payload 静默消失
|
||||
expect(decodeDeeplinkPayload("d293", () => "")).toBe("d293");
|
||||
});
|
||||
|
||||
it("returns empty for a non-string field instead of throwing", () => {
|
||||
// 该字段来自解码后的任意 JSON,形状不可信;抛错会让确认框整个渲染失败
|
||||
for (const hostile of [42, null, undefined, { a: 1 }, ["x"]]) {
|
||||
expect(() => decodeDeeplinkPayload(hostile, ok)).not.toThrow();
|
||||
expect(decodeDeeplinkPayload(hostile, ok)).toBe("");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("maskValue", () => {
|
||||
it("masks credential-shaped keys but keeps ordinary values readable", () => {
|
||||
expect(maskValue("ANTHROPIC_AUTH_TOKEN", "sk-ant-1234567890abcdef")).toBe(
|
||||
|
||||
Reference in New Issue
Block a user