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:
Danny Liu 2024-04-05 21:00:48 -07:00 committed by GitHub
commit a68b95c95f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 55 additions and 3 deletions

View file

@ -6,7 +6,7 @@
import { updateUserProfile } from '$lib/apis/auths';
import UpdatePassword from './Account/UpdatePassword.svelte';
import { generateInitialsImage } from '$lib/utils';
import { generateInitialsImage, canvasPixelTest } from '$lib/utils';
import { copyToClipboard } from '$lib/utils';
const i18n = getContext('i18n');
@ -148,7 +148,13 @@
<button
class=" text-xs text-gray-600"
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
>
</div>

View file

@ -96,12 +96,52 @@ export const getGravatarURL = (email) => {
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) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 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.fillRect(0, 0, canvas.width, canvas.height);

View file

@ -5,7 +5,7 @@
import { WEBUI_NAME, config, user } from '$lib/stores';
import { onMount, getContext } from 'svelte';
import { toast } from 'svelte-sonner';
import { generateInitialsImage } from '$lib/utils';
import { generateInitialsImage, canvasPixelTest } from '$lib/utils';
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);
};