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
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 2024-05-10 - Secure API Service Endpoints against HTTP Parameter Pollution and Path Traversal
**Vulnerability:** Dynamic variables were interpolated directly into template literals for API endpoints without encoding, which could allow malicious users to inject special characters to manipulate the URL path (Path Traversal) or add additional query parameters (HTTP Parameter Pollution).
**Learning:** It is crucial to always encode user-supplied or dynamic input when constructing URLs, even for internal API calls, to prevent injection attacks that alter the intended request structure.
**Prevention:** Use `encodeURIComponent()` consistently for all dynamic path segments and query parameters within template literals when building API requests.
30 changes: 15 additions & 15 deletions src/services/apiService.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,49 @@ import api from './api';
// Roadmaps API
export const roadmapsApi = {
getAll: () => api.get('/roadmaps'),
getById: (id) => api.get(`/roadmaps/${id}`),
getById: (id) => api.get(`/roadmaps/${encodeURIComponent(id)}`),
};

// Topics API
export const topicsApi = {
getByRoadmapId: (roadmapId) => api.get(`/topics/roadmap/${roadmapId}`),
getById: (id) => api.get(`/topics/${id}`),
getByRoadmapId: (roadmapId) => api.get(`/topics/roadmap/${encodeURIComponent(roadmapId)}`),
getById: (id) => api.get(`/topics/${encodeURIComponent(id)}`),
};

// Lessons API
export const lessonsApi = {
getByTopicId: (topicId) => api.get(`/lessons/topic/${topicId}`),
getById: (id) => api.get(`/lessons/${id}`),
getByTopicId: (topicId) => api.get(`/lessons/topic/${encodeURIComponent(topicId)}`),
getById: (id) => api.get(`/lessons/${encodeURIComponent(id)}`),
};

// Progress API
export const progressApi = {
getAll: () => api.get('/progress'),
getByRoadmapId: (roadmapId) => api.get(`/progress/roadmap/${roadmapId}`),
getByRoadmapId: (roadmapId) => api.get(`/progress/roadmap/${encodeURIComponent(roadmapId)}`),
markComplete: (lessonId) => api.post('/progress/complete', { lessonId }),
markIncomplete: (lessonId) => api.post('/progress/incomplete', { lessonId }),
};

// Notes API
export const notesApi = {
getAll: () => api.get('/notes'),
getByLessonId: (lessonId) => api.get(`/notes/lesson/${lessonId}`),
getByLessonId: (lessonId) => api.get(`/notes/lesson/${encodeURIComponent(lessonId)}`),
create: (data) => api.post('/notes', data),
update: (id, data) => api.put(`/notes/${id}`, data),
delete: (id) => api.delete(`/notes/${id}`),
update: (id, data) => api.put(`/notes/${encodeURIComponent(id)}`, data),
delete: (id) => api.delete(`/notes/${encodeURIComponent(id)}`),
};

// Comments API
export const commentsApi = {
getByLessonId: (lessonId) => api.get(`/comments/lesson/${lessonId}`),
getByLessonId: (lessonId) => api.get(`/comments/lesson/${encodeURIComponent(lessonId)}`),
create: (data) => api.post('/comments', data),
update: (id, data) => api.put(`/comments/${id}`, data),
delete: (id) => api.delete(`/comments/${id}`),
update: (id, data) => api.put(`/comments/${encodeURIComponent(id)}`, data),
delete: (id) => api.delete(`/comments/${encodeURIComponent(id)}`),
};

// Search API
export const searchApi = {
search: (query) => api.get(`/search?q=${query}`),
search: (query) => api.get(`/search?q=${encodeURIComponent(query)}`),
};

// Auth API
Expand All @@ -65,7 +65,7 @@ export const aboutApi = {
};

export const contactApi = {
send: (data) => Promise.resolve({ data: { success: true } }),
send: (_data) => Promise.resolve({ data: { success: true } }),
};

export const documentariesApi = {
Expand All @@ -78,5 +78,5 @@ export const forumsApi = {

export const postsApi = {
getAll: () => Promise.resolve({ data: [] }),
getById: (id) => Promise.resolve({ data: {} }),
getById: (_id) => Promise.resolve({ data: {} }),
};