From 53b32526241d06db4c48679b8984547587d616ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Fri, 19 May 2023 23:16:53 +0200 Subject: [PATCH] fix: invalid local pointers are treated as external by resolveInlineRef (#123) --- src/__tests__/resolveInlineRef.spec.ts | 50 +++++++++++++++++++++++--- src/extractSourceFromRef.ts | 4 +-- src/index.ts | 1 + src/isExternalRef.ts | 1 + 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 src/isExternalRef.ts diff --git a/src/__tests__/resolveInlineRef.spec.ts b/src/__tests__/resolveInlineRef.spec.ts index 1f84dca..533cf44 100644 --- a/src/__tests__/resolveInlineRef.spec.ts +++ b/src/__tests__/resolveInlineRef.spec.ts @@ -137,7 +137,7 @@ describe('resolveInlineRef', () => { it('should resolve top-level $ref', () => { const doc = { - $ref: '#/$defs/User', + $ref: '#/%24defs/User', $defs: { User: { type: 'object', @@ -169,16 +169,16 @@ describe('resolveInlineRef', () => { type: 'array', contains: { summary: 'Bear cave', - $ref: '#/$defs/Cave', + $ref: '#/%24defs/Cave', description: 'Apparently Tom likes bears', }, }, greatestBear: { - $ref: '#/$defs/Bear', + $ref: '#/%24defs/Bear', description: 'The greatest bear!', }, bestBear: { - $ref: '#/$defs/Bear', + $ref: '#/%24defs/Bear', summary: 'The best bear!', }, }, @@ -223,4 +223,46 @@ describe('resolveInlineRef', () => { expect(resolveInlineRefWithLocation(doc, '#/properties/bestBear')).toHaveProperty('location', ['$defs', 'Bear']); }); }); + + it('handles encoded characters', () => { + const doc = { + type: 'object', + $defs: { + 'Cool Bear': { + type: 'string', + }, + 'самый крутой медведь?': { + const: 'винни пух)', + }, + }, + }; + + expect(resolveInlineRef(doc, '#/%24defs/Cool%20Bear')).toStrictEqual({ + type: 'string', + }); + + expect( + resolveInlineRef( + doc, + '#/%24defs/%D1%81%D0%B0%D0%BC%D1%8B%D0%B9%20%D0%BA%D1%80%D1%83%D1%82%D0%BE%D0%B9%20%D0%BC%D0%B5%D0%B4%D0%B2%D0%B5%D0%B4%D1%8C%3F', + ), + ).toStrictEqual({ + const: 'винни пух)', + }); + }); + + it('gracefully handles unencoded characters', () => { + const doc = { + type: 'object', + $defs: { + 'Cool Bear': { + type: 'string', + }, + }, + }; + + expect(resolveInlineRef(doc, '#/$defs/Cool Bear')).toStrictEqual({ + type: 'string', + }); + }); }); diff --git a/src/extractSourceFromRef.ts b/src/extractSourceFromRef.ts index 0284d30..8f32a59 100644 --- a/src/extractSourceFromRef.ts +++ b/src/extractSourceFromRef.ts @@ -1,7 +1,7 @@ -import { isLocalRef } from './isLocalRef'; +import { isExternalRef } from './isExternalRef'; export const extractSourceFromRef = (ref: unknown): string | null => { - if (typeof ref !== 'string' || ref.length === 0 || isLocalRef(ref)) { + if (typeof ref !== 'string' || ref.length === 0 || !isExternalRef(ref)) { return null; } diff --git a/src/index.ts b/src/index.ts index b09c547..30a3bd2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ export * from './getJsonPathForPosition'; export * from './getLastPathSegment'; export * from './getLocationForJsonPath'; export * from './hasRef'; +export * from './isExternalRef'; export * from './isLocalRef'; export * from './isPlainObject'; export * from './parseWithPointers'; diff --git a/src/isExternalRef.ts b/src/isExternalRef.ts new file mode 100644 index 0000000..a9ccdde --- /dev/null +++ b/src/isExternalRef.ts @@ -0,0 +1 @@ +export const isExternalRef = (pointer: string) => pointer.length > 0 && pointer[0] !== '#';