Skip to content

Commit 523fa7e

Browse files
committed
fix: add allowlist
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
1 parent bfda0b2 commit 523fa7e

2 files changed

Lines changed: 74 additions & 1 deletion

File tree

services/apps/packages_worker/src/maven/__tests__/normalize.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,44 @@ describe('normalizeScmUrl', () => {
194194
expect(normalizeScmUrl('${scm-url}')).toBeNull()
195195
expect(normalizeScmUrl('http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/foo')).toBeNull()
196196
})
197+
198+
// SCP colon form: "host:owner/repo" where the colon is a path separator, not a port
199+
it('recovers bare host:owner/repo SCP colon form', () => {
200+
expect(normalizeScmUrl('github.com:japgolly/scalacss.git')).toBe(
201+
'https://github.com/japgolly/scalacss',
202+
)
203+
})
204+
205+
it('recovers scheme://host:owner/repo SCP colon form', () => {
206+
expect(normalizeScmUrl('https://github.com:networknt/light-4j.git')).toBe(
207+
'https://github.com/networknt/light-4j',
208+
)
209+
})
210+
211+
it('recovers ssh://git@host:owner/repo SCP colon form', () => {
212+
expect(normalizeScmUrl('ssh://git@github.com:apache/iotdb.git')).toBe(
213+
'https://github.com/apache/iotdb',
214+
)
215+
expect(normalizeScmUrl('ssh://git@bitbucket.org:eci-elements/web-services.git')).toBe(
216+
'https://bitbucket.org/eci-elements/web-services',
217+
)
218+
})
219+
220+
it('does not treat a numeric port as an SCP separator', () => {
221+
expect(normalizeScmUrl('https://gitlab.com:443/foo/bar')).toBe('https://gitlab.com/foo/bar')
222+
})
223+
224+
it('accepts allowlisted self-hosted GitLab/Gitea hosts', () => {
225+
expect(normalizeScmUrl('https://git.neckar.it/neckarit/neckar-hub')).toBe(
226+
'https://git.neckar.it/neckarit/neckar-hub',
227+
)
228+
expect(normalizeScmUrl('scm:git:https://gitlab.inria.fr/owner/repo.git')).toBe(
229+
'https://gitlab.inria.fr/owner/repo',
230+
)
231+
})
232+
233+
it('still returns null for hosts not in the allowlist', () => {
234+
expect(normalizeScmUrl('https://git.corp.adobe.com/team/project')).toBeNull()
235+
expect(normalizeScmUrl('https://android.googlesource.com/platform/tools/base')).toBeNull()
236+
})
197237
})

services/apps/packages_worker/src/maven/extract.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,13 +458,32 @@ export async function extractArtifact(groupId: string, artifactId: string, versi
458458
* placeholders) yields null so it is never stored as a repository link.
459459
*
460460
* TODO(CM): host list pending product confirmation before rollout.
461+
*
462+
* Candidate additions found in Maven declared_repository_url (critical rows) but
463+
* intentionally NOT added yet, because they need more than a host allowlist:
464+
* - gitbox.apache.org / git.apache.org / git-wip-us.apache.org (~1.3k rows):
465+
* paths are `/repos/asf/<repo>`, so the generic first-two-segments logic would
466+
* collapse every Apache repo to `repos/asf`. Needs path-aware handling (skip
467+
* the `/repos/asf/` prefix) or mapping to the github.com/apache mirror.
468+
* - git.eclipse.org (~180 rows): Gerrit paths, likewise not owner/repo.
469+
* - android.googlesource.com, ec.europa.eu (Bitbucket-Server /projects/x/repos/y):
470+
* multi-segment paths, not owner/repo.
471+
* - ambiguous `git.*` hosts (git.iem.at, git.i-novus.ru, git.oschina.net) and the
472+
* internal git.corp.adobe.com: pending path/reachability confirmation.
461473
*/
462474
const SCM_HOSTS = new Set([
463475
'github.com',
464476
'gitlab.com',
465477
'bitbucket.org',
466478
'gitee.com',
467479
'codeberg.org',
480+
// Self-hosted GitLab / Gitea instances seen in Maven POMs with clean
481+
// /<owner>/<repo> paths (same shape as gitlab.com — handled by the generic logic).
482+
'gitlab.smartb.city',
483+
'gitlab.ow2.org',
484+
'gitlab.nuiton.org',
485+
'gitlab.inria.fr',
486+
'git.neckar.it',
468487
])
469488

470489
/** Hosts whose owner/repo path is case-insensitive and should be lower-cased. */
@@ -483,6 +502,9 @@ const CASE_INSENSITIVE_HOSTS = new Set(['github.com', 'gitlab.com'])
483502
* github.com/owner/repo (no scheme) → https://github.com/owner/repo
484503
* git://github.com/owner/repo.git → https://github.com/owner/repo
485504
* http://github.com/owner/repo/tree/... → https://github.com/owner/repo
505+
* github.com:owner/repo.git (SCP colon) → https://github.com/owner/repo
506+
* https://github.com:owner/repo → https://github.com/owner/repo
507+
* ssh://git@github.com:owner/repo.git → https://github.com/owner/repo
486508
*
487509
* Rejected (→ null): website-only URLs (https://meson.ai/), non-SCM hosts
488510
* (svn://…, http://source.android.com), placeholders (Private, ${scm-url}).
@@ -501,14 +523,25 @@ export function normalizeScmUrl(raw: string | null): string | null {
501523
// SCP form git@host:owner/repo → https://host/owner/repo
502524
s = s.replace(/^git@([^:/]+):(.+)$/, 'https://$1/$2')
503525

526+
// ssh://git@host:owner/repo → https://host/owner/repo (SCP colon under ssh)
527+
s = s.replace(/^ssh:\/\/git@([^:/]+):(?=\D)/, 'https://$1/')
528+
504529
// ssh://git@host/… → https://host/…
505530
s = s.replace(/^ssh:\/\/git@([^/]+)\//, 'https://$1/')
506531

532+
// scheme://host:owner/repo → scheme://host/owner/repo — the colon is an SCP path
533+
// separator, not a port (guarded by \D so real numeric ports are left intact).
534+
s = s.replace(/^(https?):\/\/([^:/]+):(?=\D)/, '$1://$2/')
535+
507536
// git:// → https://, and upgrade http:// → https://
508537
s = s.replace(/^git:\/\//, 'https://').replace(/^http:\/\//, 'https://')
509538

510539
// No scheme at all (e.g. "github.com/owner/repo") → assume https
511-
if (!s.includes('://')) s = `https://${s}`
540+
if (!s.includes('://')) {
541+
// Bare SCP form "host:owner/repo" → "host/owner/repo" before assuming https.
542+
s = s.replace(/^([^/:]+):(?=\D)/, '$1/')
543+
s = `https://${s}`
544+
}
512545

513546
let parsed: URL
514547
try {

0 commit comments

Comments
 (0)