security: vulnerability fixes & security hardening (2026-03-12) #1
@@ -12,7 +12,7 @@ const securityHeadersMiddleware = (req, res, next) => {
|
|||||||
'Content-Security-Policy',
|
'Content-Security-Policy',
|
||||||
[
|
[
|
||||||
"default-src 'self'",
|
"default-src 'self'",
|
||||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://code.jquery.com https://cdnjs.cloudflare.com https://maxcdn.bootstrapcdn.com https://cdn.jsdelivr.net https://cdn.tailwindcss.com",
|
"script-src 'self' 'unsafe-inline' https://code.jquery.com https://cdnjs.cloudflare.com https://maxcdn.bootstrapcdn.com https://cdn.jsdelivr.net https://cdn.tailwindcss.com",
|
||||||
"style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com",
|
"style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com",
|
||||||
"img-src 'self' data: https: blob:",
|
"img-src 'self' data: https: blob:",
|
||||||
"font-src 'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com",
|
"font-src 'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@cdn-app/insider-myaxrin-labs-dinawo",
|
"name": "@cdn-app/insider-myaxrin-labs-dinawo",
|
||||||
"version": "1.2.1-beta",
|
"version": "1.2.2-beta",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const child_process = require('child_process');
|
|
||||||
const packageJson = require('../package.json');
|
const packageJson = require('../package.json');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
@@ -64,7 +63,7 @@ router.get('/', async (req, res) => {
|
|||||||
res.json(buildMetadata);
|
res.json(buildMetadata);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in /build-metadata: ', error);
|
console.error('Error in /build-metadata: ', error);
|
||||||
res.status(500).send('Error in /build-metadata: ' + error.toString());
|
res.status(500).send('Internal server error');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,16 @@ router.post('/wallpaper', (req, res) => {
|
|||||||
return res.status(400).send('No wallpaper URL provided.');
|
return res.status(400).send('No wallpaper URL provided.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate URL to prevent XSS/CSS injection via malicious URLs
|
||||||
|
try {
|
||||||
|
const parsed = new URL(wallpaperUrl);
|
||||||
|
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
||||||
|
return res.status(400).send('Invalid URL protocol. Only HTTP/HTTPS allowed.');
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return res.status(400).send('Invalid URL format.');
|
||||||
|
}
|
||||||
|
|
||||||
updateUserWallpaper(userId, wallpaperUrl, res);
|
updateUserWallpaper(userId, wallpaperUrl, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -129,9 +129,9 @@ function authenticateToken(req, res, next) {
|
|||||||
return res.status(401).json({ message: 'Unauthorized' });
|
return res.status(401).json({ message: 'Unauthorized' });
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.readFile(path.join(__dirname, '../../../data', 'user.jso,'), 'utf8', (err, data) => {
|
fs.readFile(path.join(__dirname, '../../../data', 'user.json'), 'utf8', (err, data) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('Error reading user.jso,:', err);
|
console.error('Error reading user.json:', err);
|
||||||
return res.status(401).json({ message: 'Unauthorized' });
|
return res.status(401).json({ message: 'Unauthorized' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ function authenticateToken(req, res, next) {
|
|||||||
return res.status(401).json({ message: 'Unauthorized' });
|
return res.status(401).json({ message: 'Unauthorized' });
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.readFile(path.join(__dirname, '../../../data', 'user.jso,'), 'utf8', (err, data) => {
|
fs.readFile(path.join(__dirname, '../../../data', 'user.json'), 'utf8', (err, data) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('Error reading user.json:', err);
|
console.error('Error reading user.json:', err);
|
||||||
return res.status(401).json({ message: 'Unauthorized' });
|
return res.status(401).json({ message: 'Unauthorized' });
|
||||||
|
|||||||
@@ -13,6 +13,16 @@ router.post('/', (req, res) => {
|
|||||||
return res.status(400).send('No profile picture URL provided.');
|
return res.status(400).send('No profile picture URL provided.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate URL to prevent XSS/injection via malicious URLs
|
||||||
|
try {
|
||||||
|
const parsed = new URL(profilePictureUrl);
|
||||||
|
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
||||||
|
return res.status(400).send('Invalid URL protocol. Only HTTP/HTTPS allowed.');
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return res.status(400).send('Invalid URL format.');
|
||||||
|
}
|
||||||
|
|
||||||
updateUserProfilePicture(userId, profilePictureUrl, res);
|
updateUserProfilePicture(userId, profilePictureUrl, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ function authenticateToken(req, res, next) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.readFile(path.join(__dirname, '../../../data', 'user.jso,'), 'utf8', (err, data) => {
|
fs.readFile(path.join(__dirname, '../../../data', 'user.json'), 'utf8', (err, data) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('Error reading user.json:', err);
|
console.error('Error reading user.json:', err);
|
||||||
return res.status(401).json({ message: 'Unauthorized' });
|
return res.status(401).json({ message: 'Unauthorized' });
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ router.post('/', (req, res) => {
|
|||||||
const filename = fields.filename ? fields.filename[0] : file.originalFilename;
|
const filename = fields.filename ? fields.filename[0] : file.originalFilename;
|
||||||
const filePath = path.join(userDir, filename);
|
const filePath = path.join(userDir, filename);
|
||||||
|
|
||||||
|
// Path traversal protection: ensure resolved path stays within user directory
|
||||||
|
const resolvedUserDir = path.resolve(process.cwd(), 'cdn-files', userName);
|
||||||
|
const resolvedFilePath = path.resolve(filePath);
|
||||||
|
if (!resolvedFilePath.startsWith(resolvedUserDir + path.sep) && resolvedFilePath !== resolvedUserDir) {
|
||||||
|
if (fs.existsSync(file.path)) fs.unlinkSync(file.path);
|
||||||
|
return res.status(403).send('Path traversal detected');
|
||||||
|
}
|
||||||
|
|
||||||
// Récupérer les champs supplémentaires
|
// Récupérer les champs supplémentaires
|
||||||
const expiryDate = fields.expiryDate ? fields.expiryDate[0] : '';
|
const expiryDate = fields.expiryDate ? fields.expiryDate[0] : '';
|
||||||
const password = fields.password ? fields.password[0] : '';
|
const password = fields.password ? fields.password[0] : '';
|
||||||
|
|||||||
Reference in New Issue
Block a user