Potential fix for code scanning alert no. 636: URL redirection from remote source#7760
Potential fix for code scanning alert no. 636: URL redirection from remote source#7760sriramveeraghanta merged 1 commit intopreviewfrom
Conversation
…emote source Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
WalkthroughThe path validator in apps/api/plane/utils/path_validator.py was updated to sanitize and validate redirect paths: normalize backslashes, extract paths from absolute URLs, enforce non-empty paths starting with "/", and continue blocking ".." traversal. Docstrings and comments were adjusted; return behavior remains the sanitized path. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant Validator as PathValidator
participant URL as URLParser
Client->>Validator: next_path
Note right of Validator: Normalize backslashes
Validator->>URL: parse(next_path)
alt Has scheme or netloc
URL-->>Validator: parsed_url
Note right of Validator: Use parsed_url.path
else Relative path
URL-->>Validator: parsed_url
Note right of Validator: Keep as-is (post-normalization)
end
alt Invalid: empty OR not starting with "/" OR contains ".."
Validator-->>Client: Reject/None (invalid)
else Valid
Validator-->>Client: Sanitized path
end
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested reviewers
Pre-merge checks (2 passed, 1 warning)❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull Request Overview
This PR addresses a security vulnerability (code scanning alert #636) related to URL redirection from remote sources by improving the validation logic in the validate_next_path function. The fix strengthens the validation to prevent untrusted URL redirection attacks.
Key changes:
- Enhanced path validation to handle backslash interpretation like browsers
- Improved validation logic to block absolute URLs and dangerous path traversal
- Added explicit checks for empty paths and proper relative path format
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| # Browsers interpret backslashes as forward slashes. Remove all backslashes. | ||
| next_path = next_path.replace("\\", "") |
There was a problem hiding this comment.
Removing backslashes entirely may be too aggressive and could break legitimate paths that contain escaped characters. Consider replacing backslashes with forward slashes instead: next_path = next_path.replace('\\', '/') to maintain path structure while preventing bypass attempts.
| # Browsers interpret backslashes as forward slashes. Remove all backslashes. | |
| next_path = next_path.replace("\\", "") | |
| # Browsers interpret backslashes as forward slashes. Replace backslashes with forward slashes. | |
| next_path = next_path.replace("\\", "/") |
| # Ensure next_path is not an absolute URL | ||
| # Block absolute URLs or anything with scheme/netloc | ||
| if parsed_url.scheme or parsed_url.netloc: | ||
| next_path = parsed_url.path # Extract only the path component |
There was a problem hiding this comment.
The current logic extracts the path component from absolute URLs instead of rejecting them entirely. This could still allow malicious redirects if the extracted path contains dangerous sequences. Consider returning an empty string immediately when scheme or netloc is detected: return ''.
| next_path = parsed_url.path # Extract only the path component | |
| return "" |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/api/plane/utils/path_validator.py (1)
5-23: Enforce validate_next_path on all next_path assignments
- Replace raw
str(next_path)withstr(validate_next_path(next_path))in space/github.py (line 51), space/gitlab.py (line 51) and space/magic.py (line 103).- Ensure when
validate_next_pathreturns"", redirects fall back to a safe default (e.g./).
🧹 Nitpick comments (4)
apps/api/plane/utils/path_validator.py (4)
6-8: Normalize backslashes to '/' instead of deleting them.Dropping backslashes can merge tokens unexpectedly (e.g., "a..\b" -> "a..\b"). Using '/' mirrors browser behavior without altering token boundaries.
- # Browsers interpret backslashes as forward slashes. Remove all backslashes. - next_path = next_path.replace("\\", "") + # Browsers interpret backslashes as forward slashes. Normalize backslashes. + next_path = next_path.replace("\\", "/")
15-17: Trim whitespace before leading-slash check.Prevents false negatives on inputs like ' /foo'.
- # Must start with a forward slash and not be empty - if not next_path or not next_path.startswith("/"): + # Must start with a forward slash and not be empty + next_path = next_path.strip() + if not next_path or not next_path.startswith("/"): return ""
5-23: Optional: tighten invariants and add minimal input guard.If non-strings can reach here (e.g., None),
.replacewill throw. Consider a quick type/length guard.def validate_next_path(next_path: str) -> str: - """Validates that next_path is a safe relative path for redirection.""" + """Validates that next_path is a safe relative path for redirection.""" + if not isinstance(next_path, str): + return ""
5-23: Test vectors to cover before merge.Suggest adding unit tests for:
- "/projects" -> "/projects"
- " //evil.com/x" (with leading space) -> ""
- "//evil.com/x" -> ""
- "////evil.com/x" -> ""
- "https://evil.com/a?b=1" -> "/a" (or confirm desired behavior)
- "/%2e%2e/admin" -> ""
- "/a/%2E%2E/b" -> ""
- "/a..b" -> allowed (confirm requirements) or disallow if you want a strict policy
- "http:\evil.com\foo" -> ""
- "/..\admin" -> "" (after normalization)
Do you want me to add a pytest module with these cases?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/api/plane/utils/path_validator.py(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build and lint web apps
🔇 Additional comments (1)
apps/api/plane/utils/path_validator.py (1)
11-13: Confirm intent: query/fragment from absolute URLs are dropped.For 'https://ex.com/a?b=1#x' you now return '/a' (query lost). If callers expect to retain safe query params, consider preserving
parsed_url.queryafter validating. Otherwise, please confirm this behavior is desired.
| # Block absolute URLs or anything with scheme/netloc | ||
| if parsed_url.scheme or parsed_url.netloc: | ||
| next_path = parsed_url.path # Extract only the path component |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Block protocol-relative URLs ('//...') and multi-slash variants explicitly.
Cases like '////evil.com/path' may be parsed as a path but are treated by browsers as network-path references. Guard them.
if parsed_url.scheme or parsed_url.netloc:
next_path = parsed_url.path # Extract only the path component
+
+ # Disallow protocol-relative/network-path references
+ if next_path.startswith("//"):
+ return ""📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Block absolute URLs or anything with scheme/netloc | |
| if parsed_url.scheme or parsed_url.netloc: | |
| next_path = parsed_url.path # Extract only the path component | |
| # Block absolute URLs or anything with scheme/netloc | |
| if parsed_url.scheme or parsed_url.netloc: | |
| next_path = parsed_url.path # Extract only the path component | |
| # Disallow protocol-relative/network-path references | |
| if next_path.startswith("//"): | |
| return "" |
🤖 Prompt for AI Agents
In apps/api/plane/utils/path_validator.py around lines 11 to 13, the current
check only blocks absolute URLs via scheme/netloc but misses protocol-relative
or multi-leading-slash inputs like '//evil.com' or '////evil.com/path' which
browsers treat as network-path references; update the validator to explicitly
detect and reject any input whose original string or parsed_url.path begins with
two or more slashes (e.g. startswith('//') or re.match(r'^/{2,}', ...)) and
treat them as invalid before accepting parsed_url.path so these network-path
references cannot slip through.
| # Prevent path traversal | ||
| if ".." in next_path: | ||
| return "" |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Catch encoded traversal ('%2e%2e') by decoding once before checking.
Naively searching for '..' misses percent-encoded variants. Decode once for detection; still return the original sanitized path.
- # Prevent path traversal
- if ".." in next_path:
+ # Prevent path traversal (including percent-encoded variants)
+ if ".." in unquote(next_path):
return ""Add import (outside this hunk):
-from urllib.parse import urlparse
+from urllib.parse import urlparse, unquote📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Prevent path traversal | |
| if ".." in next_path: | |
| return "" | |
| ++ b/apps/api/plane/utils/path_validator.py | |
| @@ imports | |
| from urllib.parse import urlparse, unquote | |
| @@ def validate_next_path(...): | |
| - # Prevent path traversal | |
| # Prevent path traversal (including percent-encoded variants) | |
| if ".." in unquote(next_path): | |
| return "" |
🤖 Prompt for AI Agents
In apps/api/plane/utils/path_validator.py around lines 19 to 21, the traversal
check only looks for literal ".." and misses percent-encoded variants; decode
the incoming path once (e.g., using urllib.parse.unquote) into a local variable
and check that decoded string for ".." (and other traversal patterns if
desired), but continue to return the original sanitized path as before; also add
the required import (from urllib.parse import unquote) at the top of the file
outside this hunk.
* chore: added access for workspace admin to edit project settings * chore: workspace admin to update members details * chore: workspace admin to label, state, workflow settings * Revert "chore: added access for workspace admin to edit project settings" This reverts commit 803b56514887339d884eaef170de8a9e4ecfda8c. * chore: updated worspace admin access for projects * Revert "chore: workspace admin to update members details" This reverts commit ac465d618d7a89ef696db3484e515957b6b5e264. * Revert "chore: workspace admin to label, state, workflow settings" This reverts commit f01a89604e71792096cbae8e029cac160ea209fb. * chore: workspace admin access in permission classes and decorator * chore: check for teamspace members * chore: refactor permission logic * [WIKI-632] chore: accept additional props for document collaborative editor (#7718) * chore: add collaborative document editor extended props * fix: additional rich text extension props * fix: formatting * chore: add types to the trailing node extension --------- Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com> * [WEB-4854] chore: project admin accesss to workspace admins (#7749) * chore: project admin accesss to workspace admins * chore: frontend changes * chore: remove console.log * chore: refactor permission decorator * chore: role enum * chore: rearrange role_choices * Potential fix for code scanning alert no. 636: URL redirection from remote source (#7760) Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * [WEB-4441]fix: members account type dropdown position #7759 * [WEB-4857] fix: applied filters root update #7750 * [WEB-4858]chore: updated content for error page (#7766) * chore: updated content for error page * chore: updated btn url * fix: merge conflicts * fix: merge conflicts * fix: use enum for roles --------- Co-authored-by: vamsikrishnamathala <matalav55@gmail.com> Co-authored-by: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com> Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com> Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com>
…emote source (makeplane#7760) Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Potential fix for https://github.com/makeplane/plane/security/code-scanning/636
To fully fix this issue and prevent untrusted URL redirection, we should improve the validation logic in
validate_next_pathsuch that it matches browser behaviors—specifically, removing backslashes, robustly checking for absolute URLs, and only allowing safe, relative paths. We should adjustvalidate_next_pathinapps/api/plane/utils/path_validator.pyso it replaces backslashes with nothing (or converts to forward slashes), then usesurlparseto ensure no scheme/netloc is present. It should also only allow paths that start with a forward slash, do not contain path traversal (..), and are not empty.You should update
validate_next_pathaccordingly so all places where it is called (notably the redirect in the SignUpAuthEndpoint class inapps/api/plane/authentication/views/app/email.py) are using improved validation that blocks malformed or dangerous input.No changes are required in the view file—just improve the underlying validator so its contract is strong and future-proof.
Suggested fixes powered by Copilot Autofix. Review carefully before merging.
Summary by CodeRabbit