From ed0b9cd842ec902e7c3d5212d981d01157eb71a4 Mon Sep 17 00:00:00 2001 From: Siddharth Gelera Date: Fri, 7 Nov 2025 18:16:23 +0530 Subject: [PATCH 1/2] feat: add patternHasher utility for variable hashing This utility function hashes input strings containing variables and allows for restoration of the original string. It includes support for custom matchers and handles cases where no variables are matched. --- .../src/utils/template-hasher.spec.ts | 32 ++++++++++++ .../bruno-common/src/utils/template-hasher.ts | 49 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 packages/bruno-common/src/utils/template-hasher.spec.ts create mode 100644 packages/bruno-common/src/utils/template-hasher.ts diff --git a/packages/bruno-common/src/utils/template-hasher.spec.ts b/packages/bruno-common/src/utils/template-hasher.spec.ts new file mode 100644 index 0000000000..e4ad1aeeea --- /dev/null +++ b/packages/bruno-common/src/utils/template-hasher.spec.ts @@ -0,0 +1,32 @@ +import { describe, it, expect } from '@jest/globals'; +import { patternHasher } from './template-hasher'; + +describe('patternHasher', () => { + it('hashes and restore are mathematically reproducible', () => { + const originalUrl = '{{host}}.example.com'; + const { hashed, restore } = patternHasher(originalUrl); + expect(hashed).toMatchInlineSnapshot(`"bruno-var-hash--163450413.example.com"`); + expect(restore(hashed)).toEqual(originalUrl); + }); + + it('hashes more than once', () => { + const originalUrl = '{{host}}.example.{{new}}'; + const { hashed, restore } = patternHasher(originalUrl); + expect(hashed).toMatchInlineSnapshot(`"bruno-var-hash--163450413.example.bruno-var-hash-652560383"`); + expect(restore(hashed)).toEqual(originalUrl); + }); + + it('allows custom matchers', () => { + const originalUrl = '$name.example.com'; + const { hashed, restore } = patternHasher(originalUrl, /\$(\w+)/); + expect(hashed).toMatchInlineSnapshot(`"bruno-var-hash-180907786.example.com"`); + expect(restore(hashed)).toEqual(originalUrl); + }); + + it('ignore unless matched', () => { + const originalUrl = '$name.example.com'; + const { hashed, restore } = patternHasher(originalUrl); + expect(hashed).toMatchInlineSnapshot(`"$name.example.com"`); + expect(restore(hashed)).toEqual(originalUrl); + }); +}); diff --git a/packages/bruno-common/src/utils/template-hasher.ts b/packages/bruno-common/src/utils/template-hasher.ts new file mode 100644 index 0000000000..9f71f7d7a8 --- /dev/null +++ b/packages/bruno-common/src/utils/template-hasher.ts @@ -0,0 +1,49 @@ +const VARIABLE_REGEX = /\{\{([^}]+)\}\}/g; + +/** + * Was implemented specifically for request.url where the url might have variables + * that might need to be sanitised before being passed to a URL validator that doesn't + * allow special characters that bruno uses as variables (`{{var_name}}`) + * + * The function replaces the input string with a unique hash that can be restored + * later by the helper returned by this function + */ +export function patternHasher(input: string, pattern: string | RegExp = VARIABLE_REGEX) { + const usableRegex = new RegExp(pattern, 'g'); + function hash(toHash: string) { + let hash = 5381; + let c; + for (let i = 0; i < toHash.length; i++) { + c = toHash.charCodeAt(i); + hash = (hash << 5) + hash + c; + } + return '' + hash; + } + + const prefix = `bruno-var-hash-`; + const hashToOriginal: Record = {}; + let result = input; + let hashed = false; + if (usableRegex.test(input)) { + hashed = true; + result = input.replace(usableRegex, function (matchedVar) { + const hashedValue = `${prefix}${hash(matchedVar)}`; + hashToOriginal[hashedValue] = matchedVar; + return hashedValue; + }); + } + return { + hashed: result, + restore(current: string) { + if (!hashed) { + return current; + } + let clone = current; + for (const hash in hashToOriginal) { + const value = hashToOriginal[hash]; + clone = clone.replace(hash, value); + } + return clone; + } + }; +} From 80c80b737aff38cf9aaff362f5b8b8e483802344 Mon Sep 17 00:00:00 2001 From: Sid Date: Fri, 7 Nov 2025 18:24:39 +0530 Subject: [PATCH 2/2] Update packages/bruno-common/src/utils/template-hasher.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/bruno-common/src/utils/template-hasher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bruno-common/src/utils/template-hasher.ts b/packages/bruno-common/src/utils/template-hasher.ts index 9f71f7d7a8..290c8a3e9a 100644 --- a/packages/bruno-common/src/utils/template-hasher.ts +++ b/packages/bruno-common/src/utils/template-hasher.ts @@ -15,7 +15,7 @@ export function patternHasher(input: string, pattern: string | RegExp = VARIABLE let c; for (let i = 0; i < toHash.length; i++) { c = toHash.charCodeAt(i); - hash = (hash << 5) + hash + c; + hash = ((hash << 5) + hash + c) | 0; } return '' + hash; }