11<script setup lang="ts">
22import type {
33 NpmVersionDist ,
4+ PackageVersionInfo ,
45 PackumentVersion ,
56 ProvenanceDetails ,
67 ReadmeResponse ,
@@ -12,6 +13,7 @@ import { joinURL } from 'ufo'
1213import { areUrlsEquivalent } from ' #shared/utils/url'
1314import { isEditableElement } from ' ~/utils/input'
1415import { getDependencyCount } from ' ~/utils/npm/dependency-count'
16+ import { detectPublishSecurityDowngradeForVersion } from ' ~/utils/publish-security'
1517import { useModal } from ' ~/composables/useModal'
1618import { useAtproto } from ' ~/composables/atproto/useAtproto'
1719import { togglePackageLike } from ' ~/utils/atproto/likes'
@@ -143,6 +145,18 @@ const {
143145 error,
144146} = usePackage (packageName , () => resolvedVersion .value ?? requestedVersion .value )
145147const displayVersion = computed (() => pkg .value ?.requestedVersion ?? null )
148+ const versionSecurityMetadata = computed <PackageVersionInfo []>(() => {
149+ if (! pkg .value ) return []
150+ if (pkg .value .securityVersions ?.length ) return pkg .value .securityVersions
151+
152+ return Object .entries (pkg .value .versions ).map (([version , metadata ]) => ({
153+ version ,
154+ time: pkg .value ?.time ?.[version ],
155+ hasProvenance: !! metadata .hasProvenance ,
156+ trustLevel: metadata .trustLevel ,
157+ deprecated: metadata .deprecated ,
158+ }))
159+ })
146160
147161// Process package description
148162const pkgDescription = useMarkdown (() => ({
@@ -225,6 +239,30 @@ const deprecationNoticeMessage = useMarkdown(() => ({
225239 text: deprecationNotice .value ?.message ?? ' ' ,
226240}))
227241
242+ const publishSecurityDowngrade = computed (() => {
243+ const currentVersion = displayVersion .value ?.version
244+ if (! currentVersion ) return null
245+ return detectPublishSecurityDowngradeForVersion (versionSecurityMetadata .value , currentVersion )
246+ })
247+
248+ const installVersionOverride = computed (
249+ () => publishSecurityDowngrade .value ?.trustedVersion ?? null ,
250+ )
251+
252+ const downgradeFallbackInstallText = computed (() => {
253+ const d = publishSecurityDowngrade .value
254+ if (! d ?.trustedVersion ) return null
255+ if (d .trustedTrustLevel === ' provenance' )
256+ return $t (' package.security_downgrade.fallback_install_provenance' , {
257+ version: d .trustedVersion ,
258+ })
259+ if (d .trustedTrustLevel === ' trustedPublisher' )
260+ return $t (' package.security_downgrade.fallback_install_trustedPublisher' , {
261+ version: d .trustedVersion ,
262+ })
263+ return null
264+ })
265+
228266const sizeTooltip = computed (() => {
229267 const chunks = [
230268 displayVersion .value &&
@@ -1020,9 +1058,96 @@ onKeyStroke(
10201058 :id =" `pm-panel-${activePmId}`"
10211059 :aria-labelledby =" `pm-tab-${activePmId}`"
10221060 >
1061+ <div
1062+ v-if =" publishSecurityDowngrade"
1063+ role =" alert"
1064+ class =" mb-4 rounded-lg border border-amber-600/40 bg-amber-500/10 px-4 py-3 text-amber-700 dark:text-amber-400"
1065+ >
1066+ <h3 class =" m-0 flex items-center gap-2 font-mono text-sm font-medium" >
1067+ <span class =" i-carbon:warning-alt w-4 h-4 shrink-0" aria-hidden =" true" />
1068+ {{ $t('package.security_downgrade.title') }}
1069+ </h3 >
1070+ <p class =" mt-2 mb-0 text-sm" >
1071+ <i18n-t
1072+ v-if ="
1073+ publishSecurityDowngrade.downgradedTrustLevel === 'none' &&
1074+ publishSecurityDowngrade.trustedTrustLevel === 'provenance'
1075+ "
1076+ keypath =" package.security_downgrade.description_to_none_provenance"
1077+ tag =" span"
1078+ scope =" global"
1079+ >
1080+ <template #provenance >
1081+ <a
1082+ href =" https://docs.npmjs.com/generating-provenance-statements"
1083+ target =" _blank"
1084+ rel =" noopener noreferrer"
1085+ class =" inline-flex items-center gap-1 rounded-sm underline underline-offset-4 decoration-amber-600/60 dark:decoration-amber-400/50 hover:decoration-fg focus-visible:decoration-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/70 transition-colors"
1086+ >{{ $t('package.security_downgrade.provenance_link_text')
1087+ }}<span class =" i-carbon-launch w-3 h-3" aria-hidden =" true"
1088+ /></a >
1089+ </template >
1090+ </i18n-t >
1091+ <i18n-t
1092+ v-else-if ="
1093+ publishSecurityDowngrade.downgradedTrustLevel === 'none' &&
1094+ publishSecurityDowngrade.trustedTrustLevel === 'trustedPublisher'
1095+ "
1096+ keypath =" package.security_downgrade.description_to_none_trustedPublisher"
1097+ tag =" span"
1098+ scope =" global"
1099+ >
1100+ <template #trustedPublishing >
1101+ <a
1102+ href =" https://docs.npmjs.com/adding-a-trusted-publisher-to-a-package"
1103+ target =" _blank"
1104+ rel =" noopener noreferrer"
1105+ class =" inline-flex items-center gap-1 rounded-sm underline underline-offset-4 decoration-amber-600/60 dark:decoration-amber-400/50 hover:decoration-fg focus-visible:decoration-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/70 transition-colors"
1106+ >{{ $t('package.security_downgrade.trusted_publishing_link_text')
1107+ }}<span class =" i-carbon-launch w-3 h-3" aria-hidden =" true"
1108+ /></a >
1109+ </template >
1110+ </i18n-t >
1111+ <i18n-t
1112+ v-else-if ="
1113+ publishSecurityDowngrade.downgradedTrustLevel === 'provenance' &&
1114+ publishSecurityDowngrade.trustedTrustLevel === 'trustedPublisher'
1115+ "
1116+ keypath =" package.security_downgrade.description_to_provenance_trustedPublisher"
1117+ tag =" span"
1118+ scope =" global"
1119+ >
1120+ <template #provenance >
1121+ <a
1122+ href =" https://docs.npmjs.com/generating-provenance-statements"
1123+ target =" _blank"
1124+ rel =" noopener noreferrer"
1125+ class =" inline-flex items-center gap-1 rounded-sm underline underline-offset-4 decoration-amber-600/60 dark:decoration-amber-400/50 hover:decoration-fg focus-visible:decoration-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/70 transition-colors"
1126+ >{{ $t('package.security_downgrade.provenance_link_text')
1127+ }}<span class =" i-carbon-launch w-3 h-3" aria-hidden =" true"
1128+ /></a >
1129+ </template >
1130+ <template #trustedPublishing >
1131+ <a
1132+ href =" https://docs.npmjs.com/adding-a-trusted-publisher-to-a-package"
1133+ target =" _blank"
1134+ rel =" noopener noreferrer"
1135+ class =" inline-flex items-center gap-1 rounded-sm underline underline-offset-4 decoration-amber-600/60 dark:decoration-amber-400/50 hover:decoration-fg focus-visible:decoration-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/70 transition-colors"
1136+ >{{ $t('package.security_downgrade.trusted_publishing_link_text')
1137+ }}<span class =" i-carbon-launch w-3 h-3" aria-hidden =" true"
1138+ /></a >
1139+ </template >
1140+ </i18n-t >
1141+ {{ ' ' }}
1142+ <template v-if =" downgradeFallbackInstallText " >
1143+ {{ downgradeFallbackInstallText }}
1144+ </template >
1145+ </p >
1146+ </div >
10231147 <TerminalInstall
10241148 :package-name =" pkg.name"
10251149 :requested-version =" requestedVersion"
1150+ :install-version-override =" installVersionOverride"
10261151 :jsr-info =" jsrInfo"
10271152 :types-package-name =" typesPackageName"
10281153 :executable-info =" executableInfo"
0 commit comments