fix(frontend): add missing rel="noopener noreferrer" to target="_blank" links (#1741)

* fix(frontend): add missing rel="noopener noreferrer" to target="_blank" links

Prevent tabnabbing attacks and referrer leakage by ensuring all
external links with target="_blank" include both noopener and
noreferrer in the rel attribute.

Made-with: Cursor

* style: fix code formatting
This commit is contained in:
yangzheli 2026-04-02 17:32:52 +08:00 committed by GitHub
parent f56d0b4869
commit 636053fb6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 33 additions and 14 deletions

View File

@ -253,7 +253,7 @@ export const OpenInChatGPT = (props: OpenInChatGPTProps) => {
<a
className="flex items-center gap-2"
href={providers.chatgpt.createUrl(query)}
rel="noopener"
rel="noopener noreferrer"
target="_blank"
>
<span className="shrink-0">{providers.chatgpt.icon}</span>
@ -273,7 +273,7 @@ export const OpenInClaude = (props: OpenInClaudeProps) => {
<a
className="flex items-center gap-2"
href={providers.claude.createUrl(query)}
rel="noopener"
rel="noopener noreferrer"
target="_blank"
>
<span className="shrink-0">{providers.claude.icon}</span>
@ -293,7 +293,7 @@ export const OpenInT3 = (props: OpenInT3Props) => {
<a
className="flex items-center gap-2"
href={providers.t3.createUrl(query)}
rel="noopener"
rel="noopener noreferrer"
target="_blank"
>
<span className="shrink-0">{providers.t3.icon}</span>
@ -313,7 +313,7 @@ export const OpenInScira = (props: OpenInSciraProps) => {
<a
className="flex items-center gap-2"
href={providers.scira.createUrl(query)}
rel="noopener"
rel="noopener noreferrer"
target="_blank"
>
<span className="shrink-0">{providers.scira.icon}</span>
@ -333,7 +333,7 @@ export const OpenInv0 = (props: OpenInv0Props) => {
<a
className="flex items-center gap-2"
href={providers.v0.createUrl(query)}
rel="noopener"
rel="noopener noreferrer"
target="_blank"
>
<span className="shrink-0">{providers.v0.icon}</span>
@ -353,7 +353,7 @@ export const OpenInCursor = (props: OpenInCursorProps) => {
<a
className="flex items-center gap-2"
href={providers.cursor.createUrl(query)}
rel="noopener"
rel="noopener noreferrer"
target="_blank"
>
<span className="shrink-0">{providers.cursor.icon}</span>

View File

@ -63,7 +63,7 @@ export const Source = ({ href, title, children, ...props }: SourceProps) => (
<a
className="flex items-center gap-2"
href={href}
rel="noreferrer"
rel="noopener noreferrer"
target="_blank"
{...props}
>

View File

@ -8,7 +8,11 @@ export function Header() {
return (
<header className="container-md fixed top-0 right-0 left-0 z-20 mx-auto flex h-16 items-center justify-between backdrop-blur-xs">
<div className="flex items-center gap-2">
<a href="https://github.com/bytedance/deer-flow" target="_blank">
<a
href="https://github.com/bytedance/deer-flow"
target="_blank"
rel="noopener noreferrer"
>
<h1 className="font-serif text-xl">DeerFlow</h1>
</a>
</div>
@ -26,7 +30,11 @@ export function Header() {
asChild
className="group relative z-10"
>
<a href="https://github.com/bytedance/deer-flow" target="_blank">
<a
href="https://github.com/bytedance/deer-flow"
target="_blank"
rel="noopener noreferrer"
>
<GitHubLogoIcon className="size-4" />
Star on GitHub
{env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" &&

View File

@ -57,6 +57,7 @@ export function CaseStudySection({ className }: { className?: string }) {
key={caseStudy.title}
href={pathOfThread(caseStudy.threadId) + "?mock=true"}
target="_blank"
rel="noopener noreferrer"
>
<Card className="group/card relative h-64 overflow-hidden">
<div

View File

@ -20,7 +20,11 @@ export function CommunitySection() {
>
<div className="flex justify-center">
<Button className="text-xl" size="lg" asChild>
<Link href="https://github.com/bytedance/deer-flow" target="_blank">
<Link
href="https://github.com/bytedance/deer-flow"
target="_blank"
rel="noopener noreferrer"
>
<GitHubLogoIcon />
Contribute Now
</Link>

View File

@ -188,7 +188,11 @@ export function ArtifactFileDetail({
</Tooltip>
)}
{!isWriteFile && (
<a href={urlOfArtifact({ filepath, threadId })} target="_blank">
<a
href={urlOfArtifact({ filepath, threadId })}
target="_blank"
rel="noopener noreferrer"
>
<ArtifactAction
icon={SquareArrowOutUpRightIcon}
label={t.common.openInNewWindow}
@ -217,6 +221,7 @@ export function ArtifactFileDetail({
<a
href={urlOfArtifact({ filepath, threadId, download: true })}
target="_blank"
rel="noopener noreferrer"
>
<ArtifactAction
icon={DownloadIcon}

View File

@ -111,6 +111,7 @@ export function ArtifactFileList({
download: true,
})}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
>
<Button variant="ghost">

View File

@ -215,7 +215,7 @@ function ToolCall({
<ChainOfThoughtSearchResults>
{result.map((item) => (
<ChainOfThoughtSearchResult key={item.url}>
<a href={item.url} target="_blank" rel="noreferrer">
<a href={item.url} target="_blank" rel="noopener noreferrer">
{item.title}
</a>
</ChainOfThoughtSearchResult>
@ -250,7 +250,7 @@ function ToolCall({
className="size-24 overflow-hidden rounded-lg object-cover"
href={item.source_url}
target="_blank"
rel="noreferrer"
rel="noopener noreferrer"
>
<div className="bg-accent size-24">
<img
@ -289,7 +289,7 @@ function ToolCall({
>
<ChainOfThoughtSearchResult>
{url && (
<a href={url} target="_blank" rel="noreferrer">
<a href={url} target="_blank" rel="noopener noreferrer">
{title}
</a>
)}