Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions app/admin/community/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
*/

import { useEffect, useState } from "react";
import Image from "next/image";
import { AdminGuard } from "@/app/admin/events/AdminGuard";
import type { SharedLinkView } from "@/app/feed/types";
import { sanitizeExternalUrl } from "@/lib/url-safety";
Expand Down Expand Up @@ -130,16 +129,18 @@ function AdminCommunityInner() {
key={link.id}
className="border border-[var(--foreground)]/40 p-4 flex flex-col md:flex-row gap-4"
>
{/* 左:OG 封面缩略图(没抓到就占位) */}
{/* 左:OG 封面缩略图(没抓到就占位)。
改用 <img> + referrerPolicy="no-referrer":微信/知乎/小红书
图床防盗链会检查 Referer,非本站来源返回"未经允许"裂图。
next/image 的 remotePatterns 限制外站域名也一并规避。 */}
<div className="w-full md:w-40 aspect-[16/9] flex-shrink-0 bg-neutral-100 dark:bg-neutral-900 relative overflow-hidden">
{link.ogCover ? (
<Image
// eslint-disable-next-line @next/next/no-img-element
<img
src={link.ogCover}
alt={link.ogTitle ?? link.url}
fill
sizes="160px"
className="object-cover"
unoptimized
referrerPolicy="no-referrer"
className="absolute inset-0 w-full h-full object-cover"
Comment on lines +139 to +143
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里把 OG 封面从 next/image 改为原生 <img> 后,link.ogCover 会以未校验的 URL 直接进入 src。仓库已有 sanitizeMediaUrl 用于拦截 javascript:/data: 等协议(lib/url-safety.ts),建议在渲染前对 link.ogCover 做白名单过滤,并在返回 null 时回退到占位内容。

Copilot uses AI. Check for mistakes.
/>
) : (
<span className="absolute inset-0 flex items-center justify-center text-3xl font-bold text-neutral-400">
Expand Down
6 changes: 5 additions & 1 deletion app/feed/components/LinkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,15 @@ export function LinkCard({ link, categoryLabel, isLoggedIn }: LinkCardProps) {
>
{/* OG 封面 / 占位块 */}
{link.ogCover && !link.ogFetchFailed ? (
// next/image 全站 unoptimized:true,用 img 即可(与 events 页一致)
// next/image 全站 unoptimized:true,用 img 即可(与 events 页一致)。
// referrerPolicy="no-referrer":微信 mmbiz.qpic.cn 防盗链会检查 Referer,
// 非 mp.weixin.qq.com 来源直接返回"未经允许使用"裂图;不发 Referer 时
// 反而放行(微信客户端内打开文章浏览器也不发 Referer)。
// eslint-disable-next-line @next/next/no-img-element
<img
src={link.ogCover}
alt={link.ogTitle ?? link.host}
referrerPolicy="no-referrer"
className="w-full aspect-[16/9] object-cover border-b border-[var(--foreground)]"
Comment on lines 48 to 52
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link.ogCover 直接作为 <img src> 渲染,绕过了仓库里用于媒体 URL scheme 白名单的 sanitizeMediaUrl(lib/url-safety.ts 里明确要求后端/用户输入的媒体 URL 渲染前必须过白名单)。建议在渲染前先计算 const safeOgCover = sanitizeMediaUrl(link.ogCover),并仅在 safeOgCover 非空时渲染封面(否则走占位块)。

Copilot uses AI. Check for mistakes.
/>
) : (
Expand Down
Loading