Conversation
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
I am requesting changes because the current server implementation in src/createServer.js does not fully meet several core requirements and has security-relevant flaws.
Critical issues that must be fixed:
- Incorrect
/fileand/file/handling (around the logic where you normalize the pathname):/file/is currently not reliably mapped topublic/index.html, and the normalization can leave you with'/'instead ofindex.html. Per the spec, both/fileand/file/must returnpublic/index.html. A practical fix is to explicitly checkpathname === '/file' || pathname === '/file/'and, in those cases, serveindex.html. - Overly permissive route check (where you use
pathname.startsWith('/file')): this allows paths like/fileX, which are not valid according to the requirement that only/file,/file/, and paths starting with/file/should be served. Update this to something likepathname === '/file' || pathname === '/file/' || pathname.startsWith('/file/')so that only valid routes are processed. - Path traversal and escaping
public(in thepath.join/fs.readFilesection): directly joining a segment that may start with'/'or contain..allows access outsidepublic, breaking the “Return only files from a folder public” requirement. You should strip any leading/from the requested subpath, then usepath.resolve(publicDir, requestedPath)and verify that the resolved path starts with thepublicDirpath; if it doesn’t, return a 404. - Directory handling and validation before reading: currently you call
fs.readFilewithout checking whether the resolved path is a directory or an invalid location. After resolving the path and confirming it’s insidepublic, if it resolves to a directory (or to'/'after normalization), you should servepublic/index.html; if anything fails validation, respond with 404 as required. - Hint message for non-
/filepaths: when the pathname does not match the allowed/filepatterns, the requirement asks for a clear hint about how to load files. Instead of a generic or ambiguous message, return something explicit likeUse /file/<path> to load files, and only use this branch for truly invalid routes (after tightening the prefix check above).
Minor improvements (not blockers): you always respond with text/plain, which is functional but not ideal for HTML/CSS; consider a simple content-type mapping if you want to polish the solution. Overall, your structure and error handling are on the right track; once you tighten the route checks, correctly map /file and /file/, and secure the path resolution against traversal, your implementation will align well with the task requirements.
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Important
Fix the issues, commit the changes, and then re-request my review using the Re-request review button2 in the Reviewers tab.
Tip
If you have any comments about this review, please leave a message in the Add a comment section below and mention me with the @mateacademy-ai-mentor tag3.
I will read your message during my next review of your pull request. Please do not reply directly to my comments, as that feature is not supported yet.
Caution
Do not close the pull request until the review is complete. Closing it early can lead to unexpected behavior and disrupt the review process.
Footnotes
src/createServer.js
Outdated
| // Return instance of http.Server class | ||
| const server = http.createServer((req, res) => { | ||
| const { pathname } = new URL(req.url, `http://${req.headers.host}`); | ||
| const normalizedPathWay = pathname.replace('/file', '') || 'index.html'; |
There was a problem hiding this comment.
The normalization on this line doesn't reliably map '/file/' to 'index.html' and may leave a leading '/' (e.g. '/file/' -> '/'). Also replace('/file', '') will replace any occurrence, not just the leading segment. This breaks the requirement that /file/ and /file return public/index.html. Consider explicitly handling pathname === '/file' and pathname === '/file/' (map them to 'index.html'), or use a regex such as pathname.replace(/^\/file(?=\/|$)/, '') and then treat a resulting '/' or empty string as '/index.html'.
src/createServer.js
Outdated
| const server = http.createServer((req, res) => { | ||
| const { pathname } = new URL(req.url, `http://${req.headers.host}`); | ||
| const normalizedPathWay = pathname.replace('/file', '') || 'index.html'; | ||
| const pathToFile = path.join(__dirname, '..', 'public', normalizedPathWay); |
There was a problem hiding this comment.
Joining using a segment that may start with '/' or contain .. can produce absolute paths or allow path traversal (access outside public). Remove any leading slash from the normalized segment before joining (e.g. strip leading '/') and then normalize and validate the final path. After resolving, ensure the resolved pathToFile starts with the public directory path to enforce the requirement "Return only files from a folder public".
|
|
||
| res.setHeader('content-type', 'text/plain'); | ||
|
|
||
| if (!pathname.startsWith('/file')) { |
There was a problem hiding this comment.
Checking pathname.startsWith('/file') is too permissive: it will accept paths like '/fileX' which should not be considered valid. Per requirements you should allow /file, /file/, and any path that starts with /file/. Use an explicit condition such as pathname === '/file' || pathname === '/file/' || pathname.startsWith('/file/').
src/createServer.js
Outdated
| res.statusCode = 400; | ||
| res.end('Routes not starting with /file/'); |
There was a problem hiding this comment.
The response message and status here are unclear for the user hint requirement. The task requires returning a message with a hint how to load files when the pathname does not start with /file/. Return a clearer hint (for example: Use /file/<path> to load files, or Use /file/ to load files) and keep the status consistent with your API design. Also ensure this branch is only taken for truly invalid routes (see previous comment about startsWith).
src/createServer.js
Outdated
| return; | ||
| } | ||
|
|
||
| fs.readFile(pathToFile, 'utf-8', (err, file) => { |
There was a problem hiding this comment.
Reading the file without validating pathToFile allows serving files outside public (path traversal) and will not correctly handle directory requests (e.g. when normalized value is '/' or points to a directory). Before fs.readFile, validate the resolved path is within the public directory and, if the path resolves to a directory, serve public/index.html. If validation fails, return 404 per requirements.
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
I am requesting changes because there is still a critical security/requirements issue with the path containment logic in src/createServer.js. Around the check that uses resolvedPath.startsWith(publicDir) (look near where you decide whether to serve the file or return 404), this approach can incorrectly treat some sibling paths as inside public, which violates the requirement that there must be no access to files outside the public directory ([CHECKLIST ITEM #2], [#3], [#9], [#10]). To fix this robustly, either:
- Compute
const rel = path.relative(publicDir, resolvedPath)and return 404 whenrel.startsWith('..')orpath.isAbsolute(rel), or - Require
resolvedPath === publicDir || resolvedPath.startsWith(publicDir + path.sep)before serving the file, otherwise return 404.
This change is required because without it a crafted request could escape thepublicfolder while still being served by your server, breaking the core constraint of the exercise. Everything else about your routing and 404 handling for missing files looks solid, so once you harden this containment check your solution should fully meet the task requirements. Keep up the good work—this is a subtle, real-world security edge case and fixing it will significantly strengthen your implementation.
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Important
Fix the issues, commit the changes, and then re-request my review using the Re-request review button2 in the Reviewers tab.
Tip
If you have any comments about this review, please leave a message in the Add a comment section below and mention me with the @mateacademy-ai-mentor tag3.
I will read your message during my next review of your pull request. Please do not reply directly to my comments, as that feature is not supported yet.
Caution
Do not close the pull request until the review is complete. Closing it early can lead to unexpected behavior and disrupt the review process.
Footnotes
src/createServer.js
Outdated
| const resolvedPath = path.resolve(publicDir, subPath); | ||
|
|
||
| if (!resolvedPath.startsWith(publicDir)) { |
There was a problem hiding this comment.
Security issue: the containment check uses resolvedPath.startsWith(publicDir) which can produce false positives (for example if a sibling path begins with the same prefix). This can allow access outside public, violating checklist items #2/#3/#9/#10. Use a robust test instead — for example:
- Compute
const rel = path.relative(publicDir, resolvedPath)and reject whenrel.startsWith('..')orpath.isAbsolute(rel); or - Require
resolvedPath === publicDir || resolvedPath.startsWith(publicDir + path.sep).
Make this change so any path resolving outside public returns 404.
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
I am approving your solution as an educational override so you can keep progressing while you refine your server implementation.
You’ve done a solid job setting up the basic HTTP server, parsing the URL, and wiring fs.readFile to serve files under public, which shows good understanding of the core flow. The main issues to address next are: (1) when relativeFilePath is empty (requests to /file or /file/), your code currently returns a 'File Not Found' message instead of serving public/index.html — you can fix this by treating an empty path as 'index.html' before reading the file (around the branch where you check for an empty path). (2) You are building file paths with string concatenation like public/${relativeFilePath}; this allows path traversal (e.g. /file/../secret), so you should import path and use path.resolve plus a path.relative containment check to ensure the resolved path stays inside the public directory before calling fs.readFile. (3) The route guard using pathname.startsWith('/file') is too permissive and will accept /fileABC; instead, explicitly allow only '/file', '/file/', or paths starting with '/file/' and return the hint message when the pathname does not match these patterns.
As smaller improvements, your pathname.includes('//') check is okay as a light sanity check, but don’t rely on it for security once you have proper path.resolve containment in place, and make sure missing files consistently return a 404 status instead of a 200. Overall, you’re very close to a robust solution — your structure is good, and tightening these routing and security details will bring your implementation fully in line with the requirements.
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Footnotes
| if (!relativeFilePath) { | ||
| res.writeHead(200, { 'Content-Type': 'text/plain' }); | ||
| res.end('File Not Found'); | ||
|
|
||
| return; |
There was a problem hiding this comment.
When relativeFilePath is empty (requests to /file or /file/) the handler returns 'File Not Found'. The spec requires /file and /file/ to return public/index.html. Change this branch to serve public/index.html (or default relativeFilePath to 'index.html' earlier) instead of returning this error.
| return; | ||
| } | ||
|
|
||
| fs.readFile(`public/${relativeFilePath}`, (err, data) => { |
There was a problem hiding this comment.
Reading public/${relativeFilePath} directly allows path traversal (for example /file/../secret). Import and use path to resolve and validate the filesystem path: compute publicDir = path.resolve(__dirname, '..', 'public'), resolvedPath = path.resolve(publicDir, relativeFilePath || 'index.html'), then verify containment using either const rel = path.relative(publicDir, resolvedPath) and return 404 when rel.startsWith('..') || path.isAbsolute(rel), or require resolvedPath === publicDir || resolvedPath.startsWith(publicDir + path.sep). Only then call fs.readFile(resolvedPath, ...). Also add const path = require('path') at the top.
| if (!pathname.startsWith('/file')) { | ||
| res.writeHead(400, { 'Content-Type': 'text/plain' }); | ||
| res.end('Should be /file/*'); | ||
|
|
||
| return; |
There was a problem hiding this comment.
This check !pathname.startsWith('/file') is too permissive: it treats /fileABC as a valid route. Instead accept exactly /file, /file/ or paths that start with /file/ (e.g. if (!(pathname === '/file' || pathname === '/file/' || pathname.startsWith('/file/')))). The spec requires the handler to detect when pathname does not start with /file/ and return a hint message.
| if (pathname.includes('//')) { | ||
| res.writeHead(404, { 'Content-Type': 'text/plain' }); | ||
| res.end('File Not Found'); | ||
|
|
||
| return; |
There was a problem hiding this comment.
The pathname.includes('//') check is not sufficient protection against crafted paths (e.g. .. segments). It's fine to keep as a simple sanity check, but do not rely on it for security — the path.resolve + path.relative containment check is required to guarantee no access outside public.
No description provided.