diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml new file mode 100644 index 0000000..d104769 --- /dev/null +++ b/.codacy/codacy.yaml @@ -0,0 +1,11 @@ +runtimes: + - java@17.0.10 + - node@22.2.0 + - python@3.11.11 +tools: + - eslint@8.57.0 + - lizard@1.17.31 + - pmd@6.55.0 + - pylint@3.3.9 + - semgrep@1.78.0 + - trivy@0.66.0 diff --git a/.codacyignore b/.codacyignore new file mode 100644 index 0000000..b27ca39 --- /dev/null +++ b/.codacyignore @@ -0,0 +1,7 @@ +node_modules/ +backend/bgutil-ytdlp-pot-provider/ +dist/ +build/ +coverage/ +.codacy/ +.git/ diff --git a/backend/src/controllers/settingsController.ts b/backend/src/controllers/settingsController.ts index e89eeaf..6522e34 100644 --- a/backend/src/controllers/settingsController.ts +++ b/backend/src/controllers/settingsController.ts @@ -1,11 +1,12 @@ import bcrypt from "bcryptjs"; +import crypto from "crypto"; import { Request, Response } from "express"; import fs from "fs-extra"; import path from "path"; import { - COLLECTIONS_DATA_PATH, - STATUS_DATA_PATH, - VIDEOS_DATA_PATH, + COLLECTIONS_DATA_PATH, + STATUS_DATA_PATH, + VIDEOS_DATA_PATH, } from "../config/paths"; import { NotFoundError, ValidationError } from "../errors/DownloadErrors"; import { cloudflaredService } from "../services/cloudflaredService"; @@ -323,23 +324,23 @@ export const updateSettings = async ( if (newSettings.cloudflaredTunnelEnabled) { // Determine port const port = process.env.PORT ? parseInt(process.env.PORT) : 5551; - + const shouldRestart = existingSettings.cloudflaredTunnelEnabled; if (shouldRestart) { - // If it was already enabled, we need to restart to apply changes (Token -> No Token, or vice versa) - if (newSettings.cloudflaredToken) { - cloudflaredService.restart(newSettings.cloudflaredToken); - } else { - cloudflaredService.restart(undefined, port); - } + // If it was already enabled, we need to restart to apply changes (Token -> No Token, or vice versa) + if (newSettings.cloudflaredToken) { + cloudflaredService.restart(newSettings.cloudflaredToken); + } else { + cloudflaredService.restart(undefined, port); + } } else { - // It was disabled, now enabling -> just start - if (newSettings.cloudflaredToken) { - cloudflaredService.start(newSettings.cloudflaredToken); - } else { - cloudflaredService.start(undefined, port); - } + // It was disabled, now enabling -> just start + if (newSettings.cloudflaredToken) { + cloudflaredService.start(newSettings.cloudflaredToken); + } else { + cloudflaredService.start(undefined, port); + } } } else { // If disabled, stop @@ -517,13 +518,13 @@ export const resetPassword = async ( _req: Request, res: Response ): Promise => { - // Generate random 8-character password + // Generate random 8-character password using cryptographically secure random const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - let newPassword = ""; - for (let i = 0; i < 8; i++) { - newPassword += chars.charAt(Math.floor(Math.random() * chars.length)); - } + const randomBytes = crypto.randomBytes(8); + const newPassword = Array.from(randomBytes, (byte) => + chars.charAt(byte % chars.length) + ).join(""); // Hash the new password const salt = await bcrypt.genSalt(10); @@ -922,4 +923,3 @@ export const getCloudflaredStatus = async ( const status = cloudflaredService.getStatus(); res.json(status); }; - diff --git a/backend/src/controllers/videoMetadataController.ts b/backend/src/controllers/videoMetadataController.ts index c517eb7..967ad44 100644 --- a/backend/src/controllers/videoMetadataController.ts +++ b/backend/src/controllers/videoMetadataController.ts @@ -6,7 +6,7 @@ import { NotFoundError, ValidationError } from "../errors/DownloadErrors"; import * as storageService from "../services/storageService"; import { logger } from "../utils/logger"; import { successResponse } from "../utils/response"; -import { execFileSafe, validateVideoPath, validateImagePath } from "../utils/security"; +import { execFileSafe, validateImagePath, validateVideoPath } from "../utils/security"; /** * Rate video @@ -63,8 +63,11 @@ export const refreshThumbnail = async ( throw new ValidationError("Video file path not found in record", "video"); } - if (!fs.existsSync(videoFilePath)) { - throw new NotFoundError("Video file", videoFilePath); + // Validate paths to prevent path traversal + const validatedVideoPath = validateVideoPath(videoFilePath); + + if (!fs.existsSync(validatedVideoPath)) { + throw new NotFoundError("Video file", validatedVideoPath); } // Determine thumbnail path on disk @@ -89,11 +92,8 @@ export const refreshThumbnail = async ( } // Ensure directory exists - fs.ensureDirSync(path.dirname(thumbnailAbsolutePath)); - - // Validate paths to prevent path traversal - const validatedVideoPath = validateVideoPath(videoFilePath); const validatedThumbnailPath = validateImagePath(thumbnailAbsolutePath); + fs.ensureDirSync(path.dirname(validatedThumbnailPath)); // Generate thumbnail using execFileSafe to prevent command injection try {