Skip to content

Commit 0314e52

Browse files
author
Rajat
committed
CodeQL fixes 2
1 parent 37e592f commit 0314e52

3 files changed

Lines changed: 36 additions & 2 deletions

File tree

apps/web/app/api/scorm/lesson/[lessonId]/runtime/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,8 @@ function setNestedValue(obj: any, path: string, value: unknown): void {
197197
}
198198
if (current[part] === undefined) {
199199
const nextPart = parts[i + 1];
200-
current[part] = /^\d+$/.test(nextPart) ? [] : {};
200+
// Use Object.create(null) to create objects without prototype chain
201+
current[part] = /^\d+$/.test(nextPart) ? [] : Object.create(null);
201202
}
202203
current = current[part];
203204
}

apps/web/components/public/lesson-viewer/scorm-viewer.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@ function setNestedValue(obj: any, path: string, value: unknown): void {
328328
}
329329

330330
if (current[part] === undefined || current[part] === null) {
331-
current[part] = {};
331+
// Use Object.create(null) to create objects without prototype chain
332+
current[part] = Object.create(null);
332333
}
333334

334335
current = current[part];

apps/web/lib/scorm/cache.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,30 @@ async function fetchZipFromMediaLit(
113113
}
114114
}
115115

116+
/**
117+
* Validate ZIP entry name to prevent directory traversal
118+
*/
119+
function isSafeZipEntryName(entryName: string): boolean {
120+
if (!entryName) return false;
121+
122+
// ZIP specification uses '/' as directory separator; reject backslashes
123+
if (entryName.includes("\\")) return false;
124+
125+
// Reject absolute paths or Windows drive-letter paths (e.g. "C:...").
126+
if (entryName.startsWith("/")) return false;
127+
if (/^[a-zA-Z]:/.test(entryName)) return false;
128+
129+
// Normalize and ensure there are no ".." segments.
130+
const segments = entryName.split("/");
131+
for (const segment of segments) {
132+
if (segment === "..") {
133+
return false;
134+
}
135+
}
136+
137+
return true;
138+
}
139+
116140
/**
117141
* Extract ZIP to disk directory
118142
*/
@@ -130,6 +154,14 @@ async function extractZipToDisk(
130154
const resolvedExtractDir = path.resolve(extractDir);
131155
for (const entry of zip.getEntries()) {
132156
if (!entry.isDirectory) {
157+
// Validate entry name to prevent directory traversal (Zip Slip)
158+
if (!isSafeZipEntryName(entry.entryName)) {
159+
error("Skipping unsafe ZIP entry name during extraction", {
160+
entryName: entry.entryName,
161+
});
162+
continue;
163+
}
164+
133165
const targetPath = path.join(resolvedExtractDir, entry.entryName);
134166
const resolvedTargetPath = path.resolve(targetPath);
135167

0 commit comments

Comments
 (0)