/* * Copyright (C) 2024 Puter Technologies Inc. * * This file is part of Puter. * * Puter is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ /* ~~~ Filesystem validation ~~~ This module contains functions that validate filesystem operations. */ /* eslint-disable no-control-regex */ const config = require("../config"); const path_excludes = () => /[\x00-\x1F]/g; const node_excludes = () => /[/\x00-\x1F]/g; // this characters are not allowed in path names because // they might be used to trick the user into thinking // a filename is different from what it actually is. const safety_excludes = [ /[\u202A-\u202E]/, // RTL and LTR override /[\u200E-\u200F]/, // RTL and LTR mark /[\u2066-\u2069]/, // RTL and LTR isolate /[\u2028-\u2029]/, // line and paragraph separator /[\uFF01-\uFF5E]/, // fullwidth ASCII /[\u2060]/, // word joiner /[\uFEFF]/, // zero width no-break space /[\uFFFE-\uFFFF]/, // non-characters ]; const is_valid_node_name = function is_valid_node_name (name) { if ( typeof name !== 'string' ) return false; if ( node_excludes().test(name) ) return false; for ( const exclude of safety_excludes ) { if ( exclude.test(name) ) return false; } if ( name.length > config.max_fsentry_name_length ) return false; // Names are allowed to contain dots, but cannot // contain only dots. (this covers '.' and '..') const name_without_dots = name.replace(/\./g, ''); if ( name_without_dots.length < 1 ) return false; return true; } const is_valid_path = function is_valid_path (path, { no_relative_components, allow_path_fragment, } = {}) { if ( typeof path !== 'string' ) return false; if ( path.length < 1 ) false; if ( path_excludes().test(path) ) return false; for ( const exclude of safety_excludes ) { if ( exclude.test(path) ) return false; } if ( ! allow_path_fragment ) if ( path[0] !== '/' && path[0] !== '.' ) { return false; } if ( no_relative_components ) { const components = path.split('/'); for ( const component of components ) { if ( component === '' ) continue; const name_without_dots = component.replace(/\./g, ''); if ( name_without_dots.length < 1 ) return false; } } return true; } module.exports = { is_valid_node_name, is_valid_path, };