Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/server/services/api/api-i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ export const saveMissing = (req, res) => {
const lng = req.params.lng;
const ns = req.params.ns;

// Validate and sanitize the input to prevent path traversal
if (!/^[a-zA-Z0-9_-]+$/.test(lng) || !/^[a-zA-Z0-9_-]+$/.test(ns)) {
res.status(400).send('Invalid language or namespace');
return;
}

const mergedFile = path.join(settings.assets.app.path, 'i18n', lng, `${ns}.json`);
const mergedObject = JSON.parse(fs.readFileSync(mergedFile, 'utf8'));

Expand Down
71 changes: 53 additions & 18 deletions src/server/services/api/api-profile-definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ export const getRawDefinition = (req, res) => {
}

const filename = `${definitionId}.def.json`;
const configDir = `${DataStorage.configDir}/${configPath}/${filename}`;
const configDir = path.resolve(DataStorage.configDir, configPath, filename);
if (!configDir.startsWith(DataStorage.configDir)) {
res.status(ERR_BAD_REQUEST).send({ err: 'Invalid path.' });
return;
}

try {
const readFileSync = fs.readFileSync(configDir);
const parse = JSON.parse(readFileSync);
Expand Down Expand Up @@ -121,8 +126,13 @@ export const createDefinition = async (req, res) => {
definitionLoader.fromObject(definition);
const configPath = isPublicProfile(definitionLoader.definitionId) ? headType : (req.body.configPath ?? '');

const filePath = path.join(`${DataStorage.configDir}/${configPath}`, `${definitionLoader.definitionId}.def.json`);
const backupPath = path.join(`${DataStorage.activeConfigDir}/${configPath}`, `${definitionLoader.definitionId}.def.json`);
const filePath = path.resolve(DataStorage.configDir, configPath, `${definitionLoader.definitionId}.def.json`);
const backupPath = path.resolve(DataStorage.activeConfigDir, configPath, `${definitionLoader.definitionId}.def.json`);
if (!filePath.startsWith(DataStorage.configDir) || !backupPath.startsWith(DataStorage.activeConfigDir)) {
res.status(ERR_BAD_REQUEST).send({ err: 'Invalid path.' });
return;
}

const data = JSON.stringify(definitionLoader.toJSON(), null, 2);
if (!fs.existsSync(backupPath)) {
try {
Expand Down Expand Up @@ -171,10 +181,15 @@ export const updateDefaultDefinition = (req, res) => {

let filePath = '';
if (isPublicProfile(definitionId)) {
filePath = path.join(`${DataStorage.defaultConfigDir}/${headType}`, `${definitionId}.def.json`);
filePath = path.resolve(DataStorage.defaultConfigDir, headType, `${definitionId}.def.json`);
} else {
filePath = path.join(`${DataStorage.defaultConfigDir}/${configPath}`, `${definitionId}.def.json`);
filePath = path.resolve(DataStorage.defaultConfigDir, configPath, `${definitionId}.def.json`);
}
if (!filePath.startsWith(DataStorage.defaultConfigDir)) {
res.status(ERR_BAD_REQUEST).send({ err: 'Invalid path.' });
return;
}

const data = JSON.stringify(definitionLoader.toJSON(), null, 2);
fs.writeFile(filePath, data, 'utf8', (err) => {
if (err) {
Expand All @@ -194,7 +209,12 @@ export const createTmpDefinition = (req, res) => {
definitionLoader.fromObject(definition);

const uploadName = `${filename ?? definitionLoader.definitionId}.def.json`;
const filePath = path.join(`${DataStorage.tmpDir}`, uploadName);
const filePath = path.resolve(DataStorage.tmpDir, uploadName);
if (!filePath.startsWith(DataStorage.tmpDir)) {
res.status(ERR_BAD_REQUEST).send({ err: 'Invalid path.' });
return;
}

fs.writeFile(filePath, JSON.stringify(definitionLoader.toJSON(), null, 2), 'utf8', (err) => {
if (err) {
log.error(err);
Expand All @@ -212,8 +232,13 @@ export const removeDefinition = (req, res) => {
const { definitionId } = req.params;
const configPath = req.body.configPath;

const filePath = path.join(`${DataStorage.configDir}/${configPath}`, `${definitionId}.def.json`);
const backupPath = path.join(`${DataStorage.activeConfigDir}/${configPath}`, `${definitionId}.def.json`);
const filePath = path.resolve(DataStorage.configDir, configPath, `${definitionId}.def.json`);
const backupPath = path.resolve(DataStorage.activeConfigDir, configPath, `${definitionId}.def.json`);
if (!filePath.startsWith(DataStorage.configDir) || !backupPath.startsWith(DataStorage.activeConfigDir)) {
res.status(ERR_BAD_REQUEST).send({ err: 'Invalid path.' });
return;
}

fs.unlink(filePath, (err) => {
if (err) {
log.error(err);
Expand Down Expand Up @@ -270,12 +295,17 @@ export const updateDefinition = async (req, res) => {
let filePath = '';
let activeRecoverPath = '';
if (isPublicProfile(definitionId)) {
filePath = path.join(`${DataStorage.configDir}/${headType}`, `${definitionId}.def.json`);
activeRecoverPath = path.join(`${DataStorage.activeConfigDir}/${headType}`, `${definitionId}.def.json`);
filePath = path.resolve(DataStorage.configDir, headType, `${definitionId}.def.json`);
activeRecoverPath = path.resolve(DataStorage.activeConfigDir, headType, `${definitionId}.def.json`);
} else {
filePath = path.join(`${DataStorage.configDir}/${configPath}`, `${definitionId}.def.json`);
activeRecoverPath = path.join(`${DataStorage.activeConfigDir}/${configPath}`, `${definitionId}.def.json`);
filePath = path.resolve(DataStorage.configDir, configPath, `${definitionId}.def.json`);
activeRecoverPath = path.resolve(DataStorage.activeConfigDir, configPath, `${definitionId}.def.json`);
}
if (!filePath.startsWith(DataStorage.configDir) || !activeRecoverPath.startsWith(DataStorage.activeConfigDir)) {
res.status(ERR_BAD_REQUEST).send({ err: 'Invalid path.' });
return;
}

if (!fs.existsSync(DataStorage.activeConfigDir)) {
try {
await fs.copy(DataStorage.configDir, DataStorage.activeConfigDir);
Expand Down Expand Up @@ -308,7 +338,7 @@ const isSourceFormDefault = (obj) => {
export const uploadDefinition = (req, res) => {
const { headType } = req.params;
const { definitionId, uploadName, configPath } = req.body;
const readFileSync = fs.readFileSync(`${DataStorage.tmpDir}/${uploadName}`, 'utf-8');
const readFileSync = fs.readFileSync(path.resolve(DataStorage.tmpDir, uploadName), 'utf-8');
let obj;
try {
obj = JSON.parse(readFileSync);
Expand All @@ -322,7 +352,7 @@ export const uploadDefinition = (req, res) => {
obj = {};
}

if (!obj.inherits || !fs.existsSync(`${DataStorage.configDir}/${headType}/${obj.inherits}.json`)) {
if (!obj.inherits || !fs.existsSync(path.resolve(DataStorage.configDir, headType, `${obj.inherits}.json`))) {
obj.inherits = 'snapmaker2';
}

Expand All @@ -334,8 +364,13 @@ export const uploadDefinition = (req, res) => {
const definitionLoader = new DefinitionLoader();
try {
definitionLoader.loadJSON(headType, definitionId, obj);
const filePath = path.join(`${DataStorage.configDir}/${configPath}`, `${definitionId}.def.json`);
const backupPath = path.join(`${DataStorage.activeConfigDir}/${configPath}`, `${definitionId}.def.json`);
const filePath = path.resolve(DataStorage.configDir, configPath, `${definitionId}.def.json`);
const backupPath = path.resolve(DataStorage.activeConfigDir, configPath, `${definitionId}.def.json`);
if (!filePath.startsWith(DataStorage.configDir) || !backupPath.startsWith(DataStorage.activeConfigDir)) {
res.status(ERR_BAD_REQUEST).send({ err: 'Invalid path.' });
return;
}

const data = JSON.stringify(definitionLoader.toJSON(), null, 2);
const callback = () => {
fsWriteFile(backupPath, data, res, (err) => {
Expand Down Expand Up @@ -366,15 +401,15 @@ export const getParameterDoc = (req, res) => {
const langDir = lang.toUpperCase() === 'ZH-CN' ? 'CN' : lang.toUpperCase();

const fileRelativePath = `${langDir}/${category}/${key}.md`;
const filePath = `${DataStorage.getParameterDocumentDir()}/${fileRelativePath}`;
const filePath = path.resolve(DataStorage.getParameterDocumentDir(), fileRelativePath);

let content;
if (fs.existsSync(filePath)) {
content = fs.readFileSync(`${filePath}`, 'utf-8');
} else if (lang !== 'en') {
log.info(`Request: "${fileRelativePath}"\nNo documentation was found for the user's language ${lang}. An English version was given.`);

const filePathEN = `${DataStorage.getParameterDocumentDir()}/EN/${category}/${key}.md`;
const filePathEN = path.resolve(DataStorage.getParameterDocumentDir(), 'EN', category, `${key}.md`);
content = fs.readFileSync(filePathEN, 'utf-8');
}

Expand Down
2 changes: 1 addition & 1 deletion src/server/services/api/api-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const get = (req, res) => {
}

const value = config.get(key);
res.send(value);
res.json(value); // Changed from res.send(value) to res.json(value)
};

export const unset = (req, res) => {
Expand Down
16 changes: 13 additions & 3 deletions src/server/services/api/api-svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ export const convertRasterToSvg = async (req, res) => {
// options: { filename, vectorThreshold, invert, turdSize }
const options = req.body;
const result = await convertRaster(options);
res.send(result);
res.send(escapeHtml(result)); // Escape the result before sending
};

export const convertTextToSvg = async (req, res) => {
// options: { text, font, size, lineHeight, alignment, pathType, fillDensity }
const options = req.body;
try {
const result = await convertText(options);
res.send(result);
res.send(escapeHtml(result)); // Escape the result before sending
} catch (e) {
log.error(`Fail to convert text to SVG: ${e}`);
res.status(ERR_INTERNAL_SERVER_ERROR).send({
Expand All @@ -29,5 +29,15 @@ export const convertOneLineTextToSvg = async (req, res) => {
// options: { text, font, name, size, sourceWidth, sourceHeight }
const options = req.body;
const result = await convertOneLineText(options);
res.send(result);
res.send(escapeHtml(result)); // Escape the result before sending
};

// Helper function to escape HTML
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}