AOSSIE-Org / AOSSIE-Org/Gitcord-GithubDiscordBot
[BUG]: Phishing URL injection via improper hostname validation in LinkedInProfileValidator
- Dominant language
- Python
- Stars
- 22
- Forks
- 21
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 21
Description
### Description
`LinkedInProfileValidator` (`src/ghdcbot/core/social_validators.py`) me hostname validation missing hai. Validator sirf check karta hai ke URL me `/in/` maujood hai ya nahi, lekin yeh verify nahi karta ke domain `linkedin.com` hai.
## Impact
* **Severity:** High (CWE-20: Improper Input Validation, CWE-601: URL Redirection to Untrusted Site)
* **Attack Scenario:** An attacker links a malicious credential-harvesting site (e.g. `https://evil-phishing.com/in/target`) via `/connect-social platform:LinkedIn profile:https://evil-phishing.com/in/target`.
* **Public Visibility:** The URL is displayed in Discord `/profile` embeds, where community members may trust and click it as a verified LinkedIn profile.
---
## Technical Details & Root Cause
In `src/ghdcbot/core/social_validators.py`:
```python
@staticmethod
def _normalize_url(url: str) -> str:
url = url.strip()
# Ensure https
if url.startswith("http://"):
url = "https://" + url[7:]
elif not url.startswith("https://"):
url = "https://" + url
# Remove www if present (normalize to linkedin.com)
url = url.replace("www.linkedin.com", "linkedin.com")
# Remove trailing slashes and query params
url = url.split("?")[0].rstrip("/")
# Reject company pages
if "/company/" in url or "/companies/" in url or "/school/" in url:
raise ValueError("Company and school pages are not supported, only personal profiles")
# Must be in /in/ path for personal profiles
if "/in/" not in url:
raise ValueError("Only LinkedIn profile URLs (linkedin.com/in/...) are supported")
return url
Key Flaws:
No Hostname Check: The validator relies purely on string manipulation (.replace("www.linkedin.com", "linkedin.com")) and a substring check (if "/in/" not in url:).
Inconsistency with other validators: In contrast, XProfileValidator uses urllib.parse.urlparse to strictly assert:
python
if host in {"x.com", "twitter.com"}:
Spoofing Vulnerability: URLs such as https://linkedin.com.attacker.com/in/victim or https://evil-phishing.com/in/victim pass _normalize_url and _extract_profile_id.
Exception Handling: Rejection currently relies on downstream Pydantic model initialization, which raises an unhandled pydantic_core.ValidationError rather than a clean, user-friendly ValueError expected by the command handler.
### Steps to Reproduce
from ghdcbot.core.social_validators import LinkedInProfileValidator
validator = LinkedInProfileValidator()
normalized = validator._normalize_url("https://evil-phishing.com/in/target")
profile_id = validator._extract_profile_id("https://evil-phishing.com/in/target")
print("Normalized:", normalized) # Output: https://evil-phishing.com/in/target
print("Profile ID:", profile_id) # Output: target
## Proposed Fix
### 1. In `src/ghdcbot/core/social_validators.py` (`LinkedInProfileValidator`):
- Parse the URL using `urllib.parse.urlparse`.
- Validate that the scheme is `http` or `https` (and canonicalize to `https://`).
- Extract `hostname`, strip leading `www.`, and enforce that it strictly matches `linkedin.com`.
- Validate that the path strictly matches `/in/`.
- Raise clean `ValueError` exceptions with descriptive error messages.
### 2. In `tests/test_social_validators.py`:
- Add unit tests verifying rejection of:
- `https://evil-phishing.com/in/target`
- `https://linkedin.com.attacker.org/in/target`
- `https://fake-linkedin.com/in/target`
- Malicious schemes (e.g., `javascript:`, `ftp:`)
---
## Acceptance Criteria
- [ ] `LinkedInProfileValidator.validate()` rejects non-LinkedIn domains containing `/in/`.
- [ ] Subdomain spoofing (e.g. `linkedin.com.attacker.com`) is rejected with a `ValueError`.
- [ ] Valid LinkedIn formats (`https://linkedin.com/in/user`, `https://www.linkedin.com/in/user`, `linkedin.com/in/user`) continue to pass.
- [ ] All test suites pass cleanly (`pytest tests/test_social_validators.py`).
### Logs and Screenshots
_No response_
### Environment Details
_No response_
### Impact
High - Major feature is broken
### Code of Conduct
- [x] I have joined the [Discord server](https://discord.gg/hjUhu33uAn) and will post updates there
- [x] I have searched existing issues to avoid duplicates
Contributor guide
Research direction
Start in src/ghdcbot/core/social_validators.py, reading LinkedInProfileValidator and the existing XProfileValidator hostname checks. Run pytest tests/test_social_validators.py, then cover the listed spoofed domains and malicious schemes while preserving the accepted LinkedIn URL formats. Done means invalid hosts and schemes raise ValueError and the targeted test suite passes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 85/100