diff --git a/biome.json b/biome.json index 4dc84017d3..47f23895db 100644 --- a/biome.json +++ b/biome.json @@ -25,7 +25,8 @@ "suspicious": { "noExplicitAny": "off", "noExtraNonNullAssertion": "off", - "noRedeclare": "off" + "noRedeclare": "off", + "noPrototypeBuiltins": "off" } }, "ignore": [ diff --git a/package.json b/package.json index eefb006330..5d4eccb011 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,6 @@ "express-rate-limit": "^6.6.0", "express-session": "^1.17.1", "fast-json-patch": "^3.1.0", - "gravatar-url": "^3.1.0", "hash-sum": "^2.0.0", "helmet": "^6.0.0", "http-errors": "^2.0.0", diff --git a/src/lib/addons/feature-event-formatter-md.ts b/src/lib/addons/feature-event-formatter-md.ts index f975a91679..bcd6d8b50c 100644 --- a/src/lib/addons/feature-event-formatter-md.ts +++ b/src/lib/addons/feature-event-formatter-md.ts @@ -531,11 +531,10 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter { SEMVER_LT: 'is a SemVer less than', }; const formatConstraint = (constraint: IConstraint) => { - const val = Object.hasOwn(constraint, 'value') + const val = constraint.hasOwnProperty('value') ? constraint.value : `(${constraint.values?.join(',')})`; - const operator = Object.hasOwn( - constraintOperatorDescriptions, + const operator = constraintOperatorDescriptions.hasOwnProperty( constraint.operator, ) ? constraintOperatorDescriptions[constraint.operator] diff --git a/src/lib/features/feature-toggle/feature-toggle-service.ts b/src/lib/features/feature-toggle/feature-toggle-service.ts index aa22b3ff0d..6f8a6208ed 100644 --- a/src/lib/features/feature-toggle/feature-toggle-service.ts +++ b/src/lib/features/feature-toggle/feature-toggle-service.ts @@ -1321,7 +1321,7 @@ class FeatureToggleService { if ( replaceGroupId && s.parameters && - Object.hasOwn(s.parameters, 'groupId') + s.parameters.hasOwnProperty('groupId') ) { s.parameters.groupId = newFeatureName; } diff --git a/src/lib/types/user.test.ts b/src/lib/types/user.test.ts index 44f815cc91..d6cbfd31cb 100644 --- a/src/lib/types/user.test.ts +++ b/src/lib/types/user.test.ts @@ -5,7 +5,7 @@ test('should create user', () => { expect(user.name).toBe('ole'); expect(user.email).toBe('some@email.com'); expect(user.imageUrl).toBe( - 'https://gravatar.com/avatar/d8ffeba65ee5baf57e4901690edc8e1b?size=42&default=retro', + 'https://gravatar.com/avatar/676212ff796c79a3c06261eb10e3f455aa93998ee6e45263da13679c74b1e674?s=42&d=retro&r=g', ); }); @@ -20,7 +20,7 @@ test('should create user, all fields', () => { expect(user.username).toBe('admin'); expect(user.email).toBe('some@email.com'); expect(user.imageUrl).toBe( - 'https://gravatar.com/avatar/d8ffeba65ee5baf57e4901690edc8e1b?size=42&default=retro', + 'https://gravatar.com/avatar/676212ff796c79a3c06261eb10e3f455aa93998ee6e45263da13679c74b1e674?s=42&d=retro&r=g', ); }); @@ -47,7 +47,7 @@ test('Should create user with only username defined', () => { const user = new User({ id: 133, username: 'some-user' }); expect(user.username).toBe('some-user'); expect(user.imageUrl).toBe( - 'https://gravatar.com/avatar/140fd5a002fb8d728a9848f8c9fcea2a?size=42&default=retro', + 'https://gravatar.com/avatar/7e90ac329986624ba9929659913354473c6f965d5b559704409e3f933c0643b7?s=42&d=retro&r=g', ); }); diff --git a/src/lib/util/generateImageUrl.test.ts b/src/lib/util/generateImageUrl.test.ts new file mode 100644 index 0000000000..2dfe9b367e --- /dev/null +++ b/src/lib/util/generateImageUrl.test.ts @@ -0,0 +1,21 @@ +import { generateImageUrl } from './generateImageUrl'; + +describe('Gravatar image url', () => { + it('generates the correct sha-256 hash for gravatars test idents', () => { + expect(generateImageUrl({ email: 'MyEmailAddress@example.com' })).toBe( + 'https://gravatar.com/avatar/84059b07d4be67b806386c0aad8070a23f18836bbaae342275dc0a83414c32ee?s=42&d=retro&r=g', + ); + }); + it('lowercases and trims all emails', () => { + const upperCaseAndLeadingSpace = ' helloWorld@example.com'; + const upperCaseAndTrailingSpace = 'helloWorld@exAMPLE.com '; + const lowerCaseAndNoSpaces = 'helloworld@example.com'; + const uCALSHash = generateImageUrl({ email: upperCaseAndLeadingSpace }); + const uCATSHash = generateImageUrl({ + email: upperCaseAndTrailingSpace, + }); + const lCANSHash = generateImageUrl({ email: lowerCaseAndNoSpaces }); + expect(uCALSHash).toBe(uCATSHash); + expect(uCATSHash).toBe(lCANSHash); + }); +}); diff --git a/src/lib/util/generateImageUrl.ts b/src/lib/util/generateImageUrl.ts index 77110194a2..9e7382a91d 100644 --- a/src/lib/util/generateImageUrl.ts +++ b/src/lib/util/generateImageUrl.ts @@ -1,11 +1,17 @@ -import gravatarUrl from 'gravatar-url'; +import { createHash } from 'crypto'; +const base: string = 'https://gravatar.com/avatar'; export const generateImageUrl = (user: { - email: string; - username: string; - id: number; -}): string => - gravatarUrl(user.email || user.username || String(user.id), { - size: 42, - default: 'retro', - }); + email?: string; + username?: string; + id?: number; +}): string => { + let ident = user.email || user.username || String(user.id); + if (ident.indexOf('@')) { + ident = ident.toLowerCase().trim(); + } else { + ident = ident.trim(); + } + const identHash = createHash('sha256').update(ident).digest('hex'); + return `${base}/${identHash}?s=42&d=retro&r=g`; +}; diff --git a/yarn.lock b/yarn.lock index 0a7ab89acb..0b8665269e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1859,11 +1859,6 @@ bluebird@^3.1.1, bluebird@^3.7.2: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -blueimp-md5@^2.10.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz#b53feea5498dcb53dc6ec4b823adb84b729c4af0" - integrity sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w== - body-parser@1.20.1: version "1.20.1" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" @@ -3384,14 +3379,6 @@ graceful-fs@^4.2.10, graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -gravatar-url@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/gravatar-url/-/gravatar-url-3.1.0.tgz#0cbeedab7c00a7bc9b627b3716e331359efcc999" - integrity sha512-+lOs7Rz1A051OqdqE8Tm4lmeyVgkqH8c6ll5fv///ncdIaL+XnOFmKAB70ix1du/yj8c3EWKbP6OhKjihsBSfA== - dependencies: - md5-hex "^3.0.1" - type-fest "^0.8.1" - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -4684,13 +4671,6 @@ map-stream@~0.1.0: resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== -md5-hex@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-3.0.1.tgz#be3741b510591434b2784d79e556eefc2c9a8e5c" - integrity sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw== - dependencies: - blueimp-md5 "^2.10.0" - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -6631,11 +6611,6 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - type-fest@^1.0.1, type-fest@^1.2.1, type-fest@^1.2.2: version "1.4.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1"