mirror of
1
2
Fork 0
ucaptcha/src/helpers/verifyImage.js

138 lines
2.9 KiB
JavaScript

import utils from './utils.js';
import db from '../helpers/db/index.js';
import {
MAX_SESSION_TIME,
MAX_TOKEN_TIME,
TAGS,
MAT_TOLERANCE,
SCORE_TOLERANCE,
} from '../R.js';
import pickRandomFile from './pickRandomFile.js';
import logger from './logging.js';
/** @typedef {import("../models/Mat.js").DbMat} DbMat */
/**
* Update the image mat
* @param {DbMat} imageMat
* @param {Array<number>} userMat
* @param {string} image
*/
function updateMat(imageMat, userMat, image) {
const updatedMat = [...imageMat.mat];
for (let i = 0; i < imageMat.mat.length; i++) {
const delta = 0.1;
if (userMat[i] === 1) {
if (updatedMat[i] + delta > 1) {
updatedMat[i] = 1.0;
} else {
updatedMat[i] += delta;
}
} else {
if (updatedMat[i] < delta) {
updatedMat[i] = 0.0;
} else {
updatedMat[i] -= delta;
}
}
}
logger.debug({oldMat: imageMat.mat});
logger.debug({newMat: updatedMat});
imageMat.num += 1;
imageMat.mat = updatedMat;
db.updateMat(image, imageMat);
}
/**
* @param {string} sessionId
* @param {Array<number>} userMat
* @return {Promise<Session>}
*/
export default async function verifyImage(sessionId, userMat) {
const result = await db.getSession(sessionId);
/** @type {import('../models/Session').DbSession} */
logger.debug({session: result});
if (!result) {
return [
'Session Expired',
null,
];
}
let {image, score, websiteKey} = result;
/** @type {import('../models/Session.js').Session)} */
const session = {
sessionId,
websiteKey,
};
const imageMat = await db.getMat(image);
logger.debug({imageMat});
const trueArgmax = utils.argmaxThresh(imageMat.mat, MAT_TOLERANCE).join(',');
const userArgmax = utils.argmaxThresh(userMat, 1).join(',');
updateMat(imageMat, userMat, image);
logger.debug({trueArgmax});
logger.debug({userArgmax});
const delta = imageMat.num * 0.05;
if (userArgmax !== trueArgmax) {
// TODO: Decrease the score based on mat dispersion
// High variance = small reduction
// Low variance = high reduction
score -= delta;
} else {
score += delta;
}
if (score >= SCORE_TOLERANCE) {
const nonce = utils.randomBytes(24);
session.token = nonce;
await db.deleteSession(sessionId);
await db.setToken({
token: nonce,
websiteKey,
score,
expiry: Date.now() + MAX_TOKEN_TIME,
});
return [
null,
session,
{
removeCookie: websiteKey,
},
];
}
const newImageFilename = await pickRandomFile();
// TODO: Make imageTagId dynamic
const newImageTagId = 1;
const update = Object.assign(result, {
image: newImageFilename.split('.')[0],
obj: newImageTagId,
score,
expiry: Date.now() + MAX_SESSION_TIME,
});
logger.debug({update});
await db.updateSession(sessionId, update);
session.imageTag = TAGS[newImageTagId];
return [
null,
session,
];
}