From af817f9e8081960024a96e1f9b25bf0e648d352c Mon Sep 17 00:00:00 2001 From: Matt Palmer <9059517+56KBs@users.noreply.github.com> Date: Fri, 17 Dec 2021 12:47:45 +0000 Subject: [PATCH] fix(go): Mimic Go logic for `GOPRIVATE` parsing (#13166) Ensure that the parsing of `GOPRIVATE`/`GONOPROXY` matches the behaviour of Go itself. The documentation for these values state it matches the logic of `path.Match`, however in reality it's actually a prefix based match. Updating the regex to allow for either an exact match, or a match where the configured value is a prefix of the package, when a `/` is added. Additionally, strip any trailing `/`'s from the configured value, as this matches the logic that Go takes when matching. Fixes #13138 --- lib/datasource/go/releases-goproxy.spec.ts | 63 +++++++++++++++++----- lib/datasource/go/releases-goproxy.ts | 8 ++- 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/lib/datasource/go/releases-goproxy.spec.ts b/lib/datasource/go/releases-goproxy.spec.ts index e6ec16ca38..3dc555a74d 100644 --- a/lib/datasource/go/releases-goproxy.spec.ts +++ b/lib/datasource/go/releases-goproxy.spec.ts @@ -98,22 +98,31 @@ describe('datasource/go/releases-goproxy', () => { expect(parseNoproxy(undefined)).toBeNull(); expect(parseNoproxy(null)).toBeNull(); expect(parseNoproxy('')).toBeNull(); - expect(parseNoproxy('*')?.source).toBe('^(?:[^\\/]*)$'); - expect(parseNoproxy('?')?.source).toBe('^(?:[^\\/])$'); - expect(parseNoproxy('foo')?.source).toBe('^(?:foo)$'); - expect(parseNoproxy('\\f\\o\\o')?.source).toBe('^(?:foo)$'); - expect(parseNoproxy('foo,bar')?.source).toBe('^(?:foo|bar)$'); - expect(parseNoproxy('[abc]')?.source).toBe('^(?:[abc])$'); - expect(parseNoproxy('[a-c]')?.source).toBe('^(?:[a-c])$'); - expect(parseNoproxy('[\\a-\\c]')?.source).toBe('^(?:[a-c])$'); - expect(parseNoproxy('a.b.c')?.source).toBe('^(?:a\\.b\\.c)$'); + expect(parseNoproxy('/')).toBeNull(); + expect(parseNoproxy('*')?.source).toBe('^(?:[^\\/]*)(?:\\/.*)?$'); + expect(parseNoproxy('?')?.source).toBe('^(?:[^\\/])(?:\\/.*)?$'); + expect(parseNoproxy('foo')?.source).toBe('^(?:foo)(?:\\/.*)?$'); + expect(parseNoproxy('\\f\\o\\o')?.source).toBe('^(?:foo)(?:\\/.*)?$'); + expect(parseNoproxy('foo,bar')?.source).toBe('^(?:foo|bar)(?:\\/.*)?$'); + expect(parseNoproxy('[abc]')?.source).toBe('^(?:[abc])(?:\\/.*)?$'); + expect(parseNoproxy('[a-c]')?.source).toBe('^(?:[a-c])(?:\\/.*)?$'); + expect(parseNoproxy('[\\a-\\c]')?.source).toBe('^(?:[a-c])(?:\\/.*)?$'); + expect(parseNoproxy('a.b.c')?.source).toBe('^(?:a\\.b\\.c)(?:\\/.*)?$'); + expect(parseNoproxy('trailing/')?.source).toBe( + '^(?:trailing)(?:\\/.*)?$' + ); }); it('matches on real package prefixes', () => { + expect(parseNoproxy('ex.co').test('ex.co/foo')).toBeTrue(); + expect(parseNoproxy('ex.co/').test('ex.co/foo')).toBeTrue(); + expect(parseNoproxy('ex.co/foo/bar').test('ex.co/foo/bar')).toBeTrue(); expect(parseNoproxy('ex.co/foo/bar').test('ex.co/foo/bar')).toBeTrue(); expect(parseNoproxy('*/foo/*').test('example.com/foo/bar')).toBeTrue(); expect(parseNoproxy('ex.co/foo/*').test('ex.co/foo/bar')).toBeTrue(); expect(parseNoproxy('ex.co/foo/*').test('ex.co/foo/baz')).toBeTrue(); + expect(parseNoproxy('ex.co').test('ex.co/foo/v2')).toBeTrue(); + expect( parseNoproxy('ex.co/foo/bar,ex.co/foo/baz').test('ex.co/foo/bar') ).toBeTrue(); @@ -123,15 +132,45 @@ describe('datasource/go/releases-goproxy', () => { expect( parseNoproxy('ex.co/foo/bar,ex.co/foo/baz').test('ex.co/foo/qux') ).toBeFalse(); - }); - it('Matches from start to end', () => { - expect(parseNoproxy('x').test('x/aba')).toBeFalse(); + expect(parseNoproxy('ex').test('ex.co/foo')).toBeFalse(); + expect(parseNoproxy('aba').test('x/aba')).toBeFalse(); expect(parseNoproxy('x/b').test('x/aba')).toBeFalse(); expect(parseNoproxy('x/ab').test('x/aba')).toBeFalse(); expect(parseNoproxy('x/ab[a-b]').test('x/aba')).toBeTrue(); }); + + it('matches on wildcards', () => { + expect(parseNoproxy('/*/').test('ex.co/foo')).toBeFalse(); + expect(parseNoproxy('*/foo').test('ex.co/foo')).toBeTrue(); + expect(parseNoproxy('*/fo').test('ex.co/foo')).toBeFalse(); + expect(parseNoproxy('*/fo?').test('ex.co/foo')).toBeTrue(); + expect(parseNoproxy('*/fo*').test('ex.co/foo')).toBeTrue(); + expect(parseNoproxy('*fo*').test('ex.co/foo')).toBeFalse(); + + expect(parseNoproxy('*.co').test('ex.co/foo')).toBeTrue(); + expect(parseNoproxy('ex*').test('ex.co/foo')).toBeTrue(); + expect(parseNoproxy('*/foo').test('ex.co/foo/v2')).toBeTrue(); + expect(parseNoproxy('*/foo/').test('ex.co/foo/v2')).toBeTrue(); + expect(parseNoproxy('*/foo/*').test('ex.co/foo/v2')).toBeTrue(); + expect(parseNoproxy('*/foo/*/').test('ex.co/foo/v2')).toBeTrue(); + expect(parseNoproxy('*/v2').test('ex.co/foo/v2')).toBeFalse(); + expect(parseNoproxy('*/*/v2').test('ex.co/foo/v2')).toBeTrue(); + expect(parseNoproxy('*/*/*').test('ex.co/foo/v2')).toBeTrue(); + expect(parseNoproxy('*/*/*/').test('ex.co/foo/v2')).toBeTrue(); + expect(parseNoproxy('*/*/*').test('ex.co/foo')).toBeFalse(); + expect(parseNoproxy('*/*/*/').test('ex.co/foo')).toBeFalse(); + + expect(parseNoproxy('*/*/*,,').test('ex.co/repo')).toBeFalse(); + expect(parseNoproxy('*/*/*,,*/repo').test('ex.co/repo')).toBeTrue(); + expect(parseNoproxy(',,*/repo').test('ex.co/repo')).toBeTrue(); + }); + + it('matches on character ranges', () => { + expect(parseNoproxy('x/ab[a-b]').test('x/aba')).toBeTrue(); + expect(parseNoproxy('x/ab[a-b]').test('x/abc')).toBeFalse(); + }); }); describe('getReleases', () => { diff --git a/lib/datasource/go/releases-goproxy.ts b/lib/datasource/go/releases-goproxy.ts index c35de8224f..2546e88e7e 100644 --- a/lib/datasource/go/releases-goproxy.ts +++ b/lib/datasource/go/releases-goproxy.ts @@ -72,6 +72,10 @@ const lexer = moo.states({ push: 'characterRange', value: (_: string) => '[', }, + trailingSlash: { + match: /\/$/, + value: (_: string) => '', + }, char: { match: /[^*?\\[\n]/, value: (s: string) => s.replace(regEx('\\.', 'g'), '\\.'), @@ -107,7 +111,9 @@ export function parseNoproxy( } lexer.reset(input); const noproxyPattern = [...lexer].map(({ value }) => value).join(''); - const result = noproxyPattern ? regEx(`^(?:${noproxyPattern})$`) : null; + const result = noproxyPattern + ? regEx(`^(?:${noproxyPattern})(?:/.*)?$`) + : null; parsedNoproxy[input] = result; return result; } -- GitLab