-
Notifications
You must be signed in to change notification settings - Fork 235
fix: path Traversal #10447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
fix: path Traversal #10447
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR refactors locale template path resolution and configuration management to improve type safety and security. The main changes include adding a new resolveLocaleTemplatePath utility that validates template paths and sanitizes locale configuration values to prevent invalid locales from being stored.
- Introduced
resolveLocaleTemplatePathfunction for secure, locale-aware template path resolution with fallback support - Added
sanitizeConfigValuefunction to validate and normalize locale configuration values at persistence boundaries - Updated all email template path constructions to use the new secure resolution method
Reviewed Changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/app/src/server/util/locale-utils.ts | Added new resolveLocaleTemplatePath and coerceToSupportedLang functions with path traversal protection and locale validation |
| apps/app/src/server/service/global-notification/global-notification-mail.js | Updated to use async resolveLocaleTemplatePath for template resolution |
| apps/app/src/server/service/config-manager/config-manager.ts | Integrated sanitizeConfigValue to normalize locale values before database persistence |
| apps/app/src/server/service/config-manager/config-loader.ts | Added sanitizeConfigValue function and applied sanitization during config loading from environment and database |
| apps/app/src/server/service/config-manager/config-definition.ts | Changed app:globalLang type from string to Lang enum |
| apps/app/src/server/routes/login.js | Migrated from direct path construction to resolveLocaleTemplatePath |
| apps/app/src/server/routes/apiv3/users.js | Migrated from direct path construction to resolveLocaleTemplatePath |
| apps/app/src/server/routes/apiv3/user-activation.ts | Migrated from direct path construction to resolveLocaleTemplatePath |
| apps/app/src/server/routes/apiv3/forgot-password.js | Migrated from direct path construction to resolveLocaleTemplatePath, improved error message security |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative); | ||
| }; | ||
|
|
||
| const doesTemplateExist = async(candidatePath: string, baseDir: string): Promise<boolean> => { |
Copilot
AI
Oct 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space between 'async' and the opening parenthesis. Should be async (candidatePath instead of async(candidatePath.
| return Array.from(locales).map(locale => path.resolve(resolvedBase, locale, ...segments)); | ||
| }; | ||
|
|
||
| // Guard functions that collapse to undefined or default values for further processing. |
Copilot
AI
Oct 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected 'functions' to 'function' since only one function follows this comment.
| // Guard functions that collapse to undefined or default values for further processing. | |
| // Guard function that collapses to undefined or default values for further processing. |
|
|
||
| const isPathInsideBase = (basePath: string, targetPath: string): boolean => { | ||
| const relative = path.relative(basePath, targetPath); | ||
| return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative); |
Copilot
AI
Oct 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for isPathInsideBase is incorrect. When basePath and targetPath are the same, path.relative() returns an empty string, and the function returns false. However, a path pointing to the base directory itself could be considered valid. More importantly, if targetPath equals basePath, the condition relative !== '' will fail. Consider whether this edge case is intentional or if the logic should be relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative)).
| return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative); | |
| return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative)); |
タスク : https://redmine.weseek.co.jp/issues/171275