diff --git a/__tests__/resolvers/exotics/registry-resolver.js b/__tests__/resolvers/exotics/registry-resolver.js index ab8d802594..03fa8af907 100644 --- a/__tests__/resolvers/exotics/registry-resolver.js +++ b/__tests__/resolvers/exotics/registry-resolver.js @@ -28,3 +28,22 @@ test('resolves scoped yarn: package', () => { expect(resolver.name).toEqual('@org/foo'); }); + +describe('RegistryResolver DOS regression test', () => { + const MAX_MS = 200; + + test('long fragment without # should finish quickly and throw', () => { + const fragment = '\u0000' + '\u0000:'.repeat(100000) + '\n1\n'; + const reqWithReporter: any = { + reporter: {lang: (_key, frag) => `invalidFragment: ${String(frag).slice(0, 16)}...`}, + }; + + const start = Date.now(); + expect(() => { + new RegistryResolver((reqWithReporter: any), fragment); + }).toThrow(MessageError); + const duration = Date.now() - start; + + expect(duration).toBeLessThan(MAX_MS); + }); +}); diff --git a/src/resolvers/exotics/registry-resolver.js b/src/resolvers/exotics/registry-resolver.js index f75f673e86..9b06a94288 100644 --- a/src/resolvers/exotics/registry-resolver.js +++ b/src/resolvers/exotics/registry-resolver.js @@ -9,7 +9,7 @@ export default class RegistryResolver extends ExoticResolver { constructor(request: PackageRequest, fragment: string) { super(request, fragment); - const match = fragment.match(/^(\S+):(@?.*?)(@(.*?)|)$/); + const match = fragment.match(/^(\S+):(@?(?:(?![@:]).)*?)(@((?:(?![@:]).)*?)|)$/); if (match) { this.range = match[4] || 'latest'; this.name = match[2];