Skip to content

Commit d24d9ad

Browse files
committed
🐛 修复SRI资源校验
1 parent 2b33a70 commit d24d9ad

2 files changed

Lines changed: 61 additions & 10 deletions

File tree

src/app/service/resource/manager.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import CacheKey from "@App/pkg/utils/cache_key";
1717
import { isText } from "@App/pkg/utils/istextorbinary";
1818
import Manager from "../manager";
1919
import { calculateHashFromArrayBuffer } from "@App/pkg/utils/crypto";
20+
import { base64ToHex, isBase64 } from "./utils";
2021

2122
// 资源管理器,负责资源的更新获取等操作
2223

@@ -126,7 +127,7 @@ export class ResourceManager extends Manager {
126127
}
127128
const ret: { [key: string]: Resource } = {};
128129
await Promise.allSettled(
129-
script.metadata.require.map(async (u) => {
130+
script.metadata!.require!.map(async (u) => {
130131
const res = await this.getResource(script.id, u, "require-css");
131132
if (res) {
132133
ret[u] = res;
@@ -188,7 +189,7 @@ export class ResourceManager extends Manager {
188189
}
189190
const ret: { [key: string]: Resource } = {};
190191
await Promise.allSettled(
191-
script.metadata.require.map(async (u) => {
192+
script.metadata!.require!.map(async (u) => {
192193
const res = await this.checkResource(script.id, u, "require-css");
193194
if (res) {
194195
ret[u] = res;
@@ -339,13 +340,21 @@ export class ResourceManager extends Manager {
339340
if (resource) {
340341
// 校验hash
341342
if (u.hash) {
342-
if (
343-
(u.hash.md5 && u.hash.md5 !== resource.hash.md5) ||
344-
(u.hash.sha1 && u.hash.sha1 !== resource.hash.sha1) ||
345-
(u.hash.sha256 && u.hash.sha256 !== resource.hash.sha256) ||
346-
(u.hash.sha384 && u.hash.sha384 !== resource.hash.sha384) ||
347-
(u.hash.sha512 && u.hash.sha512 !== resource.hash.sha512)
348-
) {
343+
let flag = true;
344+
Object.keys(u.hash).forEach((key) => {
345+
if (key in resource.hash) {
346+
let hex = u.hash![key];
347+
if (isBase64(hex)) {
348+
// 转换为hash进对比
349+
hex = base64ToHex(u.hash![key]);
350+
}
351+
// 对比普通的hash
352+
if (resource.hash[key as keyof ResourceHash] !== hex) {
353+
flag = false;
354+
}
355+
}
356+
});
357+
if (!flag) {
349358
resource.content = `console.warn("ScriptCat: couldn't load resource from URL ${url} due to a SRI error ");`;
350359
}
351360
}
@@ -406,7 +415,8 @@ export class ResourceManager extends Manager {
406415
if (kv.length < 2) {
407416
return;
408417
}
409-
hash[kv[0]] = kv[1].toLocaleLowerCase();
418+
const [key, value] = kv;
419+
hash[key] = value;
410420
});
411421
return { url: urls[0], hash };
412422
}

src/app/service/resource/utils.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// 检查是不是base64编码
2+
export function isBase64(str: string): boolean {
3+
if (typeof str !== "string" || str.length === 0) {
4+
return false;
5+
}
6+
7+
// Base64字符串必须只包含有效的Base64字符
8+
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
9+
if (!base64Regex.test(str)) {
10+
return false;
11+
}
12+
13+
// Base64字符串长度必须是4的倍数(如果有填充),或者没有填充的情况下可以是其他长度
14+
// 但要确保它不是纯数字或纯字母(避免误判十六进制字符串)
15+
const lengthMod4 = str.length % 4;
16+
if (lengthMod4 === 1) {
17+
// 长度除以4余数为1的字符串不可能是有效的Base64
18+
return false;
19+
}
20+
21+
// 检查是否包含Base64特有的字符(+ 或 /),或者有正确的填充
22+
// 这样可以避免将纯十六进制字符串误判为Base64
23+
if (str.includes("+") || str.includes("/") || str.endsWith("=")) {
24+
return true;
25+
}
26+
27+
// 如果没有特殊字符,检查是否可能是有效的Base64(但要排除明显的十六进制)
28+
// 十六进制字符串只包含0-9和a-f(或A-F),而Base64还包含其他字母
29+
const hexOnlyRegex = /^[0-9a-fA-F]+$/;
30+
if (hexOnlyRegex.test(str)) {
31+
// 这很可能是十六进制字符串,不是Base64
32+
return false;
33+
}
34+
35+
return true;
36+
}
37+
38+
export function base64ToHex(base64: string): string {
39+
const buffer = Buffer.from(base64, "base64");
40+
return buffer.toString("hex");
41+
}

0 commit comments

Comments
 (0)