Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #608 +/- ##
=======================================
Coverage 79.03% 79.03%
=======================================
Files 48 48
Lines 7235 7235
Branches 7235 7235
=======================================
Hits 5718 5718
Misses 1140 1140
Partials 377 377 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review
This pull request introduces a new EndevSponsors component to display company sponsors in the footer, fetching data dynamically from an external JSON source. Feedback includes improving list rendering stability by using unique keys, adhering to SEO best practices with the rel="sponsored" attribute, optimizing image loading for performance, and converting the component to TypeScript for consistency with the rest of the project.
| <div class="EndevSponsorsLogos"> | ||
| <a | ||
| v-for="sponsor in sponsors" | ||
| :key="sponsor.name" |
| :aria-label="sponsor.name" | ||
| class="EndevSponsorsLogo" | ||
| :href="sponsor.url" | ||
| rel="noopener noreferrer" |
| rel="noopener noreferrer" | ||
| target="_blank" | ||
| > | ||
| <img :alt="sponsor.name" :src="sponsor.logo" /> |
There was a problem hiding this comment.
| <script setup> | ||
| import { onMounted, ref } from "vue"; | ||
|
|
||
| const sponsors = ref([]); | ||
|
|
||
| onMounted(async () => { | ||
| try { | ||
| const res = await fetch("https://en.dev/sponsors.json", { | ||
| headers: { Accept: "application/json" }, | ||
| }); | ||
| if (!res.ok) return; | ||
|
|
||
| const payload = await res.json(); | ||
| sponsors.value = (Array.isArray(payload.sponsors) ? payload.sponsors : []) | ||
| .filter((sponsor) => | ||
| sponsor?.kind !== "infrastructure" && | ||
| sponsor?.name && | ||
| sponsor?.url && | ||
| sponsor?.logo | ||
| ); | ||
| } catch { | ||
| sponsors.value = []; | ||
| } | ||
| }); |
There was a problem hiding this comment.
The project uses TypeScript in other theme files (e.g., index.ts). To maintain consistency and leverage type safety, consider using <script setup lang="ts"> and defining an interface for the sponsor data structure.
<script setup lang="ts">
import { onMounted, ref } from "vue";
interface Sponsor {
name: string;
url: string;
logo: string;
kind?: string;
}
const sponsors = ref<Sponsor[]>([]);
onMounted(async () => {
try {
const res = await fetch("https://en.dev/sponsors.json", {
headers: { Accept: "application/json" },
});
if (!res.ok) return;
const payload = await res.json();
const rawSponsors = Array.isArray(payload.sponsors) ? payload.sponsors : [];
sponsors.value = rawSponsors.filter((sponsor: any) =>
sponsor?.kind !== "infrastructure" &&
sponsor?.name &&
sponsor?.url &&
sponsor?.logo
);
} catch {
sponsors.value = [];
}
});
Greptile SummaryAdds
Confidence Score: 3/5Not safe to merge without URL scheme validation — a compromised feed could result in XSS on the docs site. A P1 security finding (unvalidated external URL used as href) is present in the primary new file. While the risk depends on a compromised external endpoint, the fix is trivial and the pattern should be corrected before shipping. docs/.vitepress/theme/EndevSponsors.vue — specifically the href binding on line 13.
|
| Filename | Overview |
|---|---|
| docs/.vitepress/theme/EndevSponsors.vue | New component that fetches and renders sponsor logos from an external JSON feed; has an unvalidated external URL used as href (P1 security) and missing image dimensions (P2 CLS). |
| docs/.vitepress/theme/index.ts | Imports EndevSponsors and slots it before EndevFooter in layout-bottom; change is minimal and correct. |
Sequence Diagram
sequenceDiagram
participant Browser
participant VitePress as VitePress Layout
participant EndevSponsors as EndevSponsors.vue
participant API as en.dev/sponsors.json
Browser->>VitePress: Page load
VitePress->>EndevSponsors: Render (layout-bottom slot)
Note over EndevSponsors: sponsors = [] → section hidden
EndevSponsors-->>Browser: (nothing rendered yet)
EndevSponsors->>API: fetch() on onMounted
API-->>EndevSponsors: { sponsors: [...] }
Note over EndevSponsors: filter out infrastructure,<br/>require name + url + logo
EndevSponsors-->>Browser: Render sponsor logos + CTA
Reviews (1): Last reviewed commit: "add sponsor feed to docs" | Re-trigger Greptile
| </p> | ||
| <div class="EndevSponsorsLogos"> | ||
| <a | ||
| v-for="sponsor in sponsors" |
There was a problem hiding this comment.
Unvalidated external URL used as
href
sponsor.url is inserted directly into the anchor's href with only a truthiness check. A javascript:… value would pass that check and execute in the visitor's browser if the en.dev/sponsors.json feed is ever compromised or returns unexpected data. The existing EndevFooter.vue hardcodes its URL precisely to avoid this, so the same level of safety should apply here.
| v-for="sponsor in sponsors" | |
| :href="sanitizeUrl(sponsor.url)" |
And add a helper in <script setup>:
function sanitizeUrl(url) {
try {
const { protocol } = new URL(url);
return protocol === 'https:' || protocol === 'http:' ? url : '#';
} catch {
return '#';
}
}| rel="noopener noreferrer" | ||
| target="_blank" | ||
| > | ||
| <img :alt="sponsor.name" :src="sponsor.logo" /> |
There was a problem hiding this comment.
Missing
width/height on sponsor <img> causes CLS
The existing EndevFooter.vue sets explicit width and height on its image to prevent layout shifts. These sponsor logos lack dimensions, so every page load that renders this block will produce a measurable Cumulative Layout Shift while the images load.
| <img :alt="sponsor.name" :src="sponsor.logo" /> | |
| <img :alt="sponsor.name" :src="sponsor.logo" width="120" height="22" /> |
Summary
Adds an en.dev company sponsor block to the docs footer.
Changes
EndevSponsors.vue, which fetcheshttps://en.dev/sponsors.jsonclient-side.sponsorslist, which is paid company sponsors only.Validation
npm install --no-package-lock --ignore-scripts --legacy-peer-depsnpm run docs:buildNote
Medium Risk
Adds a client-side fetch to an external endpoint (
https://en.dev/sponsors.json) and renders third-party-provided logo/link data in the docs layout, which can impact privacy, performance, and reliability if the endpoint is slow or changes.Overview
Adds a new
EndevSponsorsfooter section to the VitePress docs theme that fetches and displays company sponsor logos/links fromhttps://en.dev/sponsors.json(filtering outinfrastructureentries and requiringname/url/logo).Updates the theme layout to render the sponsors block above the existing
EndevFooteratlayout-bottom.Reviewed by Cursor Bugbot for commit 7a79a49. Bugbot is set up for automated code reviews on this repo. Configure here.