Added "rel=noopener-noreferrer" attribute for links to external pages (#3039)

* added rel="noopener noreferrer" to link elements using external and noIcon prop

* fix: resolve asChild and external prop conflicts in Link components

* Revert "fix: resolve asChild and external prop conflicts in Link components"

This reverts commit e99580049946b487ce4dc77e7cdab2cd17e5c820.

* fix: remove asChild prop conflicts with external links
This commit is contained in:
enochlee
2025-10-07 18:03:34 +02:00
committed by GitHub
parent 70e0922d8a
commit 2d6c5b3773
24 changed files with 42 additions and 35 deletions
File diff suppressed because one or more lines are too long
@@ -152,7 +152,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre
);
})();
const contactUsLink = <span>contact us <Link href="mailto:[email protected]">help@blockscout.com</Link></span>;
const contactUsLink = <span>contact us <Link href="mailto:[email protected]" rel="noopener noreferrer">help@blockscout.com</Link></span>;
const rootError = (() => {
switch (formState.errors.root?.type) {
@@ -199,7 +199,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre
{ rootError && <Alert status="warning" mb={ 6 }>{ rootError }</Alert> }
<Box mb={ 8 }>
<span>Please select the address to sign and copy the message and sign it using the Blockscout message provider of your choice. </span>
<Link href="https://docs.blockscout.com/using-blockscout/my-account/verified-addresses/copy-and-sign-message" target="_blank">
<Link href="https://docs.blockscout.com/using-blockscout/my-account/verified-addresses/copy-and-sign-message" external noIcon>
Additional instructions
</Link>
<span>. If you do not see your address here but are sure that you are the owner of the contract, kindly </span>
@@ -19,10 +19,10 @@ const ContractVerificationFieldConstructorArgs = () => {
/>
<>
<span>Add arguments in </span>
<Link href="https://solidity.readthedocs.io/en/develop/abi-spec.html" target="_blank">ABI hex encoded form</Link>
<Link href="https://solidity.readthedocs.io/en/develop/abi-spec.html" external noIcon>ABI hex encoded form</Link>
<span> if required by the contract. Constructor arguments are written right to left, and will be found at the end of the input created bytecode.</span>
<span> They may also be </span>
<Link href="https://abi.hashex.org/" target="_blank">parsed here</Link>
<Link href="https://abi.hashex.org/" external noIcon>parsed here</Link>
<span>.</span>
</>
</ContractVerificationFormRow>
@@ -36,7 +36,8 @@ const ContractVerificationFieldEvmVersion = ({ isVyper, config }: Props) => {
'https://docs.vyperlang.org/en/stable/compiling-a-contract.html#target-options' :
'https://docs.soliditylang.org/en/latest/using-the-compiler.html#target-options'
}
target="_blank"
external
noIcon
>
EVM version details
</Link>
@@ -36,14 +36,14 @@ const ContractVerificationFieldMethod = ({ methods }: Props) => {
case 'multi-part':
return <List.Item key={ method }>Verification of multi-part Solidity files.</List.Item>;
case 'sourcify':
return <List.Item key={ method }>Verification through <Link href="https://sourcify.dev/" target="_blank" className="dark">Sourcify</Link>.</List.Item>;
return <List.Item key={ method }>Verification through <Link href="https://sourcify.dev/" external noIcon className="dark">Sourcify</Link>.</List.Item>;
case 'standard-input':
return (
<List.Item key={ method }>
<span>Verification using </span>
<Link
href="https://docs.soliditylang.org/en/latest/using-the-compiler.html#input-description"
target="_blank"
external noIcon
className="dark"
>
Standard input JSON
@@ -61,7 +61,7 @@ const ContractVerificationFieldMethod = ({ methods }: Props) => {
<span>Verification of Vyper contract using </span>
<Link
href="https://docs.vyperlang.org/en/stable/compiling-a-contract.html#compiler-input-and-output-json-description"
target="_blank"
external noIcon
className="dark"
>
Standard input JSON
@@ -17,7 +17,7 @@ const ContractVerificationVyperMultiPartFile = ({ config }: { config: SmartContr
const interfacesHint = (
<>
<span>Add any </span>
<Link href="https://docs.vyperlang.org/en/stable/interfaces.html" target="_blank">required interfaces</Link>
<Link href="https://docs.vyperlang.org/en/stable/interfaces.html" external noIcon>required interfaces</Link>
<span> for the main compiled contract.</span>
</>
);
+1 -2
View File
@@ -55,8 +55,7 @@ const CapybaraRunner = () => {
<Text fontSize="lg" textAlign="center">Congratulations! You’re eligible to claim an epic hidden badge!</Text>
<Link
href={ easterEggBadgeFeature.badgeClaimLink }
target="_blank"
asChild
external noIcon
>
<Button>Claim</Button>
</Link>
+1 -2
View File
@@ -202,8 +202,7 @@ const Puzzle15 = () => {
<Text fontSize="lg" textAlign="center">Congratulations! You're eligible to claim an epic hidden badge!</Text>
<Link
href={ easterEggPuzzleBadgeFeature.badgeClaimLink }
target="_blank"
asChild
external noIcon
>
<Button>Claim</Button>
</Link>
@@ -19,7 +19,7 @@ const SocialLink = ({ href, icon, title }: Props) => {
href={ href }
aria-label={ title }
title={ title }
target="_blank"
external noIcon
display="inline-flex"
alignItems="center"
>
+2 -1
View File
@@ -63,7 +63,8 @@ const ChainWidget = ({ data }: Props) => {
<Tooltip content="Open explorer">
<Link
href={ data.config.app.baseUrl }
target="_blank"
external
noIcon
color="icon.secondary"
_hover={{
color: 'hover',
+1 -1
View File
@@ -58,7 +58,7 @@ const ApiKeysPage: React.FC = () => {
const description = (
<AccountPageDescription>
Create API keys to use for your RPC and EthRPC API requests. For more information, see { space }
<Link href="https://docs.blockscout.com/using-blockscout/my-account/api-keys#api-keys" target="_blank">“How to use a Blockscout API key”</Link>.
<Link href="https://docs.blockscout.com/using-blockscout/my-account/api-keys#api-keys" external noIcon>"How to use a Blockscout API key"</Link>.
</AccountPageDescription>
);
+1 -1
View File
@@ -202,7 +202,7 @@ const VerifiedAddresses = () => {
<List.Item>The source code for the smart contract is deployed on “{ config.chain.name }”.</List.Item>
<List.Item>
<span>The source code is verified (if not yet verified, you can use </span>
<Link href="https://docs.blockscout.com/devs/verification" target="_blank">this tool</Link>
<Link href="https://docs.blockscout.com/devs/verification" external noIcon>this tool</Link>
<span>).</span>
</List.Item>
</List.Root>
@@ -110,7 +110,7 @@ const CongratsStepContent = ({ isReferral, customReferralReward }: Props) => {
/>
<Skeleton loading={ referralsQuery.isLoading } mt={ 6 }>
<Button asChild>
<a href={ `https://x.com/intent/tweet?text=${ encodeURIComponent(shareText) }` } target="_blank">
<a href={ `https://x.com/intent/tweet?text=${ encodeURIComponent(shareText) }` } target="_blank" rel="noopener noreferrer">
Share on <IconSvg name="social/twitter" boxSize={ 6 } ml={ 1 }/>
</a>
</Button>
+1 -1
View File
@@ -58,7 +58,7 @@ const SpecifyBanner = ({ className, platform, address, onEmpty, isLoading }: Pro
}, [ address, isMobile, onEmpty, isLoading ]);
const handleClick = React.useCallback(() => {
window.open(ad?.ctaUrl, '_blank');
window.open(ad?.ctaUrl, '_blank', 'noopener,noreferrer');
}, [ ad?.ctaUrl ]);
const { width, height } = (() => {
+1 -1
View File
@@ -25,7 +25,7 @@ const LinkNewTab = ({ className, label, href }: Props) => {
className={ className }
borderRadius={ 0 }
>
<Link href={ href } target="_blank">
<Link href={ href } external noIcon>
<IconSvg name="open-link"/>
</Link>
</IconButton>
+4 -4
View File
@@ -78,11 +78,11 @@ const Footer = () => {
const frontendLink = (() => {
if (config.UI.footer.frontendVersion) {
return <Link href={ FRONT_VERSION_URL } target="_blank">{ config.UI.footer.frontendVersion }</Link>;
return <Link href={ FRONT_VERSION_URL } external noIcon>{ config.UI.footer.frontendVersion }</Link>;
}
if (config.UI.footer.frontendCommit) {
return <Link href={ FRONT_COMMIT_URL } target="_blank">{ config.UI.footer.frontendCommit }</Link>;
return <Link href={ FRONT_COMMIT_URL } external noIcon>{ config.UI.footer.frontendCommit }</Link>;
}
return null;
@@ -123,7 +123,7 @@ const Footer = () => {
<Box gridArea={ gridArea }>
<Flex columnGap={ 2 } textStyle="xs" alignItems="center">
<span>Made with</span>
<Link href="https://www.blockscout.com" target="_blank" display="inline-flex" color={ logoColor } _hover={{ color: logoColor }}>
<Link href="https://www.blockscout.com" external noIcon display="inline-flex" color={ logoColor } _hover={{ color: logoColor }}>
<IconSvg
name="networks/logo-placeholder"
width="80px"
@@ -137,7 +137,7 @@ const Footer = () => {
<Box mt={ 6 } alignItems="start" textStyle="xs">
{ apiVersionUrl && (
<Text>
Backend: <Link href={ apiVersionUrl } target="_blank">{ backendVersionData?.backend_version }</Link>
Backend: <Link href={ apiVersionUrl } external noIcon>{ backendVersionData?.backend_version }</Link>
</Text>
) }
{ frontendLink && (
+1 -1
View File
@@ -58,7 +58,7 @@ const FooterLinkItem = ({ icon, iconSize, iconUrl, text, url, isLoading }: Props
})();
return (
<Link href={ url } display="flex" alignItems="center" h="30px" variant="subtle" target="_blank" textStyle="xs" columnGap={ 2 }>
<Link href={ url } display="flex" alignItems="center" h="30px" variant="subtle" external noIcon textStyle="xs" columnGap={ 2 }>
{ iconElement }
{ text }
</Link>
@@ -47,6 +47,7 @@ const NetworkMenuLink = ({ title, icon, isActive: isActiveProp, isMobile, url, i
display="flex"
href={ url }
target="_blank"
rel="noopener noreferrer"
px={ 2 }
py="5px"
opacity={ isActive ? 0.6 : 1 }
+1 -1
View File
@@ -59,7 +59,7 @@ const Sol2UmlDiagram = ({ addressHash }: Props) => {
const image = new Image();
image.src = imgUrl;
const newWindow = window.open(imgUrl);
const newWindow = window.open(imgUrl, '_blank', 'noopener,noreferrer');
newWindow?.document.write(image.outerHTML);
}, [ imgUrl ]);
+1 -1
View File
@@ -61,7 +61,7 @@ const TokenNftMarketplaces = ({ hash, id, isLoading, appActionData, source }: Pr
{ items.map((item) => {
return (
<Tooltip content={ `View on ${ item.name }` } key={ item.name }>
<Link href={ item.href } target="_blank">
<Link href={ item.href } external noIcon>
<Image
src={ item.logo_url }
alt={ `${ item.name } marketplace logo` }
+2 -1
View File
@@ -11,7 +11,8 @@ const DocsLink = ({ href }: Props) => {
return (
<Link
href={ href }
target="_blank"
external
noIcon
display="inline-flex"
alignItems="center"
columnGap={ 1 }
+2 -1
View File
@@ -19,7 +19,8 @@ const ServiceLink = ({ href, title, icon }: Props) => {
href={ href }
aria-label={ title }
title={ title }
target="_blank"
external
noIcon
display="inline-flex"
alignItems="center"
>
+2 -1
View File
@@ -14,7 +14,8 @@ const SupportLink = ({ url }: Props) => {
return (
<Link
href={ href }
target="_blank"
external
noIcon
display="inline-flex"
alignItems="center"
columnGap={ 1 }
@@ -75,8 +75,8 @@ const TxDetailsWithdrawalStatusOptimistic = ({ status, l1TxHash }: Props) => {
const rightSlot = hasClaimButton ? (
<Link
href={ rollupFeature.L2WithdrawalUrl }
asChild
target="_blank"
external
noIcon
>
<Button
variant="outline"