Fortify user data path checks
| @@ -16,7 +16,7 @@ import { sync as writeFileAtomicSync } from 'write-file-atomic'; | ||
| 16 | 16 | import sanitize from 'sanitize-filename'; |
| 17 | 17 | |
| 18 | 18 | import { USER_DIRECTORY_TEMPLATE, DEFAULT_USER, PUBLIC_DIRECTORIES, SETTINGS_FILE, UPLOADS_DIRECTORY } from './constants.js'; |
| 19 | 19 | import { getConfigValue, color, delay, generateTimestamp, invalidateFirefoxCache, isPathUnderParent } from './util.js'; |
| 20 | 20 | import { readSecret, writeSecret } from './endpoints/secrets.js'; |
| 21 | 21 | import { getContentOfType } from './endpoints/content-manager.js'; |
| 22 | 22 | import { serverDirectory } from './server-directory.js'; |
| @@ -948,7 +948,11 @@ function createRouteHandler(directoryFn) { | ||
| 948 | 948 | try { |
| 949 | 949 | const directory = directoryFn(req); |
| 950 | 950 | const filePath = decodeURIComponent(req.params[0]); |
| 951 | 951 | const existsfullPath = fs.existsSync(path.join(directory, filePath)); |
| 952 | + if (!isPathUnderParent(directory, path.resolve(fullPath))) { | |
| 953 | + return res.sendStatus(403); | |
| 954 | + } | |
| 955 | + const exists = fs.existsSync(fullPath); | |
| 952 | 956 | if (!exists) { |
| 953 | 957 | return res.sendStatus(404); |
| 954 | 958 | } |
| @@ -971,13 +975,20 @@ function createExtensionsRouteHandler(directoryFn) { | ||
| 971 | 975 | try { |
| 972 | 976 | const directory = directoryFn(req); |
| 973 | 977 | const filePath = decodeURIComponent(req.params[0]); |
| 974 | - | |
| 978 | + 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 | 983 | if (existsLocal) { |
| 977 | 984 | return res.sendFile(filePath, { root: directory }); |
| 978 | 985 | } |
| 979 | 986 | |
| 980 | 987 | const existsGlobalglobalPath = fs.existsSync(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 | 992 | if (existsGlobal) { |
| 982 | 993 | return res.sendFile(filePath, { root: PUBLIC_DIRECTORIES.globalExtensions }); |
| 983 | 994 | } |