You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome扩展:使用不可信源创建图片时触发JavaScript错误

问题描述

在Chrome扩展中爬取页面并渲染结果,尝试加入网站图标(favicon)时,触发了如下Content Security Policy(CSP)错误:

Refused to load the script 'https://gohugo.io/dist/app.bundle.js' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

推测部分Hugo博客的图标链接指向JS文件,但疑惑为何img标签的src会触发脚本加载。图标渲染代码如下:

try {
    const resultImg = document.createElement("img");
    resultImg.src = result.favicon;
    if (result.favicon.includes(".js")) {
        // this never hits
        console.warn("found favicon that is js!");
        console.dir(result);
    }
    resultImg.width = 24;
    resultImg.height = 24;
    resultImg.className = "favicon";
    return resultImg
} catch (err) {
    return document.createElement("span")
}

注释该代码后错误消失,确认由其导致,但try-catch未捕获错误。另外发现Chrome访问https://gohugo.io/favicon-32x32.png时会加载展示图片的HTML页面,而curl直接下载PNG,怀疑存在含JS的图标链接。

问题分析与解决

核心原因

1. 为什么img标签触发脚本加载?

根源是服务器的内容协商机制:Chrome请求图标时,请求头的Accept字段包含text/html,服务器返回了包裹图片的HTML页面,而非直接返回PNG文件。这个HTML里嵌入了app.bundle.js脚本,浏览器解析HTML资源时会尝试加载脚本,触发CSP限制。而curl的Accept是*/*,服务器直接返回图片,所以不会触发脚本加载。

2. 为什么try-catch没捕获错误?

CSP错误属于浏览器级别的安全限制,这类错误不会抛出到JavaScript执行上下文,因此try-catch无法捕获,只会在控制台输出。

解决方案

方案1:过滤非图片格式的链接

直接排除非图片后缀的链接,只加载.png、.ico、.svg等格式的资源:

const validImageExts = [".png", ".ico", ".svg", ".jpg", ".jpeg"];
const isImageFavicon = validImageExts.some(ext => result.favicon.endsWith(ext));
if (!isImageFavicon) {
    return document.createElement("span");
}
// 后续正常创建img标签

方案2:强制请求图片资源

通过修改请求参数或设置属性,让服务器返回图片而非HTML:

const resultImg = document.createElement("img");
// 添加缓存破折号或格式参数,强制服务器返回图片
resultImg.src = `${result.favicon}?t=${Date.now()}`;
resultImg.crossOrigin = "anonymous";
// 监听加载失败事件,降级处理
resultImg.onerror = () => {
    resultImg.replaceWith(document.createElement("span"));
};
resultImg.width = 24;
resultImg.height = 24;
resultImg.className = "favicon";
return resultImg;

方案3:提前验证资源类型

用fetch发送HEAD请求,检查响应的Content-Type是否为图片类型:

async function createFaviconElem(url) {
    try {
        const resp = await fetch(url, { method: "HEAD" });
        const contentType = resp.headers.get("Content-Type");
        if (!contentType?.startsWith("image/")) {
            return document.createElement("span");
        }
        const img = document.createElement("img");
        img.src = url;
        img.width = 24;
        img.height = 24;
        img.className = "favicon";
        return img;
    } catch {
        return document.createElement("span");
    }
}
// 调用示例(需在async函数中)
// const faviconElem = await createFaviconElem(result.favicon);

内容的提问来源于stack exchange,提问作者user717847

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 02:22:42