From ad5ebdd27989e1c4bd2bdbbb33995aa7f7a6447e Mon Sep 17 00:00:00 2001 From: kabeep Date: Sun, 5 May 2024 05:22:20 +0800 Subject: [PATCH] fix: unsafe regular expression --- src/core/TraceError.ts | 6 +-- src/helpers/get-address.ts | 25 +++++++++++ src/helpers/index.ts | 2 +- src/helpers/is-bg-rgb.ts | 12 ++--- src/helpers/is-nil.ts | 6 --- src/helpers/is-rgb.ts | 10 ++--- src/helpers/normalize-rgb.ts | 3 +- test/helpers/get-address.spec.ts | 45 +++++++++++++++++++ test/{utils => helpers}/is-bg-rgb.spec.ts | 0 test/{utils => helpers}/is-rgb.spec.ts | 0 test/{utils => helpers}/is-string.spec.ts | 0 .../normalize-number.spec.ts | 0 .../{utils => helpers}/normalize-path.spec.ts | 0 test/{utils => helpers}/normalize-rgb.spec.ts | 0 .../normalize-track.spec.ts | 0 test/utils/is-nil.spec.ts | 38 ---------------- 16 files changed, 85 insertions(+), 62 deletions(-) create mode 100644 src/helpers/get-address.ts delete mode 100644 src/helpers/is-nil.ts create mode 100644 test/helpers/get-address.spec.ts rename test/{utils => helpers}/is-bg-rgb.spec.ts (100%) rename test/{utils => helpers}/is-rgb.spec.ts (100%) rename test/{utils => helpers}/is-string.spec.ts (100%) rename test/{utils => helpers}/normalize-number.spec.ts (100%) rename test/{utils => helpers}/normalize-path.spec.ts (100%) rename test/{utils => helpers}/normalize-rgb.spec.ts (100%) rename test/{utils => helpers}/normalize-track.spec.ts (100%) delete mode 100644 test/utils/is-nil.spec.ts diff --git a/src/core/TraceError.ts b/src/core/TraceError.ts index 0ceddce..f79a510 100644 --- a/src/core/TraceError.ts +++ b/src/core/TraceError.ts @@ -1,5 +1,5 @@ import { basename, dirname } from 'node:path'; -import { normalizeNumber, normalizePath, normalizeTrack } from '../helpers/index.js'; +import { getAddress, normalizeNumber, normalizePath, normalizeTrack } from '../helpers/index.js'; import type { TraceOption } from '../shared/index.js'; import PaletteError from './PaletteError.js'; @@ -71,9 +71,9 @@ export default class TraceError extends PaletteError { let col: number | undefined; let packageName = '[current]'; - const addressMatch = /\(([^)<>]+)\)(?:,\s:\d+:\d+\))?$/.exec(text); + const addressMatch = getAddress(text); if (addressMatch) { - address = addressMatch[1].trim(); + address = addressMatch.trim(); name = isEval ? 'eval' : text.slice(0, Math.max(0, text.length - address.length - 2)).trim(); } else { address = text; diff --git a/src/helpers/get-address.ts b/src/helpers/get-address.ts new file mode 100644 index 0000000..a3a49ba --- /dev/null +++ b/src/helpers/get-address.ts @@ -0,0 +1,25 @@ +function getAddress(text: string) { + let index = 0; + let start = -1; + let end = -1; + + while (index >= 0) { + const nextIndex = text.slice(Math.max(0, index)).indexOf('('); + index = nextIndex > 0 ? nextIndex + index + 1 : -1; + + if (nextIndex > 0) { + start = index; + } + } + + if (start === -1) return ''; + + index = text.slice(Math.max(0, start)).indexOf(')'); + if (index === -1) return ''; + + end = index + start; + + return text.slice(start, end); +} + +export default getAddress; diff --git a/src/helpers/index.ts b/src/helpers/index.ts index f3b811f..ee20fef 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -1,5 +1,5 @@ +export { default as getAddress } from './get-address.js'; export { default as isBgRgb } from './is-bg-rgb.js'; -export { default as isNil } from './is-nil.js'; export { default as isRgb } from './is-rgb.js'; export { default as isString } from './is-string.js'; export { default as normalizeNumber } from './normalize-number.js'; diff --git a/src/helpers/is-bg-rgb.ts b/src/helpers/is-bg-rgb.ts index 88299b3..6a3187c 100644 --- a/src/helpers/is-bg-rgb.ts +++ b/src/helpers/is-bg-rgb.ts @@ -1,14 +1,10 @@ +import isRgb from './is-rgb.js'; + function isBgRgb(value: string): value is string { if (!value.startsWith('bg')) return false; - const rgbRegular = /^bg\((?:\d{1,3},){2}\d{1,3}\)$/; - const numberRegular = /\d{1,3}/g; - - const numberMatch = value.match(numberRegular); - const validNumber = Boolean(numberMatch?.every((item) => Number(item) <= 255)); - - const validRgb = rgbRegular.test(value.replace(/\s/g, '')); - return validNumber && validRgb; + const rgbValue = value.slice(2).replace(/\s/g, ''); + return isRgb(rgbValue); } export default isBgRgb; diff --git a/src/helpers/is-nil.ts b/src/helpers/is-nil.ts deleted file mode 100644 index 79c8b9b..0000000 --- a/src/helpers/is-nil.ts +++ /dev/null @@ -1,6 +0,0 @@ -function isNil(value: unknown): value is undefined { - // eslint-disable-next-line eqeqeq, no-eq-null - return value == null; -} - -export default isNil; diff --git a/src/helpers/is-rgb.ts b/src/helpers/is-rgb.ts index 56baa8b..291b895 100644 --- a/src/helpers/is-rgb.ts +++ b/src/helpers/is-rgb.ts @@ -1,12 +1,12 @@ function isRgb(value: string): value is string { - const rgbRegular = /^\((?:\d{1,3},){2}\d{1,3}\)$/; - const numberRegular = /\d{1,3}/g; + const rgbRegular = /^\(\d{1,3},\d{1,3},\d{1,3}\)$/; + const validRgb = rgbRegular.test(value.replace(/\s/g, '')); + if (!validRgb) return false; + const numberRegular = /\d{1,3}/g; const numberMatch = value.match(numberRegular); - const validNumber = Boolean(numberMatch?.every((item) => Number(item) <= 255)); - const validRgb = rgbRegular.test(value.replace(/\s/g, '')); - return validNumber && validRgb; + return Boolean(numberMatch?.every((item) => Number(item) <= 255)); } export default isRgb; diff --git a/src/helpers/normalize-rgb.ts b/src/helpers/normalize-rgb.ts index d7b9f31..61def3e 100644 --- a/src/helpers/normalize-rgb.ts +++ b/src/helpers/normalize-rgb.ts @@ -1,5 +1,6 @@ function normalizeRgb(color: string, prefix = ''): [number, number, number] { - const rgb = color.replace(/[\s()]/g, '').replace(new RegExp(`^${prefix}`), ''); + const length = color.startsWith(prefix) ? prefix.length : 0; + const rgb = color.replace(/[\s()]/g, '').slice(Math.max(0, length)); const [r, g, b] = rgb.split(',').map(Number); diff --git a/test/helpers/get-address.spec.ts b/test/helpers/get-address.spec.ts new file mode 100644 index 0000000..ed520bd --- /dev/null +++ b/test/helpers/get-address.spec.ts @@ -0,0 +1,45 @@ +import { expect, test } from 'vitest'; +import getAddress from '../../src/helpers/get-address'; + +test('getAddress - should return empty string for invalid input', () => { + const text = ''; + const result = getAddress(text); + const expected = ''; + expect(result).toBe(expected); +}); + +test('getAddress - should return empty string if no parentheses are found', () => { + const text = 'This is a test string without parentheses'; + const result = getAddress(text); + const expected = ''; + expect(result).toBe(expected); +}); + +test('getAddress - should return empty string if parentheses are not closed', () => { + const text = 'This is a test string with an open parenthesis ( but no closing parenthesis'; + const result = getAddress(text); + const expected = ''; + expect(result).toBe(expected); +}); + +test('getAddress - should return the content within the parentheses', () => { + const text = 'This is a test string with (some content) within parentheses'; + const result = getAddress(text); + const expected = 'some content'; + expect(result).toBe(expected); +}); + +test('getAddress - should return the content within the last set of parentheses', () => { + const text = + 'This is a test string with (multiple sets of parentheses) but should return the content within the last one'; + const result = getAddress(text); + const expected = 'multiple sets of parentheses'; + expect(result).toBe(expected); +}); + +test('getAddress - should handle nested parentheses and return the content within the outermost set', () => { + const text = 'This is a test string with (nested (parentheses) within parentheses)'; + const result = getAddress(text); + const expected = 'parentheses'; + expect(result).toBe(expected); +}); diff --git a/test/utils/is-bg-rgb.spec.ts b/test/helpers/is-bg-rgb.spec.ts similarity index 100% rename from test/utils/is-bg-rgb.spec.ts rename to test/helpers/is-bg-rgb.spec.ts diff --git a/test/utils/is-rgb.spec.ts b/test/helpers/is-rgb.spec.ts similarity index 100% rename from test/utils/is-rgb.spec.ts rename to test/helpers/is-rgb.spec.ts diff --git a/test/utils/is-string.spec.ts b/test/helpers/is-string.spec.ts similarity index 100% rename from test/utils/is-string.spec.ts rename to test/helpers/is-string.spec.ts diff --git a/test/utils/normalize-number.spec.ts b/test/helpers/normalize-number.spec.ts similarity index 100% rename from test/utils/normalize-number.spec.ts rename to test/helpers/normalize-number.spec.ts diff --git a/test/utils/normalize-path.spec.ts b/test/helpers/normalize-path.spec.ts similarity index 100% rename from test/utils/normalize-path.spec.ts rename to test/helpers/normalize-path.spec.ts diff --git a/test/utils/normalize-rgb.spec.ts b/test/helpers/normalize-rgb.spec.ts similarity index 100% rename from test/utils/normalize-rgb.spec.ts rename to test/helpers/normalize-rgb.spec.ts diff --git a/test/utils/normalize-track.spec.ts b/test/helpers/normalize-track.spec.ts similarity index 100% rename from test/utils/normalize-track.spec.ts rename to test/helpers/normalize-track.spec.ts diff --git a/test/utils/is-nil.spec.ts b/test/utils/is-nil.spec.ts deleted file mode 100644 index 94ee692..0000000 --- a/test/utils/is-nil.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { expect, test } from 'vitest'; -import isNil from '../../src/helpers/is-nil'; - -test('isNil - should return true for null value', () => { - const value = null; - const result = isNil(value); - expect(result).toBe(true); -}); - -test('isNil - should return true for undefined value', () => { - const value = undefined; - const result = isNil(value); - expect(result).toBe(true); -}); - -test('isNil - should return false for non-null and non-undefined values', () => { - const value = 0; - const result = isNil(value); - expect(result).toBe(false); -}); - -test('isNil - should return false for empty string value', () => { - const value = ''; - const result = isNil(value); - expect(result).toBe(false); -}); - -test('isNil - should return false for false boolean value', () => { - const value = false; - const result = isNil(value); - expect(result).toBe(false); -}); - -test('isNil - should return false for NaN value', () => { - const value = NaN; - const result = isNil(value); - expect(result).toBe(false); -});