forked from open-webui/open-webui
		
	Merge pull request #1 from lainedfles/feat/profile-image-initials-rfp
Feat/profile image initials with fix for rfp & canvas spoofing
This commit is contained in:
		
						commit
						a68b95c95f
					
				
					 3 changed files with 55 additions and 3 deletions
				
			
		|  | @ -6,7 +6,7 @@ | ||||||
| 	import { updateUserProfile } from '$lib/apis/auths'; | 	import { updateUserProfile } from '$lib/apis/auths'; | ||||||
| 
 | 
 | ||||||
| 	import UpdatePassword from './Account/UpdatePassword.svelte'; | 	import UpdatePassword from './Account/UpdatePassword.svelte'; | ||||||
| 	import { generateInitialsImage } from '$lib/utils'; | 	import { generateInitialsImage, canvasPixelTest } from '$lib/utils'; | ||||||
| 	import { copyToClipboard } from '$lib/utils'; | 	import { copyToClipboard } from '$lib/utils'; | ||||||
| 
 | 
 | ||||||
| 	const i18n = getContext('i18n'); | 	const i18n = getContext('i18n'); | ||||||
|  | @ -148,7 +148,13 @@ | ||||||
| 				<button | 				<button | ||||||
| 					class=" text-xs text-gray-600" | 					class=" text-xs text-gray-600" | ||||||
| 					on:click={async () => { | 					on:click={async () => { | ||||||
| 						profileImageUrl = generateInitialsImage(name); | 						if (canvasPixelTest()) { | ||||||
|  | 							profileImageUrl = generateInitialsImage(name); | ||||||
|  | 						} else { | ||||||
|  | 							toast.error("Canvas pixel test failed, fingerprint evasion likely. Disable fingerprint evasion and try again!", { | ||||||
|  | 								autoClose: 1000 * 10, | ||||||
|  | 							}); | ||||||
|  | 						} | ||||||
| 					}}>{$i18n.t('Use Gravatar')}</button | 					}}>{$i18n.t('Use Gravatar')}</button | ||||||
| 				> | 				> | ||||||
| 			</div> | 			</div> | ||||||
|  |  | ||||||
|  | @ -96,12 +96,52 @@ export const getGravatarURL = (email) => { | ||||||
| 	return `https://www.gravatar.com/avatar/${hash}`; | 	return `https://www.gravatar.com/avatar/${hash}`; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | export const canvasPixelTest = () => { | ||||||
|  | 	// Test a 1x1 pixel to potentially identify browser/plugin fingerprint blocking or spoofing
 | ||||||
|  | 	// Inspiration: https://github.com/kkapsner/CanvasBlocker/blob/master/test/detectionTest.js
 | ||||||
|  | 	const canvas = document.createElement("canvas"); | ||||||
|  | 	const ctx = canvas.getContext('2d'); | ||||||
|  | 	canvas.height = 1; | ||||||
|  | 	canvas.width = 1; | ||||||
|  | 	const imageData = new ImageData(canvas.width, canvas.height); | ||||||
|  | 	const pixelValues = imageData.data; | ||||||
|  | 
 | ||||||
|  | 	// Generate RGB test data
 | ||||||
|  | 	for (let i = 0; i < imageData.data.length; i += 1){ | ||||||
|  | 		if (i % 4 !== 3){ | ||||||
|  | 			pixelValues[i] = Math.floor(256 * Math.random()); | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			pixelValues[i] = 255; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ctx.putImageData(imageData, 0, 0); | ||||||
|  | 	const p = ctx.getImageData(0, 0, canvas.width, canvas.height).data; | ||||||
|  | 
 | ||||||
|  | 	// Read RGB data and fail if unmatched
 | ||||||
|  | 	for (let i = 0; i < p.length; i += 1){ | ||||||
|  | 		if (p[i] !== pixelValues[i]){ | ||||||
|  | 			console.log("canvasPixelTest: Wrong canvas pixel RGB value detected:", p[i], "at:", i, "expected:", pixelValues[i]); | ||||||
|  | 			console.log("canvasPixelTest: Canvas blocking or spoofing is likely"); | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export const generateInitialsImage = (name) => { | export const generateInitialsImage = (name) => { | ||||||
| 	const canvas = document.createElement('canvas'); | 	const canvas = document.createElement('canvas'); | ||||||
| 	const ctx = canvas.getContext('2d'); | 	const ctx = canvas.getContext('2d'); | ||||||
| 	canvas.width = 100; | 	canvas.width = 100; | ||||||
| 	canvas.height = 100; | 	canvas.height = 100; | ||||||
| 
 | 
 | ||||||
|  | 	if (!canvasPixelTest()) { | ||||||
|  | 		console.log("generateInitialsImage: failed pixel test, fingerprint evasion is likely. Using default image."); | ||||||
|  | 		return '/user.png'; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	ctx.fillStyle = '#F39C12'; | 	ctx.fillStyle = '#F39C12'; | ||||||
| 	ctx.fillRect(0, 0, canvas.width, canvas.height); | 	ctx.fillRect(0, 0, canvas.width, canvas.height); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| 	import { WEBUI_NAME, config, user } from '$lib/stores'; | 	import { WEBUI_NAME, config, user } from '$lib/stores'; | ||||||
| 	import { onMount, getContext } from 'svelte'; | 	import { onMount, getContext } from 'svelte'; | ||||||
| 	import { toast } from 'svelte-sonner'; | 	import { toast } from 'svelte-sonner'; | ||||||
| 	import { generateInitialsImage } from '$lib/utils'; | 	import { generateInitialsImage, canvasPixelTest } from '$lib/utils'; | ||||||
| 
 | 
 | ||||||
| 	const i18n = getContext('i18n'); | 	const i18n = getContext('i18n'); | ||||||
| 
 | 
 | ||||||
|  | @ -43,6 +43,12 @@ | ||||||
| 			} | 			} | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
|  | 		if (!canvasPixelTest()) { | ||||||
|  | 			toast.error("Canvas pixel test failed, fingerprint evasion likely. Default image used.", { | ||||||
|  | 				autoClose: 1000 * 10, | ||||||
|  | 			}); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		await setSessionUser(sessionUser); | 		await setSessionUser(sessionUser); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Danny Liu
						Danny Liu