diff --git a/bin/gstack-artifacts-url b/bin/gstack-artifacts-url index f5d9d55a77..baa0af2f20 100755 --- a/bin/gstack-artifacts-url +++ b/bin/gstack-artifacts-url @@ -49,6 +49,19 @@ strip_git() { echo "${1%.git}" } +valid_owner_repo() { + local owner_repo="$1" + case "$owner_repo" in + ""|/*|*/|*//*) + return 1 + ;; + esac + case "$owner_repo" in + */*) return 0 ;; + *) return 1 ;; + esac +} + # Parse to (host, owner_repo) regardless of input shape. parse_url() { local u="$1" @@ -82,7 +95,7 @@ parse_url() { exit 3 ;; esac - if [ -z "$host" ] || [ -z "$owner_repo" ] || [ "$owner_repo" = "$u" ]; then + if [ -z "$host" ] || ! valid_owner_repo "$owner_repo"; then echo "gstack-artifacts-url: failed to parse host/owner from: $u" >&2 exit 3 fi diff --git a/test/gstack-artifacts-url.test.ts b/test/gstack-artifacts-url.test.ts index efecbfb24a..133ed7fc83 100644 --- a/test/gstack-artifacts-url.test.ts +++ b/test/gstack-artifacts-url.test.ts @@ -67,6 +67,24 @@ describe('gstack-artifacts-url', () => { expect(r.stderr).toContain('unrecognized URL form'); }); + test('rejects remotes without both owner and repo path segments', () => { + const malformed = [ + 'https://github.com', + 'https://github.com/owner', + 'https://github.com/owner/', + 'https://github.com/owner//repo', + 'git@github.com:owner', + 'ssh://git@github.com', + 'ssh://git@github.com/owner', + ]; + + for (const url of malformed) { + const r = run(['--to', 'ssh', url]); + expect(r.code, url).toBe(3); + expect(r.stderr, url).toContain('failed to parse host/owner'); + } + }); + test('rejects missing args with exit 2', () => { expect(run([]).code).toBe(2); expect(run(['--to']).code).toBe(2);