Improved link validation and sanatization, in order to mitigate CVE-2025-56200 from validator.js NPM package.

This commit is contained in:
rainbow napkin 2025-10-18 07:21:17 -04:00
parent 6bab5b4723
commit 06f552a9ec
9 changed files with 38 additions and 19 deletions

View file

@ -3,8 +3,10 @@
"version": "0.4", "version": "0.4",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"dependencies": { "dependencies": {
"@braintree/sanitize-url": "^7.1.1",
"altcha": "^1.0.7", "altcha": "^1.0.7",
"altcha-lib": "^1.2.0", "altcha-lib": "^1.2.0",
"argon2": "^0.44.0",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"bootstrap-icons": "^1.11.3", "bootstrap-icons": "^1.11.3",
"connect-mongo": "^5.1.0", "connect-mongo": "^5.1.0",
@ -16,7 +18,7 @@
"hls.js": "^1.6.2", "hls.js": "^1.6.2",
"mongoose": "^8.4.3", "mongoose": "^8.4.3",
"node-cron": "^3.0.3", "node-cron": "^3.0.3",
"nodemailer": "^6.9.16", "nodemailer": "^7.0.9",
"socket.io": "^4.8.1", "socket.io": "^4.8.1",
"youtube-dl-exec": "^3.0.20" "youtube-dl-exec": "^3.0.20"
}, },
@ -26,7 +28,7 @@
"build": "node node_modules/jsdoc/jsdoc.js --verbose -r src/ -R README.md -d www/doc/server/ && node node_modules/jsdoc/jsdoc.js --verbose -r www/js/channel -r README.md -d www/doc/client/" "build": "node node_modules/jsdoc/jsdoc.js --verbose -r src/ -R README.md -d www/doc/server/ && node node_modules/jsdoc/jsdoc.js --verbose -r www/js/channel -r README.md -d www/doc/client/"
}, },
"devDependencies": { "devDependencies": {
"nodemon": "^3.1.10", "jsdoc": "^4.0.4",
"jsdoc": "^4.0.4" "nodemon": "^3.1.10"
} }
} }

View file

@ -120,12 +120,12 @@ class playlistHandler{
*/ */
async addToPlaylistValidator(socket, url){ async addToPlaylistValidator(socket, url){
//If we where given a bad URL //If we where given a bad URL
if(typeof url != 'string' || !validator.isURL(url)){ if(typeof url != 'string' || !validator.isURL(url,{require_valid_protocol: true})){
//Attempt to fix the situation by encoding it //Attempt to fix the situation by encoding it
url = encodeURI(url); url = encodeURI(url);
//If it's still bad //If it's still bad
if(typeof url != 'string' || !validator.isURL(url)){ if(typeof url != 'string' || !validator.isURL(url,{require_valid_protocol: true})){
//Bitch, moan, complain... //Bitch, moan, complain...
loggerUtils.socketErrorHandler(socket, "Bad URL!", "validation"); loggerUtils.socketErrorHandler(socket, "Bad URL!", "validation");
//and ignore it! //and ignore it!

View file

@ -132,12 +132,12 @@ class queue{
let url = data.url; let url = data.url;
//If we where given a bad URL //If we where given a bad URL
if(!validator.isURL(url)){ if(!validator.isURL(url,{require_valid_protocol: true})){
//Attempt to fix the situation by encoding it //Attempt to fix the situation by encoding it
url = encodeURI(url); url = encodeURI(url);
//If it's still bad //If it's still bad
if(!validator.isURL(url)){ if(!validator.isURL(url,{require_valid_protocol: true})){
//Bitch, moan, complain... //Bitch, moan, complain...
loggerUtils.socketErrorHandler(socket, "Bad URL!", "validation"); loggerUtils.socketErrorHandler(socket, "Bad URL!", "validation");
//and ignore it! //and ignore it!

View file

@ -16,6 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//NPM Imports //NPM Imports
const validator = require('validator');//No express here, so regular validator it is! const validator = require('validator');//No express here, so regular validator it is!
const {sanitizeUrl} = require("@braintree/sanitize-url");
//Create link cache //Create link cache
/** /**
@ -25,10 +26,12 @@ module.exports.cache = new Map();
/** /**
* Validates links and returns a marked link object that can be returned to the client to format/embed accordingly * Validates links and returns a marked link object that can be returned to the client to format/embed accordingly
* @param {String} link - URL to Validate * @param {String} dirtyLink - URL to Validate
* @returns {Object} Marked link object * @returns {Object} Marked link object
*/ */
module.exports.markLink = async function(link){ module.exports.markLink = async function(dirtyLink){
const link = sanitizeUrl(dirtyLink);
//Check link cache for the requested link //Check link cache for the requested link
const cachedLink = module.exports.cache.get(link); const cachedLink = module.exports.cache.get(link);
@ -44,7 +47,7 @@ module.exports.markLink = async function(link){
var type = "malformedLink" var type = "malformedLink"
//Make sure we have an actual, factual URL //Make sure we have an actual, factual URL
if(validator.isURL(link)){ if(validator.isURL(link,{require_valid_protocol: true, protocols: ['http', 'https']})){
//The URL is valid, so this is at least a dead link //The URL is valid, so this is at least a dead link
type = 'deadLink'; type = 'deadLink';

View file

@ -173,7 +173,7 @@ module.exports.errorMiddleware = function(err, req, res, next){
* @param {Error} err - error to dump to file * @param {Error} err - error to dump to file
* @param {Date} date - Date of error, defaults to now * @param {Date} date - Date of error, defaults to now
*/ */
module.exports.dumpError = async function(err, date = new Date(), subDir){ module.exports.dumpError = async function(err, date = new Date(), subDir = ''){
try{ try{
//Crash directory //Crash directory
const dir = `./log/crash/${subDir}` const dir = `./log/crash/${subDir}`

View file

@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//NPM Imports //NPM Imports
//const url = require("node:url"); //const url = require("node:url");
const validator = require('validator');//No express here, so regular validator it is! const validator = require('validator');//No express here, so regular validator it is!
const {sanitizeUrl} = require("@braintree/sanitize-url");
//local import //local import
const iaUtil = require('./internetArchiveUtils'); const iaUtil = require('./internetArchiveUtils');
@ -96,12 +97,15 @@ module.exports.refreshRawLink = async function(mediaObj){
* Still this has some improvements like url pre-checks and the fact that it's handled serverside, recuing possibility of bad requests. * Still this has some improvements like url pre-checks and the fact that it's handled serverside, recuing possibility of bad requests.
* Some of the regex expressions for certain services have also been improved, such as youtube, and the fore.st-unique archive.org * Some of the regex expressions for certain services have also been improved, such as youtube, and the fore.st-unique archive.org
* *
* @param {String} url - URL to determine media type of * @param {String} dirtyURL - URL to determine media type of
* @returns {Object} containing URL type and clipped ID string * @returns {Object} containing URL type and clipped ID string
*/ */
module.exports.getMediaType = async function(url){ module.exports.getMediaType = async function(dirtyURL){
//Sanatize our URL
const url = sanitizeUrl(dirtyURL);
//Check if we have a valid url, encode it on the fly in case it's too humie-friendly //Check if we have a valid url, encode it on the fly in case it's too humie-friendly
if(!validator.isURL(encodeURI(url))){ if(!validator.isURL(encodeURI(url,{require_valid_protocol: true}))){
//If not toss the fucker out //If not toss the fucker out
return { return {
type: null, type: null,

View file

@ -16,6 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//NPM Imports //NPM Imports
const { checkSchema } = require('express-validator'); const { checkSchema } = require('express-validator');
const {sanitizeUrl} = require("@braintree/sanitize-url");
//local imports //local imports
const {isRank} = require('./permissionsValidator'); const {isRank} = require('./permissionsValidator');
@ -99,11 +100,15 @@ module.exports.img = function(field = 'img'){
isURL: { isURL: {
options: { options: {
require_tld: false, require_tld: false,
require_host: false require_host: false,
require_valid_protocol: true
}, },
errorMessage: "Invalid URL." errorMessage: "Invalid URL."
}, },
trim: true trim: true,
customSanitizer: {
options: sanitizeUrl
}
} }
}); });
} }

View file

@ -83,7 +83,11 @@ module.exports.settingsMap = function(){
}, },
'settingsMap.streamURL': { 'settingsMap.streamURL': {
optional: true, optional: true,
isURL: true, isURL: {
options:{
require_valid_protocol: true
}
},
errorMessage: "Invalid Stream URL" errorMessage: "Invalid Stream URL"
} }
}) })

View file

@ -48,7 +48,8 @@ module.exports.link = function(field = 'link'){
isURL: { isURL: {
options: { options: {
require_tld: false, require_tld: false,
require_host: false require_host: false,
require_valid_protocol: true
}, },
errorMessage: "Invalid URL." errorMessage: "Invalid URL."
}, },
@ -76,7 +77,7 @@ module.exports.manualLink = function(input){
const clean = validator.trim(input) const clean = validator.trim(input)
//If we have a URL return the trimmed input //If we have a URL return the trimmed input
if(validator.isURL(clean)){ if(validator.isURL(clean,{require_valid_protocol: true})){
return clean; return clean;
} }