Skip to content

Conversation

arvi18
Copy link

@arvi18 arvi18 commented Aug 13, 2025

Summary by CodeRabbit

  • New Features
    • Introduced a Job Description Generator page with a client-side form.
    • Inputs include Job Title, Industry (Technology, Finance, Healthcare, Manufacturing, Retail), Experience Level (Entry, Mid, Senior, Lead), and Job Details.
    • Generate button disables until required fields are filled and shows a “Generating…” state during processing.
    • Displays the generated job description upon completion.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @arvi18, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new page at /generate that provides a user interface for generating job descriptions. Users can input job details such as title, industry, experience level, and specific requirements, which are then sent to a backend API to generate a comprehensive job description. The page handles form state, loading indicators, and displays the generated output.

Highlights

  • New Job Description Generation Page: Adds app/generate/page.tsx to introduce a dedicated page for generating job descriptions.
  • Interactive Form: Implements a client-side React component with a form to capture job title, industry, experience level, and detailed job requirements.
  • API Integration: Integrates with the /api/chat endpoint to send form data and receive the generated job description.
  • Dynamic UI Updates: Manages form state, loading indicators, and displays the generated job description dynamically on the page.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

refacto-test bot commented Aug 13, 2025

Create Job Description Generator Page

TL;DR: Added a new page for generating job descriptions with a form UI and API integration.


Refacto PR Summary

Added a new client-side page for generating job descriptions with form inputs for job details and API integration.
This PR introduces a new /generate route with a React form that collects job details (title, industry, experience level, and job description) from users. When submitted, the form sends data to the /api/chat endpoint with a specific generate_jd type parameter. The generated job description is then displayed to the user in a formatted HTML container. The implementation includes proper form validation, loading states, and responsive UI components styled with Tailwind CSS.

Change Highlights

Click to expand
  • app/generate/page.tsx: New client-side page with job description generation form
  • Form validation: Required fields and disabled submit button when form is incomplete
  • API integration: Structured request to /api/chat endpoint with form data
  • Loading state: Visual feedback during API request processing
  • Result display: Formatted output of the generated job description using dangerouslySetInnerHTML

Sequence Diagram

sequenceDiagram
    participant User
    participant Form as JD Generator Form
    participant API as /api/chat
    participant Response as Generated JD
    
    User->>Form: Fill job details
    User->>Form: Submit form
    Form->>Form: Set loading state
    Form->>API: POST request with job data
    Note over Form,API: {type: 'generate_jd', content: formData}
    API-->>Form: Return generated JD
    Form->>Form: Update result state
    Form->>Response: Display formatted JD
    Response-->>User: View generated job description
Loading

Testing Guide

Click to expand
  1. Navigate to the /generate page and verify the form renders correctly
  2. Fill in all required fields: job title, industry, experience level, and job details
  3. Submit the form and verify loading state appears
  4. Confirm the generated job description appears in the results section
  5. Test form validation by attempting to submit with missing fields
  6. Verify the submit button is disabled when required fields are empty

Copy link

coderabbitai bot commented Aug 13, 2025

Walkthrough

Introduces a client-side React page component (GenerateJD) that renders a form for job description inputs, posts the data to /api/chat with type generate_jd, manages loading and result state, and displays the returned HTML using dangerouslySetInnerHTML.

Changes

Cohort / File(s) Summary
Generate JD Page Component
app/(chat)/generate/page.tsx
New client-side React component with form (title, industry, experience, details), POSTs to /api/chat with { type: 'generate_jd', content: formData }, handles loading/errors, and renders returned HTML result.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant Page as GenerateJD Page
  participant API as /api/chat

  User->>Page: Fill form & submit
  Page->>API: POST { type: "generate_jd", content: formData }
  API-->>Page: JSON { generatedJD }
  Page-->>User: Render generatedJD (HTML) and update UI
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~7 minutes

Poem

I tapped my paws and clicked “Create,”
A JD blooms—how neat, how great!
Fields filled in, the prompt took flight,
The server squeaked back pure delight.
With whisker-twitch and frontend flair,
I ship this change—handle with care! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch clone-patch-6

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new page for generating job descriptions. The overall concept is good, but there are several critical issues that need to be addressed. These include a major security vulnerability (XSS), a broken API call due to a payload mismatch, and a bug in the form's submission logic. I've also included suggestions to improve error handling, code robustness, and accessibility. Please review the comments for detailed feedback.

Comment on lines +20 to +27
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'generate_jd',
content: formData
})
});

Choose a reason for hiding this comment

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

critical

The payload sent to /api/chat does not seem to match the structure expected by the API route handler in app/(chat)/api/chat/route.ts. The API expects an object with id, messages, and modelId, but this component sends { type: 'generate_jd', content: formData }. This mismatch will likely cause the API call to fail with an error. Please ensure the client and server are aligned on the API contract.

<div className="mt-6 p-4 bg-gray-50 rounded-lg">
<h2 className="text-lg font-medium mb-2">Generated Job Description:</h2>
<div className="prose max-w-none">
<div dangerouslySetInnerHTML={{ __html: result }} />

Choose a reason for hiding this comment

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

critical

Using dangerouslySetInnerHTML with content that could be influenced by user input (even indirectly via an AI model) creates a significant Cross-Site Scripting (XSS) vulnerability. An attacker could potentially craft inputs that cause the AI to generate malicious HTML, including <script> tags, which would then be executed in the user's browser.

To mitigate this, you must sanitize the HTML string before rendering it. A popular library for this is DOMPurify.

Example usage:

import DOMPurify from 'dompurify';

// ... inside your component
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(result) }} />

Comment on lines +33 to +35
}

setLoading(false);

Choose a reason for hiding this comment

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

high

To prevent the UI from getting stuck in a loading state if an error occurs (e.g., if response.json() fails), the setLoading(false) call should be placed inside a finally block. This ensures it is executed regardless of whether the try block completes successfully or throws an error.

    } finally {
      setLoading(false);
    }


<button
type="submit"
disabled={loading || !formData.title || !formData.industry || !formData.details}

Choose a reason for hiding this comment

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

high

The disabled logic for the submit button is missing a check for formData.experience. Since the 'Experience Level' field is also marked as required, it should be included in this check to ensure the button is only enabled when all required fields are filled.

          disabled={loading || !formData.title || !formData.industry || !formData.experience || !formData.details}

Comment on lines +31 to +33
} catch (error) {
console.error('Error:', error);
}

Choose a reason for hiding this comment

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

medium

The current error handling only catches network errors or errors during JSON parsing. It doesn't handle non-2xx HTTP status codes from the API (e.g., 404, 500), as fetch does not reject on these. You should check response.ok and throw an error if it's false to catch these cases. Additionally, consider adding a user-facing error message state instead of just logging to the console, to improve user experience.

<div className="max-w-4xl mx-auto p-6">
<h1 className="text-2xl font-bold mb-6">Generate Job Description</h1>

<form onSubmit={handleSubmit} className="space-y-6">

Choose a reason for hiding this comment

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

medium

For better accessibility, it's recommended to explicitly associate <label> elements with their form controls using the htmlFor attribute, which should match the id of the control. This helps screen reader users understand which label belongs to which input. This should be applied to all form fields: Job Title, Industry, Experience Level, and Job Details.

For example:

<div>
  <label htmlFor="job-title" className="block font-medium mb-1">Job Title</label>
  <input 
    id="job-title"
    type="text"
    ...
  />
</div>

@arvi18
Copy link
Author

arvi18 commented Aug 13, 2025

/refacto-test

Copy link

refacto-test bot commented Aug 13, 2025

Refacto is reviewing this PR. Please wait for the review comments to be posted.

Copy link

refacto-test bot commented Aug 13, 2025

Code Review: Job Description Generator

👍 Well Done
Form Validation Implementation

Required field validation prevents invalid submissions and unnecessary API calls

Form Structure Organization

Form fields are logically organized with clear labels and validation

📌 Files Processed
  • app/generate/page.tsx
📝 Additional Comments
app/generate/page.tsx (2)
Missing Form Components

Form field rendering is repetitive with duplicated structure for each input. This violates DRY principle and makes form maintenance difficult when adding fields or changing validation logic.

// components/FormField.tsx
interface FormFieldProps {
  label: string;
  value: string;
  onChange: (value: string) => void;
  type?: 'text' | 'select' | 'textarea';
  placeholder?: string;
  required?: boolean;
}

export function FormField({ 
  label, 
  value, 
  onChange, 
  type = 'text',
  placeholder = '',
  required = false
}: FormFieldProps) {
  return (
    <div>
      <label className="block font-medium mb-1">{label}</label>
      {type === 'text' && (
        <input
          type="text"
          value={value}
          onChange={(e) => onChange(e.target.value)}
          className="w-full p-2 border rounded"
          placeholder={placeholder}
          required={required}
        />
      )}
    </div>
  );
}

Standards:

  • Clean-Code-DRY
  • Refactoring-Extract-Component
Missing Loading State Reset

Loading state is reset after error, but result state isn't cleared on error. Failed requests leave stale data visible, causing UI inconsistency and potential confusion under network issues.

30: setResult(data.generatedJD);
31: } catch (error) {
32: console.error('Error:', error);
33: setResult('');
34: }
35: 
36: setLoading(false);

Standards:

  • React-State-Management-Best-Practices
  • ISO-IEC-25010-Performance-User-Experience

<div className="mt-6 p-4 bg-gray-50 rounded-lg">
<h2 className="text-lg font-medium mb-2">Generated Job Description:</h2>
<div className="prose max-w-none">
<div dangerouslySetInnerHTML={{ __html: result }} />
Copy link

Choose a reason for hiding this comment

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

XSS Vulnerability in HTML Rendering

Unsanitized HTML content is rendered directly using dangerouslySetInnerHTML. API responses could contain malicious scripts, enabling cross-site scripting attacks that execute in the user's browser context.

Suggested change
<div dangerouslySetInnerHTML={{ __html: result }} />
import DOMPurify from 'dompurify';
// In the return statement:
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(result) }} />
Standards
  • CWE-79
  • OWASP-A03

Comment on lines +29 to +30
const data = await response.json();
setResult(data.generatedJD);
Copy link

Choose a reason for hiding this comment

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

Missing API Response Error Handling

Code doesn't check if response is successful before parsing JSON and accessing data.generatedJD. Non-successful API responses could cause runtime errors when accessing properties on undefined data, potentially crashing the UI.

Suggested change
const data = await response.json();
setResult(data.generatedJD);
const data = await response.json();
if (!response.ok) {
console.error('API error:', data);
setResult('Error generating job description. Please try again later.');
return;
}
setResult(data.generatedJD || 'No content generated');
Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

Comment on lines +108 to +115
{result && (
<div className="mt-6 p-4 bg-gray-50 rounded-lg">
<h2 className="text-lg font-medium mb-2">Generated Job Description:</h2>
<div className="prose max-w-none">
<div dangerouslySetInnerHTML={{ __html: result }} />
</div>
</div>
)}
Copy link

Choose a reason for hiding this comment

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

Missing Loading Indicator

Loading state is set but not displayed to users. When API request is processing, users receive no visual feedback that their request is being handled, leading to potential multiple submissions and confusion.

Suggested change
{result && (
<div className="mt-6 p-4 bg-gray-50 rounded-lg">
<h2 className="text-lg font-medium mb-2">Generated Job Description:</h2>
<div className="prose max-w-none">
<div dangerouslySetInnerHTML={{ __html: result }} />
</div>
</div>
)}
{loading && (
<div className="mt-6 p-4 bg-gray-50 rounded-lg">
<div className="flex justify-center">
<div className="animate-pulse text-purple-600">Generating job description...</div>
</div>
</div>
)}
{!loading && result && (
<div className="mt-6 p-4 bg-gray-50 rounded-lg">
<h2 className="text-lg font-medium mb-2">Generated Job Description:</h2>
<div className="prose max-w-none">
<div dangerouslySetInnerHTML={{ __html: result }} />
</div>
</div>
)}
Standards
  • Logic-Verification-User-Feedback
  • Business-Rule-User-Experience

Comment on lines +31 to +33
} catch (error) {
console.error('Error:', error);
}
Copy link

Choose a reason for hiding this comment

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

Error State Not Communicated to User

Caught errors are only logged to console without UI feedback. Users have no visibility into failure states, creating confusion when API calls fail silently.

Suggested change
} catch (error) {
console.error('Error:', error);
}
} catch (error) {
console.error('Error:', error);
setResult('<p class="text-red-500">An error occurred while generating the job description. Please try again later.</p>');
}
Standards
  • ISO-IEC-25010-Reliability-Recoverability
  • ISO-IEC-25010-Functional-Appropriateness

Comment on lines +5 to +20
export default function GenerateJD() {
const [formData, setFormData] = useState({
title: '',
industry: '',
experience: '',
details: ''
});
const [result, setResult] = useState('');
const [loading, setLoading] = useState(false);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);

try {
const response = await fetch('/api/chat', {
Copy link

Choose a reason for hiding this comment

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

Component Responsibility Overload

Component handles multiple responsibilities: form state management, API interaction, and UI rendering. This violates Single Responsibility Principle and makes the component harder to maintain as features are added.

Suggested change
export default function GenerateJD() {
const [formData, setFormData] = useState({
title: '',
industry: '',
experience: '',
details: ''
});
const [result, setResult] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
try {
const response = await fetch('/api/chat', {
// hooks/useJobDescriptionForm.ts
import { useState } from 'react';
export function useJobDescriptionForm() {
const [formData, setFormData] = useState({
title: '',
industry: '',
experience: '',
details: ''
});
const [result, setResult] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const updateField = (field, value) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
return { formData, updateField, result, setResult, loading, setLoading, error, setError };
}
// services/jobDescriptionService.ts
export async function generateJobDescription(formData) {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'generate_jd',
content: formData
})
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
return data.generatedJD;
}
Standards
  • SOLID-SRP
  • Clean-Code-Component-Organization

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
app/generate/page.tsx (4)

99-105: Include experience in the disabled condition for consistent UX.

The button is disabled when title/industry/details are missing but not when experience is empty, despite the field being required.

Apply this diff:

-          disabled={loading || !formData.title || !formData.industry || !formData.details}
+          disabled={loading || !formData.title || !formData.industry || !formData.experience || !formData.details}

43-53: Associate labels with controls and add IDs/names for accessibility.

Labels aren’t linked to inputs, which hampers screen readers and click-to-focus behavior. Add id to controls and htmlFor to labels; include name and appropriate autocomplete.

Apply these diffs:

-          <label className="block font-medium mb-1">Job Title</label>
+          <label htmlFor="title" className="block font-medium mb-1">Job Title</label>
           <input 
             type="text"
+            id="title"
+            name="title"
             value={formData.title}
             onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
             className="w-full p-2 border rounded"
             placeholder="e.g., Senior Software Engineer"
+            autoComplete="off"
             required
           />
-          <label className="block font-medium mb-1">Industry</label>
+          <label htmlFor="industry" className="block font-medium mb-1">Industry</label>
           <select 
+            id="industry"
+            name="industry"
             value={formData.industry}
             onChange={(e) => setFormData(prev => ({ ...prev, industry: e.target.value }))}
             className="w-full p-2 border rounded"
             required
           >
-            <option value="">Select Industry</option>
+            <option value="" disabled>Select Industry</option>
             <option value="Technology">Technology</option>
             <option value="Finance">Finance</option>
             <option value="Healthcare">Healthcare</option>
             <option value="Manufacturing">Manufacturing</option>
             <option value="Retail">Retail</option>
           </select>
-          <label className="block font-medium mb-1">Experience Level</label>
+          <label htmlFor="experience" className="block font-medium mb-1">Experience Level</label>
           <select 
+            id="experience"
+            name="experience"
             value={formData.experience}
             onChange={(e) => setFormData(prev => ({ ...prev, experience: e.target.value }))}
             className="w-full p-2 border rounded"
             required
           >
-            <option value="">Select Experience Level</option>
+            <option value="" disabled>Select Experience Level</option>
             <option value="Entry Level">Entry Level (0-2 years)</option>
             <option value="Mid Level">Mid Level (3-5 years)</option>
             <option value="Senior Level">Senior Level (5+ years)</option>
             <option value="Lead">Lead (7+ years)</option>
           </select>
-          <label className="block font-medium mb-1">Job Details</label>
+          <label htmlFor="details" className="block font-medium mb-1">Job Details</label>
           <textarea 
+            id="details"
+            name="details"
             value={formData.details}
             onChange={(e) => setFormData(prev => ({ ...prev, details: e.target.value }))}
             className="w-full h-32 p-2 border rounded"
             placeholder="Describe the role, responsibilities, and key requirements..."
+            autoComplete="off"
             required
           />

Additionally, consider adding aria-live="polite" to the results container to announce updates:

-        <div className="mt-6 p-4 bg-gray-50 rounded-lg">
+        <div className="mt-6 p-4 bg-gray-50 rounded-lg" aria-live="polite" role="status">

Also applies to: 55-70, 72-86, 88-97


3-3: Import useId if you adopt labeled-control associations.

If you prefer unique IDs generated at runtime instead of hardcoded strings, import useId now and wire id={titleId} etc.

Apply this diff:

-import { useState } from 'react';
+import { useState, useId } from 'react';

Then add below your state hooks:

const titleId = useId();
const industryId = useId();
const experienceId = useId();
const detailsId = useId();

And replace the hardcoded "title", "industry", etc., with these IDs in id/htmlFor.


1-1: Fix path comment to match actual file location.

The header comment says app/(chat)/generate/page.tsx while the file path is app/generate/page.tsx. Update to avoid confusion.

Apply this diff:

-// app/(chat)/generate/page.tsx
+// app/generate/page.tsx
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 020494f and 1d21695.

📒 Files selected for processing (1)
  • app/generate/page.tsx (1 hunks)
🧰 Additional context used
🪛 ast-grep (0.38.6)
app/generate/page.tsx

[warning] 111-111: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test

Comment on lines +15 to +36
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);

try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'generate_jd',
content: formData
})
});

const data = await response.json();
setResult(data.generatedJD);
} catch (error) {
console.error('Error:', error);
}

setLoading(false);
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Harden fetch error handling and ensure loading state resets via finally.

Currently, non-2xx responses will be treated as success and response.json() may throw. Use response.ok and a finally for setLoading(false).

Apply this diff:

   const handleSubmit = async (e: React.FormEvent) => {
     e.preventDefault();
-    setLoading(true);
-    
-    try {
-      const response = await fetch('/api/chat', {
+    setLoading(true);
+    try {
+      const response = await fetch('/api/chat', {
         method: 'POST',
         headers: { 'Content-Type': 'application/json' },
         body: JSON.stringify({
           type: 'generate_jd',
           content: formData
         })
       });
-      
-      const data = await response.json();
-      setResult(data.generatedJD);
-    } catch (error) {
-      console.error('Error:', error);
-    }
-    
-    setLoading(false);
+      if (!response.ok) {
+        const text = await response.text().catch(() => '');
+        throw new Error(`Failed to generate JD (${response.status} ${response.statusText})${text ? `: ${text}` : ''}`);
+      }
+      const data: { generatedJD?: string } = await response.json();
+      setResult(data.generatedJD ?? '');
+    } catch (error) {
+      console.error('Error generating JD:', error);
+      setResult('');
+    } finally {
+      setLoading(false);
+    }
   };
📝 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
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'generate_jd',
content: formData
})
});
const data = await response.json();
setResult(data.generatedJD);
} catch (error) {
console.error('Error:', error);
}
setLoading(false);
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'generate_jd',
content: formData
})
});
if (!response.ok) {
const text = await response.text().catch(() => '');
throw new Error(
`Failed to generate JD (${response.status} ${response.statusText})` +
(text ? `: ${text}` : '')
);
}
const data: { generatedJD?: string } = await response.json();
setResult(data.generatedJD ?? '');
} catch (error) {
console.error('Error generating JD:', error);
setResult('');
} finally {
setLoading(false);
}
};
🤖 Prompt for AI Agents
In app/generate/page.tsx around lines 15 to 36, the submit handler treats
non-2xx fetch responses as success and may leave loading true if an exception
occurs; update the code to check response.ok after fetch and throw an error
(including status and statusText) when not ok, only call await response.json()
on successful responses, and move setLoading(false) into a finally block so
loading is always reset; keep the existing try/catch for logging the error.

Comment on lines +111 to +113
<div className="prose max-w-none">
<div dangerouslySetInnerHTML={{ __html: result }} />
</div>
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Sanitize HTML before injecting to prevent XSS (use DOMPurify).

Rendering API-provided HTML via dangerouslySetInnerHTML without sanitization is an XSS vector. Sanitize the HTML before injection.

Apply this diff to sanitize the HTML at render time:

-            <div dangerouslySetInnerHTML={{ __html: result }} />
+            <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(result) }} />

Add this import at the top of the file:

import DOMPurify from 'isomorphic-dompurify';

Optionally, prefer sending Markdown/plain text from the API and render with a Markdown renderer to avoid dangerouslySetInnerHTML.

To verify there are no other unsafe injections in the repo, run:


🏁 Script executed:

#!/bin/bash
rg -n "dangerouslySetInnerHTML"

Length of output: 177


🏁 Script executed:

#!/bin/bash
rg -n -A3 -B3 "dangerouslySetInnerHTML" app/layout.tsx

Length of output: 211


🏁 Script executed:

#!/bin/bash
rg -n -A3 -B3 "THEME_COLOR_SCRIPT"

Length of output: 676


Sanitize API-provided HTML in app/generate/page.tsx; internal script injection in app/layout.tsx is safe

  • app/generate/page.tsx (line 112): Wrap the result payload in DOMPurify.sanitize(...) before injecting.
  • app/layout.tsx (line 54): THEME_COLOR_SCRIPT is a static, internally-defined constant—not user-controlled—so this use of dangerouslySetInnerHTML does not require sanitization.

Apply the following diff in app/generate/page.tsx:

--- a/app/generate/page.tsx
+++ b/app/generate/page.tsx
@@ -1,6 +1,7 @@
 import React from 'react';
+import DOMPurify from 'isomorphic-dompurify';
 // … other imports

           <div className="prose max-w-none">
-            <div dangerouslySetInnerHTML={{ __html: result }} />
+            <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(result) }} />
           </div>

No changes required in app/layout.tsx; its script injection is from a trusted, hard-coded constant.

🧰 Tools
🪛 ast-grep (0.38.6)

[warning] 111-111: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🤖 Prompt for AI Agents
In app/generate/page.tsx around lines 111-113, the API-provided HTML is injected
directly; wrap the payload in DOMPurify.sanitize(...) before using
dangerouslySetInnerHTML to prevent script injection. Import a DOMPurify
implementation appropriate for your environment (e.g., import DOMPurify from
"isomorphic-dompurify" or "dompurify" for client-only), create a sanitizedHtml
variable = DOMPurify.sanitize(result) and pass that to __html instead of result;
leave app/layout.tsx unchanged as its script is a trusted constant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants