From 91432097fd0d068bb45e45d5d791709f050a4576 Mon Sep 17 00:00:00 2001 From: Mayank Date: Sat, 22 Jun 2024 21:47:18 +0530 Subject: [PATCH] fix: prototype pollution alert from AI --- lib/src/index.ts | 12 ++++++------ lib/src/utils.ts | 38 ++++++++++++++++++-------------------- lib/tsup.config.ts | 32 -------------------------------- 3 files changed, 24 insertions(+), 58 deletions(-) diff --git a/lib/src/index.ts b/lib/src/index.ts index 3c784200..cab23f31 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -23,13 +23,13 @@ export type { SetterArgType, SetStateAction, Plugin } from "./utils"; const useRGS = (key: string, value?: ValueType): [T, SetStateAction] => { /** Initialize the named store when invoked for the first time. */ if (!globalRGS[key]) - globalRGS[key] = [ + globalRGS[key] = { // @ts-expect-error -- ok - typeof value === "function" ? value() : value, - [], - createSetter(key), - createSubcriber(key), - ]; + v: typeof value === "function" ? value() : value, + l: [], + s: createSetter(key), + u: createSubcriber(key), + }; return createHook(key); }; diff --git a/lib/src/utils.ts b/lib/src/utils.ts index 1ee02f7d..5fa4e6a7 100644 --- a/lib/src/utils.ts +++ b/lib/src/utils.ts @@ -10,8 +10,7 @@ export type ValueType = T | (() => T); /** * This is a hack to reduce lib size + readability + not encouraging direct access to globalThis */ -const [VALUE, LISTENERS, SETTER, SUBSCRIBER] = [0, 1, 2, 3, 4]; -type RGS = [unknown, Listener[], SetStateAction | null, Subscriber]; +type RGS = { v: unknown; l: Listener[]; s: SetStateAction | null; u: Subscriber }; declare global { // eslint-disable-next-line no-var -- var required for global declaration. @@ -24,16 +23,15 @@ globalThisForBetterMinification.rgs = {}; export const globalRGS = globalThisForBetterMinification.rgs; /** trigger all listeners */ -const triggerListeners = (rgs: RGS) => - (rgs[LISTENERS] as Listener[]).forEach(listener => listener()); +const triggerListeners = (rgs: RGS) => rgs.l.forEach(listener => listener()); /** craete subscriber function to subscribe to the store. */ export const createSubcriber = (key: string): Subscriber => { return listener => { const rgs = globalRGS[key] as RGS; - (rgs[LISTENERS] as Listener[]).push(listener); + (rgs.l as Listener[]).push(listener); return () => { - rgs[LISTENERS] = (rgs[LISTENERS] as Listener[]).filter(l => l !== listener); + rgs.l = (rgs.l as Listener[]).filter(l => l !== listener); }; }; }; @@ -42,8 +40,8 @@ export const createSubcriber = (key: string): Subscriber => { export const createSetter = (key: string): SetStateAction => { return val => { const rgs = globalRGS[key] as RGS; - rgs[VALUE] = typeof val === "function" ? val(rgs[VALUE] as T) : val; - (rgs[LISTENERS] as Listener[]).forEach(listener => listener()); + rgs.v = typeof val === "function" ? val(rgs.v as T) : val; + (rgs.l as Listener[]).forEach(listener => listener()); }; }; @@ -51,9 +49,9 @@ export const createSetter = (key: string): SetStateAction => { export const createHook = (key: string): [T, SetStateAction] => { const rgs = globalRGS[key] as RGS; /** This function is called by react to get the current stored value. */ - const getSnapshot = () => rgs[VALUE] as T; - const val = useSyncExternalStore(rgs[SUBSCRIBER] as Subscriber, getSnapshot, getSnapshot); - return [val, rgs[SETTER] as SetStateAction]; + const getSnapshot = () => rgs.v as T; + const val = useSyncExternalStore(rgs.u as Subscriber, getSnapshot, getSnapshot); + return [val, rgs.s as SetStateAction]; }; type Mutate = (value?: T) => void; @@ -69,12 +67,12 @@ const initPlugins = async (key: string, plugins: Plugin[]) => { const rgs = globalRGS[key] as RGS; /** Mutate function to update the value */ const mutate: Mutate = newValue => { - rgs[VALUE] = newValue; + rgs.v = newValue; triggerListeners(rgs); }; for (const plugin of plugins) { /** Next plugins initializer will get the new value if updated by previous one */ - await plugin.init?.(key, rgs[VALUE] as T, mutate); + await plugin.init?.(key, rgs.v as T, mutate); } allExtentionsInitialized = true; }; @@ -89,7 +87,7 @@ export const initWithPlugins = ( value = value instanceof Function ? value() : value; if (doNotInit) { /** You will not have access to the setter until initialized */ - globalRGS[key] = [value, [], null, createSubcriber(key)]; + globalRGS[key] = { v: value, l: [], s: null, u: createSubcriber(key) }; return; } /** setter function to set the state. */ @@ -97,16 +95,16 @@ export const initWithPlugins = ( /** Do not allow mutating the store before all extentions are initialized */ if (!allExtentionsInitialized) return; const rgs = globalRGS[key] as RGS; - rgs[VALUE] = val instanceof Function ? val(rgs[VALUE] as T) : val; + rgs.v = val instanceof Function ? val(rgs.v as T) : val; triggerListeners(rgs); - plugins.forEach(plugin => plugin.onChange?.(key, rgs[VALUE] as T)); + plugins.forEach(plugin => plugin.onChange?.(key, rgs.v as T)); }; const rgs = globalRGS[key]; if (rgs) { - rgs[VALUE] = value; - rgs[SETTER] = setterWithPlugins; - } else globalRGS[key] = [value, [], setterWithPlugins, createSubcriber(key)]; + rgs.v = value; + rgs.s = setterWithPlugins; + } else globalRGS[key] = { v: value, l: [], s: setterWithPlugins, u: createSubcriber(key) }; initPlugins(key, plugins); }; @@ -133,6 +131,6 @@ export const useRGSWithPlugins = ( plugins?: Plugin[], doNotInit = false, ): [T, SetStateAction] => { - if (!globalRGS[key]?.[SETTER]) initWithPlugins(key, value, plugins, doNotInit); + if (!globalRGS[key]?.s) initWithPlugins(key, value, plugins, doNotInit); return createHook(key); }; diff --git a/lib/tsup.config.ts b/lib/tsup.config.ts index f36865bd..054652b9 100644 --- a/lib/tsup.config.ts +++ b/lib/tsup.config.ts @@ -9,36 +9,4 @@ export default defineConfig(options => ({ clean: !options.watch, bundle: true, minify: !options.watch, - esbuildPlugins: [ - { - name: "improve-minify", - setup(build) { - build.onLoad({ filter: /utils.ts/ }, args => { - let contents = fs.readFileSync(args.path, "utf8"); - const lines = contents.split("\n"); - const hackLine = lines.find(line => line.startsWith("const [VALUE,")); - - if (!hackLine) return { contents, loader: "ts" }; - /** remove hackLine */ - contents = contents.replace(hackLine, ""); - - /** clean up */ - const tokens = hackLine - .replace("const", "") - .split("=")[0] - .trim() - .replace(/\]|\[/g, "") - .split(",") - .map((t, i) => ({ token: t.trim(), i })); - - tokens.sort((a, b) => b.token.length - a.token.length); - - for (const t of tokens) { - contents = contents.replace(new RegExp(`${t.token}`, "g"), String(t.i)); - } - return { contents, loader: "ts" }; - }); - }, - }, - ], }));