Skip to content

Commit 37e592f

Browse files
author
Rajat
committed
CodeQL fixes
1 parent 48a790a commit 37e592f

3 files changed

Lines changed: 70 additions & 9 deletions

File tree

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,14 +181,31 @@ export async function POST(
181181

182182
function setNestedValue(obj: any, path: string, value: unknown): void {
183183
const parts = path.split(".");
184+
185+
const isUnsafeKey = (key: string): boolean => {
186+
return (
187+
key === "__proto__" || key === "constructor" || key === "prototype"
188+
);
189+
};
190+
184191
let current = obj;
185192
for (let i = 0; i < parts.length - 1; i++) {
186193
const part = parts[i];
194+
if (isUnsafeKey(part)) {
195+
// Prevent prototype pollution by ignoring unsafe path segments
196+
return;
197+
}
187198
if (current[part] === undefined) {
188199
const nextPart = parts[i + 1];
189200
current[part] = /^\d+$/.test(nextPart) ? [] : {};
190201
}
191202
current = current[part];
192203
}
193-
current[parts[parts.length - 1]] = value;
204+
const lastPart = parts[parts.length - 1];
205+
if (isUnsafeKey(lastPart)) {
206+
// Prevent prototype pollution on final assignment
207+
return;
208+
}
209+
210+
current[lastPart] = value;
194211
}

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

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,17 +309,41 @@ function getNestedValue(obj: any, path: string): unknown {
309309
return path.split(".").reduce((curr, key) => curr?.[key], obj);
310310
}
311311

312+
// Guard against prototype pollution by blocking dangerous keys
313+
function isUnsafeKey(key: string): boolean {
314+
return key === "__proto__" || key === "constructor" || key === "prototype";
315+
}
316+
312317
// Helper to set nested value in object
313318
function setNestedValue(obj: any, path: string, value: unknown): void {
314319
const parts = path.split(".");
315-
let current = obj;
320+
let current: any = obj;
321+
316322
for (let i = 0; i < parts.length - 1; i++) {
317-
if (current[parts[i]] === undefined) {
318-
current[parts[i]] = {};
323+
const part = parts[i];
324+
325+
// Prevent prototype pollution via unsafe keys
326+
if (isUnsafeKey(part)) {
327+
return;
328+
}
329+
330+
if (current[part] === undefined || current[part] === null) {
331+
current[part] = {};
332+
}
333+
334+
current = current[part];
335+
if (typeof current !== "object") {
336+
// Cannot safely nest further into non-object
337+
return;
319338
}
320-
current = current[parts[i]];
321339
}
322-
current[parts[parts.length - 1]] = value;
340+
341+
const lastPart = parts[parts.length - 1];
342+
if (isUnsafeKey(lastPart)) {
343+
return;
344+
}
345+
346+
current[lastPart] = value;
323347
}
324348

325349
export default ScormViewer;

apps/web/lib/scorm/cache.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,31 @@ async function extractZipToDisk(
127127
await fs.mkdir(extractDir, { recursive: true });
128128

129129
// Extract all files
130+
const resolvedExtractDir = path.resolve(extractDir);
130131
for (const entry of zip.getEntries()) {
131132
if (!entry.isDirectory) {
132-
const targetPath = path.join(extractDir, entry.entryName);
133-
await fs.mkdir(path.dirname(targetPath), { recursive: true });
134-
await fs.writeFile(targetPath, new Uint8Array(entry.getData()));
133+
const targetPath = path.join(resolvedExtractDir, entry.entryName);
134+
const resolvedTargetPath = path.resolve(targetPath);
135+
136+
// Prevent Zip Slip / directory traversal: ensure target stays within extractDir
137+
if (
138+
resolvedTargetPath !== resolvedExtractDir &&
139+
!resolvedTargetPath.startsWith(resolvedExtractDir + path.sep)
140+
) {
141+
error("Skipping suspicious ZIP entry path during extraction", {
142+
entryName: entry.entryName,
143+
targetPath: resolvedTargetPath,
144+
});
145+
continue;
146+
}
147+
148+
await fs.mkdir(path.dirname(resolvedTargetPath), {
149+
recursive: true,
150+
});
151+
await fs.writeFile(
152+
resolvedTargetPath,
153+
new Uint8Array(entry.getData()),
154+
);
135155
}
136156
}
137157
}

0 commit comments

Comments
 (0)