LinkedIn: browser-realm decodeLinkedInSafetyUrl accepts credentialed URLs that the Node-side helper rejects
- Dominant language
- JavaScript
- Stars
- 29.3k
- Forks
- 2.9k
- Avg merge
- 15h 36m
- Merged PRs (30d)
- 70
Description
## Summary
`decodeLinkedInSafetyUrl` exists in two realms with **different security postures for credential-bearing URLs**. The Node-side helper rejects `user:pass@` URLs; the three browser-realm (page-evaluate) copies do not, so credentialed targets — including via the `/safety/go/?url=` redirect — reach extraction output.
This is **pre-existing behaviour**, not introduced by #2351. It was found while reviewing that PR and deliberately kept out of scope, since #2351 was a pure Node-side consolidation and the browser strings were a hard negative boundary.
## Measured on current `main` (`de16476e`)
| input | Node (`shared.js`) | browser-realm |
|---|---|---|
| `https://user:pass@example.com/x` | `""` | `"https://user:pass@example.com/x"` |
| `/safety/go/?url=` | `""` | `"https://user:pass@evil.com/"` |
| `/in/someone` | `""` | `"https://www.linkedin.com/in/someone"` |
| `javascript:alert(1)` | `""` | `""` (consistent) |
## Why they differ
- **Node** (`clis/linkedin/shared.js`) routes through `normalizeHttpUrl`, which explicitly rejects `parsed.username || parsed.password`.
- **Browser** copies check only `protocol === 'http:' || 'https:'`. They cannot import `normalizeHttpUrl` (page realm) and resolve against `location.origin`.
Browser-realm copies on current main:
- `clis/linkedin/profile-experience.js:149` (extraction)
- `clis/linkedin/profile-experience.js:393` (dialog)
- `clis/linkedin/profile-projects.js:92` (extraction)
Note these are **not merely duplicated code** — they are realm-local implementations that cannot be replaced by a Node import (`normalizeWhitespace` is undefined in the page realm; `location` is undefined in Node), and they have a genuinely different relative-URL contract.
## Decision needed
Should browser-side extraction also reject credentialed URLs, matching the Node side?
- **If yes:** add a `username`/`password` check to all three page-realm copies, plus tests for credentials, `safety/go` redirect targets, and relative URLs.
- **If no:** document why extraction output may legitimately contain credentials, so the divergence is intentional rather than accidental.
Either way the **relative-URL divergence** (`/in/someone` → `""` vs origin-resolved) is entangled: aligning the realms touches that contract too, so it should be decided together rather than patching only the credential check.
The Node side's rejection is now pinned by `clis/linkedin/shared.test.js`, so one side of the divergence is test-protected regardless of the outcome.
Contributor guide
Research direction
Start with the three browser-realm implementations at clis/linkedin/profile-experience.js:149 and :393 and clis/linkedin/profile-projects.js:92, then compare them with normalizeHttpUrl and clis/linkedin/shared.test.js. Decide the intended credential and relative-URL contract, then add or update coverage for credentials, safety/go redirects, and relative URLs so the browser behavior matches the decision.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- cli, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100