Skip to content

Commit 38e3b96

Browse files
committed
fix(specs): accept npm's git+https/git+ssh repository URLs
The previous scheme class `[a-z]+` rejected `git+https://` and `git+ssh://` — npm's canonical `repository.url` forms from `npm pkg get` / `package.json`. Callers in `src/packages/operations.ts` that pass `repository.url` to `getRepoUrlDetails` silently lost user/project for those packages. Broaden the class to `[a-z][a-z+]*` so schemes may include `+` after the first letter. Still rejects github.com lookalikes (`githubXcom`, `fake-github.com.attacker.tld`) and scp-style `git@github.com:...` (which callers must normalize upstream).
1 parent 70cfcf4 commit 38e3b96

2 files changed

Lines changed: 20 additions & 4 deletions

File tree

src/packages/specs.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ export function getRepoUrlDetails(repoUrl: string = ''): {
2424
// Anchor the host to exactly `github.com` (optionally preceded by
2525
// userinfo like `user@`). Escaping the `.` blocks lookalikes like
2626
// `githubXcom`; pinning the host to the full final label blocks
27-
// `fake-github.com` or `github.com.attacker.tld` shenanigans. Callers
28-
// passing scp-style git@github.com:… need to normalize upstream; we
29-
// require `://` here so the host component is unambiguous.
27+
// `fake-github.com` or `github.com.attacker.tld` shenanigans. The
28+
// scheme class allows `+` so npm's canonical `git+https://…` and
29+
// `git+ssh://…` forms from package.json `repository.url` match.
30+
// Callers passing scp-style git@github.com:… need to normalize
31+
// upstream; we require `://` here so the host is unambiguous.
3032
const match =
31-
/^(?:[a-z]+:\/\/)(?:[^/@]+@)?github\.com\/([^?#]+)(?:[?#]|$)/i.exec(repoUrl)
33+
/^(?:[a-z][a-z+]*:\/\/)(?:[^/@]+@)?github\.com\/([^?#]+)(?:[?#]|$)/i.exec(
34+
repoUrl,
35+
)
3236
if (!match || !match[1]) {
3337
return { user: '', project: '' }
3438
}

test/unit/packages/specs.test.mts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ describe('packages/specs', () => {
5252
expect(result.project).toBe('berry')
5353
})
5454

55+
it('handles git+https:// and git+ssh:// URLs (npm repository.url forms)', () => {
56+
// npm's `npm pkg get repository.url` canonicalizes to `git+https://`
57+
// or `git+ssh://` prefixes. The scheme pattern accepts `+` so these
58+
// match the same as plain `https://github.com/…`.
59+
expect(
60+
getRepoUrlDetails('git+https://github.com/npm/cli.git'),
61+
).toEqual({ user: 'npm', project: 'cli' })
62+
expect(
63+
getRepoUrlDetails('git+ssh://git@github.com/npm/cli.git'),
64+
).toEqual({ user: 'npm', project: 'cli' })
65+
})
66+
5567
it('returns empty strings for invalid URL', () => {
5668
// Previously the loose `/^.+github.com\//` replace left garbage in
5769
// user/project for non-GitHub or malformed inputs. Now returns a

0 commit comments

Comments
 (0)