-
Notifications
You must be signed in to change notification settings - Fork 51
Fix path traversal in profile operations #55
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,13 @@ const HOME_DIR = os.homedir(); | |
| const OPENCODE_DIR = path.join(HOME_DIR, '.config', 'opencode'); | ||
| const PROFILES_DIR = path.join(HOME_DIR, '.config', 'opencode-profiles'); | ||
|
|
||
| function safeName(name) { | ||
| if (!name || name.includes('/') || name.includes('\\') || name.includes('..')) { | ||
| throw new Error('Invalid profile name'); | ||
| } | ||
| return name; | ||
| } | ||
|
Comment on lines
+9
to
+14
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
ArtifactsRepro: Node.js harness that invokes deleteProfile dot with an isolated HOME
Repro: verbose harness output showing profiles root deletion
Prompt To Fix With AIThis is a comment left during a code review.
Path: server/profile-manager.js
Line: 9-14
Comment:
**Reject dot profile names**
`safeName('.')` currently passes, so `deleteProfile('.')` builds `path.join(PROFILES_DIR, '.')` and then `fs.rmSync` removes the entire profiles directory. This leaves the delete path with a concrete filesystem escape despite rejecting separators and `..`; reject `.` before using the name in `createProfile`, `deleteProfile`, or `activateProfile`.
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| if (!fs.existsSync(PROFILES_DIR)) { | ||
| fs.mkdirSync(PROFILES_DIR, { recursive: true }); | ||
| } | ||
|
|
@@ -51,13 +58,15 @@ function listProfiles() { | |
| } | ||
|
|
||
| function createProfile(name) { | ||
| safeName(name); | ||
| const dir = path.join(PROFILES_DIR, name); | ||
| if (fs.existsSync(dir)) throw new Error('Profile already exists'); | ||
| fs.mkdirSync(dir, { recursive: true }); | ||
| return { success: true }; | ||
| } | ||
|
|
||
| function deleteProfile(name) { | ||
| safeName(name); | ||
|
Comment on lines
68
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift Authorization Bypass (CWE-862): Missing Authorization Reachability: External Require authorization before profile deletion and activation. The supplied Also applies to: 81-82 🤖 Prompt for AI Agents |
||
| const { active } = listProfiles(); | ||
| if (name === active) throw new Error('Cannot delete active profile'); | ||
| if (name === 'default') throw new Error('Cannot delete default profile'); | ||
|
|
@@ -70,6 +79,7 @@ function deleteProfile(name) { | |
| } | ||
|
|
||
| function activateProfile(name) { | ||
| safeName(name); | ||
| const target = path.join(PROFILES_DIR, name); | ||
| if (!fs.existsSync(target)) throw new Error('Profile not found'); | ||
|
|
||
|
|
||
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.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
Path Traversal (CWE-22): Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Reachability: External
Reject
.and enforce path containment.safeName('.')passes validation, butpath.join(PROFILES_DIR, '.')resolves toPROFILES_DIR. Consequently,deleteProfile('.')recursively deletes the entire profiles directory, andactivateProfile('.')can pointOPENCODE_DIRat the profiles root. Reject.and verify the resolved target is a direct child before filesystem operations; add a regression test.🤖 Prompt for AI Agents