Skip to content
Open
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
30 changes: 30 additions & 0 deletions src/components/MetricCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { useState } from "react";

interface MetricCardProps {
username: string;
}

const MetricCard: React.FC<MetricCardProps> = ({ username }) => {
const [loading, setLoading] = useState(true);

if (!username) return null;

const metricsURL = `https://metrics.lecoq.io/${username}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify the external service URL and add input validation.

The metrics.lecoq.io service URL construction should be validated for security. Consider sanitizing the username input to prevent potential issues.


🏁 Script executed:

#!/bin/bash
# Description: Verify the metrics.lecoq.io service is legitimate and check for any security considerations
# Expected: Service should be accessible and legitimate

# Check if the service responds properly
curl -I "https://metrics.lecoq.io/" 2>/dev/null | head -1

# Check if a sample metrics page loads
curl -I "https://metrics.lecoq.io/octocat" 2>/dev/null | head -1

Length of output: 123


Sanitize username in metrics URL and handle service errors

To prevent URL injection and ensure robustness when metrics.lecoq.io returns errors:

  • In src/components/MetricCard.tsx (line 12), wrap the username in encodeURIComponent:
  • const metricsURL = https://metrics.lecoq.io/${username};
  • const metricsURL = https://metrics.lecoq.io/${encodeURIComponent(username)};
  • Add client-side validation (e.g., propTypes, a regex or a validation library) to allow only expected characters, such as alphanumerics, hyphens, and underscores:
    const isValidUsername = /^[A-Za-z0-9_-]+$/.test(username);
    if (!isValidUsername) {
      // show an error message or fallback UI
    }
  • Gracefully handle non-200 responses when fetching metrics (e.g., show a “Metrics unavailable” state on 4xx/5xx).

This ensures the URL can’t be tampered with and the UI degrades cleanly if the service errors.

🤖 Prompt for AI Agents
In src/components/MetricCard.tsx at line 12, sanitize the username by wrapping
it with encodeURIComponent when constructing the metricsURL to prevent URL
injection. Add client-side validation for the username prop using a regex that
allows only alphanumeric characters, hyphens, and underscores; if validation
fails, display an error message or fallback UI. Additionally, update the metrics
fetching logic to handle non-200 HTTP responses gracefully by showing a "Metrics
unavailable" state when the service returns errors.


return (
<div className="w-full flex flex-col items-center p-4">
{loading && <p className="text-gray-500 mb-2">Loading metrics...</p>}
<iframe
src={metricsURL}
width="100%"
height="400"
frameBorder="0"
title="GitHub Metrics"
className="rounded-lg shadow-md"
onLoad={() => setLoading(false)}
></iframe>
</div>
Comment on lines +17 to +26
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add accessibility improvements.

The iframe should have better accessibility support for screen readers and keyboard navigation.

Apply this diff to improve accessibility:

       <iframe
         src={metricsURL}
         width="100%"
         height="400"
         frameBorder="0"
-        title="GitHub Metrics"
+        title={`GitHub Metrics for ${username}`}
         className="rounded-lg shadow-md"
         onLoad={() => setLoading(false)}
+        role="img"
+        aria-label={`GitHub statistics and metrics for user ${username}`}
       ></iframe>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<iframe
src={metricsURL}
width="100%"
height="400"
frameBorder="0"
title="GitHub Metrics"
className="rounded-lg shadow-md"
onLoad={() => setLoading(false)}
></iframe>
</div>
<iframe
src={metricsURL}
width="100%"
height="400"
frameBorder="0"
title={`GitHub Metrics for ${username}`}
className="rounded-lg shadow-md"
onLoad={() => setLoading(false)}
role="img"
aria-label={`GitHub statistics and metrics for user ${username}`}
></iframe>
</div>
🤖 Prompt for AI Agents
In src/components/MetricCard.tsx around lines 17 to 26, the iframe lacks
accessibility features for screen readers and keyboard users. Add a meaningful
aria-label attribute to the iframe describing its content, and ensure the title
attribute is descriptive and unique. This will improve screen reader support and
keyboard navigation for the iframe element.

🛠️ Refactor suggestion

Add security attributes and improve error handling for the iframe.

The iframe lacks important security attributes and error handling mechanisms.

Apply this diff to enhance security and error handling:

+  const [error, setError] = useState(false);
+
   return (
     <div className="w-full flex flex-col items-center p-4">
       {loading && <p className="text-gray-500 mb-2">Loading metrics...</p>}
+      {error && <p className="text-red-500 mb-2">Failed to load metrics. Please try again later.</p>}
+      {!error && (
         <iframe
           src={metricsURL}
           width="100%"
           height="400"
           frameBorder="0"
           title="GitHub Metrics"
           className="rounded-lg shadow-md"
           onLoad={() => setLoading(false)}
+          onError={() => {
+            setLoading(false);
+            setError(true);
+          }}
+          sandbox="allow-scripts allow-same-origin"
+          loading="lazy"
         ></iframe>
+      )}
     </div>
   );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<iframe
src={metricsURL}
width="100%"
height="400"
frameBorder="0"
title="GitHub Metrics"
className="rounded-lg shadow-md"
onLoad={() => setLoading(false)}
></iframe>
</div>
const [error, setError] = useState(false);
return (
<div className="w-full flex flex-col items-center p-4">
{loading && <p className="text-gray-500 mb-2">Loading metrics...</p>}
{error && <p className="text-red-500 mb-2">Failed to load metrics. Please try again later.</p>}
{!error && (
<iframe
src={metricsURL}
width="100%"
height="400"
frameBorder="0"
title="GitHub Metrics"
className="rounded-lg shadow-md"
onLoad={() => setLoading(false)}
onError={() => {
setLoading(false);
setError(true);
}}
sandbox="allow-scripts allow-same-origin"
loading="lazy"
></iframe>
)}
</div>
);
🤖 Prompt for AI Agents
In src/components/MetricCard.tsx around lines 17 to 26, the iframe element is
missing key security attributes like sandbox and allow attributes to restrict
its capabilities, and it lacks error handling for load failures. Add appropriate
sandbox attributes to limit iframe permissions, include the allow attribute if
needed, and implement an onError handler to manage loading errors gracefully,
such as setting an error state or displaying a fallback UI.

);
};

export default MetricCard;
5 changes: 5 additions & 0 deletions src/pages/ContributorProfile/ContributorProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
import MetricCard from "../../components/MetricCard";

type PR = {
title: string;
Expand Down Expand Up @@ -68,6 +69,10 @@ export default function ContributorProfile() {
</button>
</div>

{/* GitHub Metrics Preview */}
<h3 className="text-xl font-semibold mt-6 mb-2">GitHub Metrics</h3>
<MetricCard username={username || ""} />

<h3 className="text-xl font-semibold mt-6 mb-2">Pull Requests</h3>
{prs.length > 0 ? (
<ul className="list-disc ml-6 space-y-2">
Expand Down