Fortify user data path checks

ff8b029c9dc92d0f553eaf9dfecf117a49a4b53e

Cohee <18619528+Cohee1207@users.noreply.github.com>

1 files changed, +16 -5Ignore whitespace
src/users.js+16 -5
@@ -16,7 +16,7 @@ import { sync as writeFileAtomicSync } from 'write-file-atomic';
16import sanitize from 'sanitize-filename';16import sanitize from 'sanitize-filename';
1717
18import { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, SETTINGS_FILE, UPLOADS_DIRECTORY } from './constants.js';18import { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, SETTINGS_FILE, UPLOADS_DIRECTORY } from './constants.js';
19import { getConfigValue, color, delay, generateTimestamp, invalidateFirefoxCache } from './util.js';19import { getConfigValue, color, delay, generateTimestamp, invalidateFirefoxCache, isPathUnderParent } from './util.js';
20import { readSecret, writeSecret } from './endpoints/secrets.js';20import { readSecret, writeSecret } from './endpoints/secrets.js';
21import { getContentOfType } from './endpoints/content-manager.js';21import { getContentOfType } from './endpoints/content-manager.js';
22import { serverDirectory } from './server-directory.js';22import { serverDirectory } from './server-directory.js';
@@ -948,7 +948,11 @@ function createRouteHandler(directoryFn) {
948 try {948 try {
949 const directory = directoryFn(req);949 const directory = directoryFn(req);
950 const filePath = decodeURIComponent(req.params[0]);950 const filePath = decodeURIComponent(req.params[0]);
951 const exists = fs.existsSync(path.join(directory, filePath));951 const fullPath = path.join(directory, filePath);
952 if (!isPathUnderParent(directory, path.resolve(fullPath))) {
953 return res.sendStatus(403);
954 }
955 const exists = fs.existsSync(fullPath);
952 if (!exists) {956 if (!exists) {
953 return res.sendStatus(404);957 return res.sendStatus(404);
954 }958 }
@@ -971,13 +975,20 @@ function createExtensionsRouteHandler(directoryFn) {
971 try {975 try {
972 const directory = directoryFn(req);976 const directory = directoryFn(req);
973 const filePath = decodeURIComponent(req.params[0]);977 const filePath = decodeURIComponent(req.params[0]);
974978 const localPath = path.join(directory, filePath);
975 const existsLocal = fs.existsSync(path.join(directory, filePath));979 if (!isPathUnderParent(directory, path.resolve(localPath))) {
980 return res.sendStatus(403);
981 }
982 const existsLocal = fs.existsSync(localPath);
976 if (existsLocal) {983 if (existsLocal) {
977 return res.sendFile(filePath, { root: directory });984 return res.sendFile(filePath, { root: directory });
978 }985 }
979986
980 const existsGlobal = fs.existsSync(path.join(PUBLIC_DIRECTORIES.globalExtensions, filePath));987 const globalPath = path.join(PUBLIC_DIRECTORIES.globalExtensions, filePath);
988 if (!isPathUnderParent(PUBLIC_DIRECTORIES.globalExtensions, path.resolve(globalPath))) {
989 return res.sendStatus(403);
990 }
991 const existsGlobal = fs.existsSync(globalPath);
981 if (existsGlobal) {992 if (existsGlobal) {
982 return res.sendFile(filePath, { root: PUBLIC_DIRECTORIES.globalExtensions });993 return res.sendFile(filePath, { root: PUBLIC_DIRECTORIES.globalExtensions });
983 }994 }