Skip to content

Commit ae94a42

Browse files
committed
Security Fixes
1 parent c6fd88f commit ae94a42

File tree

2 files changed

+306
-93
lines changed

2 files changed

+306
-93
lines changed

app/request/requestpage.tsx

Lines changed: 103 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,63 @@ const RequestPage = () => {
5454
const [showDiscordDialog, setShowDiscordDialog] = useState(false);
5555
const [showAuthDialog, setShowAuthDialog] = useState(false);
5656

57+
const validateForm = () => {
58+
// Title validation
59+
if (!projectRequest.title.trim()) {
60+
return { isValid: false, message: 'Please enter a project title' };
61+
}
62+
if (projectRequest.title.length < 3) {
63+
return { isValid: false, message: 'Title must be at least 3 characters long' };
64+
}
65+
if (projectRequest.title.length > 100) {
66+
return { isValid: false, message: 'Title must be less than 100 characters' };
67+
}
68+
69+
// GitHub URL validation
70+
if (!projectRequest.githubLink.trim()) {
71+
return { isValid: false, message: 'Please enter a GitHub repository URL' };
72+
}
73+
const githubRegex = /^https:\/\/github\.com\/[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+\/?$/;
74+
if (!githubRegex.test(projectRequest.githubLink)) {
75+
return { isValid: false, message: 'Please enter a valid GitHub repository URL (e.g., https://github.com/owner/repo)' };
76+
}
77+
78+
// Description validation
79+
if (!projectRequest.description.trim()) {
80+
return { isValid: false, message: 'Please describe what this project is about' };
81+
}
82+
if (projectRequest.description.length < 10) {
83+
return { isValid: false, message: 'Description must be at least 10 characters long' };
84+
}
85+
if (projectRequest.description.length > 1000) {
86+
return { isValid: false, message: 'Description must be less than 1000 characters' };
87+
}
88+
89+
// Reason validation
90+
if (!projectRequest.reason.trim()) {
91+
return { isValid: false, message: 'Please explain why you think this is a good project' };
92+
}
93+
if (projectRequest.reason.length < 10) {
94+
return { isValid: false, message: 'Reason must be at least 10 characters long' };
95+
}
96+
if (projectRequest.reason.length > 1000) {
97+
return { isValid: false, message: 'Reason must be less than 1000 characters' };
98+
}
99+
100+
return { isValid: true, message: '' };
101+
};
102+
57103
const handleProjectRequest = async () => {
58104
if (!isAuthenticated || !user) {
59105
setShowAuthDialog(true);
60106
return;
61107
}
62108

63-
if (!projectRequest.title.trim() ||
64-
!projectRequest.githubLink.trim() ||
65-
!projectRequest.description.trim() ||
66-
!projectRequest.reason.trim()) {
109+
const validation = validateForm();
110+
if (!validation.isValid) {
67111
setSubmissionStatus({
68112
status: 'error',
69-
message: 'Please fill in all fields before submitting'
113+
message: validation.message
70114
});
71115
return;
72116
}
@@ -109,7 +153,12 @@ const handleFinalSubmit = async () => {
109153
const responseData = await response.json();
110154

111155
if (!response.ok) {
112-
throw new Error(responseData.error || "Failed to submit request");
156+
// Display the specific error message from the API
157+
setSubmissionStatus({
158+
status: "error",
159+
message: responseData.error || "Failed to submit request"
160+
});
161+
return;
113162
}
114163

115164
setSubmissionStatus({
@@ -259,36 +308,59 @@ const handleFinalSubmit = async () => {
259308
</CardHeader>
260309
<CardContent className="space-y-4">
261310
<div className="space-y-4">
262-
<Input
263-
placeholder="Project Title"
264-
value={projectRequest.title}
265-
onChange={(e) => setProjectRequest(prev => ({ ...prev, title: e.target.value }))}
266-
className="bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400"
267-
/>
268-
269-
<div className="flex items-center gap-2">
270-
<Github className="text-white shrink-0" />
311+
<div>
271312
<Input
272-
placeholder="GitHub Link"
273-
value={projectRequest.githubLink}
274-
onChange={(e) => setProjectRequest(prev => ({ ...prev, githubLink: e.target.value }))}
313+
placeholder="Project Title"
314+
value={projectRequest.title}
315+
onChange={(e) => setProjectRequest(prev => ({ ...prev, title: e.target.value }))}
275316
className="bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400"
317+
maxLength={100}
276318
/>
319+
<p className="text-xs text-gray-400 mt-1">
320+
{projectRequest.title.length}/100 characters (min. 3)
321+
</p>
277322
</div>
278323

279-
<Textarea
280-
placeholder="What is this project about?"
281-
value={projectRequest.description}
282-
onChange={(e) => setProjectRequest(prev => ({ ...prev, description: e.target.value }))}
283-
className="bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400 min-h-24"
284-
/>
285-
286-
<Textarea
287-
placeholder="Why do you think this is a good project?"
288-
value={projectRequest.reason}
289-
onChange={(e) => setProjectRequest(prev => ({ ...prev, reason: e.target.value }))}
290-
className="bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400 min-h-24"
291-
/>
324+
<div>
325+
<div className="flex items-center gap-2">
326+
<Github className="text-white shrink-0" />
327+
<Input
328+
placeholder="GitHub Link (e.g., https://github.com/owner/repo)"
329+
value={projectRequest.githubLink}
330+
onChange={(e) => setProjectRequest(prev => ({ ...prev, githubLink: e.target.value }))}
331+
className="bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400"
332+
/>
333+
</div>
334+
<p className="text-xs text-gray-400 mt-1 ml-8">
335+
Must be a valid GitHub repository URL
336+
</p>
337+
</div>
338+
339+
<div>
340+
<Textarea
341+
placeholder="What is this project about? Describe its purpose and main features."
342+
value={projectRequest.description}
343+
onChange={(e) => setProjectRequest(prev => ({ ...prev, description: e.target.value }))}
344+
className="bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400 min-h-24"
345+
maxLength={1000}
346+
/>
347+
<p className="text-xs text-gray-400 mt-1">
348+
{projectRequest.description.length}/1000 characters (min. 10)
349+
</p>
350+
</div>
351+
352+
<div>
353+
<Textarea
354+
placeholder="Why do you think this is a good project? What value does it provide?"
355+
value={projectRequest.reason}
356+
onChange={(e) => setProjectRequest(prev => ({ ...prev, reason: e.target.value }))}
357+
className="bg-slate-800/50 border-slate-700 text-white placeholder:text-gray-400 min-h-24"
358+
maxLength={1000}
359+
/>
360+
<p className="text-xs text-gray-400 mt-1">
361+
{projectRequest.reason.length}/1000 characters (min. 10)
362+
</p>
363+
</div>
292364
</div>
293365

294366
{submissionStatus.status && (

0 commit comments

Comments
 (0)