From 58110628915192926603ba4a5f0f9c10066943b6 Mon Sep 17 00:00:00 2001 From: Young Yu Date: Wed, 17 Jan 2024 18:25:56 -0800 Subject: [PATCH] create path validation for file paths --- CHANGELOG.md | 3 +++ package.json | 3 ++- pnpm-lock.yaml | 8 ++++++++ src/simple-app-config.ts | 25 +++++++++++++++++++++++++ tests/simple-app-config.test.ts | 23 +++++++++++++++++++++++ 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4835505..6b4a421 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 1.1.0 - 1/17/24 +Create input validation check for paths, where the input must not be outside the root dir of the project. + ## 1.0.8 - 1/17/24 Fix package.json diff --git a/package.json b/package.json index d73bd99..3d53ee6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "simple-app-config", - "version": "1.0.8", + "version": "1.1.0", "description": "A simple configuration manager for different environments", "license": "MIT", "repository": { @@ -54,6 +54,7 @@ ], "author": "Young Yu", "dependencies": { + "app-root-path": "^3.1.0", "dotenv": "^16.3.1" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a3b169..8a52d0d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + app-root-path: + specifier: ^3.1.0 + version: 3.1.0 dotenv: specifier: ^16.3.1 version: 16.3.1 @@ -1259,6 +1262,11 @@ packages: picomatch: 2.3.1 dev: true + /app-root-path@3.1.0: + resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} + engines: {node: '>= 6.0.0'} + dev: false + /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true diff --git a/src/simple-app-config.ts b/src/simple-app-config.ts index e11f5b6..e3b13a8 100644 --- a/src/simple-app-config.ts +++ b/src/simple-app-config.ts @@ -14,6 +14,7 @@ import { UnsupportedTypeError } from './errors/unsupportedTypeError'; import { UndefinedConfigValueError } from './errors/undefinedConfigValueError'; import EnvParser from './utils/envParser'; import StringUtil from './utils/stringUtil'; +import appRootPath from 'app-root-path'; /** * Customization options when running {@link Config.configure} @@ -342,14 +343,32 @@ export class Config { /** * Checks if a .env file is valid. A .env file is valid if all the below are true: + * - It is a safe path, meaning its prefix is the root of the project * - It exists * @param path The path of the .env file to check. * @returns A boolean indicating whether the .env file is valid. */ private static isValidEnvFile(path: string): boolean { + /* Return false if path is safe */ + if (!Config.isSafePath(path)) { + return false; + } + + /* Return whether the path exists */ return fs.existsSync(path); } + /** + * Returns whether the path is safe. A path is safe if: + * - Its prefix begins with the absolute path to the root of the project + * @param path + */ + private static isSafePath(filePath: string): boolean { + const root = appRootPath.path; + const resolvedPath = path.resolve(filePath); + return resolvedPath.startsWith(root); + } + /** * Resets and values set previously by dotenv to undefined from the previous call of dotenv.config * @param Path to the .env file. @@ -418,6 +437,7 @@ export class Config { /** * Checks if a config file is valid. A config file is valid if all the below are true: + * - It is a safe path, meaning its prefix is the root of the project * - It exists * - It isn't a directory * - It matches a valid file extension @@ -425,6 +445,11 @@ export class Config { * @returns A boolean indicating whether the config file is valid. */ private static isValidConfigFile(filePath: string): boolean { + /* Check if path is safe */ + if (!Config.isSafePath(filePath)) { + return false; + } + /* Return false if the path doesn't exist */ if (!fs.existsSync(filePath)) { return false; diff --git a/tests/simple-app-config.test.ts b/tests/simple-app-config.test.ts index 777fa07..5ef8556 100644 --- a/tests/simple-app-config.test.ts +++ b/tests/simple-app-config.test.ts @@ -179,6 +179,29 @@ describe('simple-app-config Tests', () => { expect(numberVal).toBe(3); }); + /* Test unsafe .env path */ + it('should not load the .env file if the path is unsafe', () => { + /* Set up */ + process.env[EnvArgs.ConfigPath] = `${__dirname}/config/default.json`; + process.env[EnvArgs.EnvPath] = `../../../..`; + Config.configure({ force: true }); + + /* Compare against expected */ + const env = Config.get('ENV'); + expect(env).toBe('default'); + }); + + /* Test unsafe config path */ + it('should not load the .env file if the path is unsafe', () => { + /* Set up */ + process.env[EnvArgs.ConfigPath] = `../../../..`; + process.env[EnvArgs.EnvPath] = `${__dirname}/config/.env.testing`; + Config.configure({ force: true }); + + /* Compare against expected */ + expect(Config.get('ENV')).toBe('default'); + }); + /* Test configuring twice without forcing */ it('should do nothing if configure is called a second time.', () => { /* Set up spies */