diff --git a/services/code.service.js b/services/code.service.js index 07eed2b3..c22a4b17 100644 --- a/services/code.service.js +++ b/services/code.service.js @@ -23,6 +23,7 @@ const axios = require('axios') const supportedLanguages = require('../enums/supportedLanguages') const { generate } = require('@builder.io/sqlgenerate') const parser = require('sqlite-parser') +const crypto = require('crypto') const _runScript = async (cmd, res, runMemoryCheck = false) => { let initialMemory = 0 @@ -802,6 +803,24 @@ const _preCleanUp = async () => { } } +const _checkIntegrity = async (non_editable_files) => { + for (const [filePath, expectedHash] of Object.entries(non_editable_files)) { + try { + const fullPath = path.join(appConfig.multifile.workingDir, filePath) + const fileContent = await fs.promises.readFile(fullPath) + const actualHash = crypto.createHash('sha256').update(fileContent).digest('hex') + if (actualHash !== expectedHash) { + logger.warn(`Integrity check failed for file: ${filePath}`) + return false + } + } catch (error) { + logger.error(`Error reading file ${filePath}: ${error.message}`) + return false + } + } + return true +} + const _executeMultiFile = async (req, res, response) => { logger.info(`serving ${req.type}`) try { @@ -815,6 +834,10 @@ const _executeMultiFile = async (req, res, response) => { try { let jasmineResults + if(req?.non_editable_files) { + const isValidSubmission = await _checkIntegrity(req.non_editable_files) + if(!isValidSubmission) throw new Error(`A non editable file has been modified, exiting...`) + } if (req.type === FRONTEND_STATIC_JASMINE) { const staticServerInstance = await _startStaticServer(appConfig.multifile.staticServerPath) jasmineResults = await _runTests() diff --git a/validators/code.validator.js b/validators/code.validator.js index bb7b8f75..09920a5d 100644 --- a/validators/code.validator.js +++ b/validators/code.validator.js @@ -39,6 +39,10 @@ const isValidForExecute = async (body) => { then: Joi.required(), otherwise: Joi.forbidden(), }), + non_editable_files: Joi.object().pattern( + Joi.string(), + Joi.string().pattern(/^[a-fA-F0-9]{64}$/) + ).optional(), points: Joi.number().integer().optional(), // totalScore hasInputFiles: Joi.bool(), args: Joi.string(),