From 141f105b7954f8c43face023722c70328f6c34a0 Mon Sep 17 00:00:00 2001 From: mini-bomba <55105495+mini-bomba@users.noreply.github.com> Date: Tue, 29 Aug 2023 15:48:34 +0200 Subject: [PATCH] fix dearrow bans --- src/routes/postBranding.ts | 20 +++- test/cases/postBranding.ts | 207 ++++++++++++++++++++++++++++++++++++- 2 files changed, 221 insertions(+), 6 deletions(-) diff --git a/src/routes/postBranding.ts b/src/routes/postBranding.ts index 06572c8..486e915 100644 --- a/src/routes/postBranding.ts +++ b/src/routes/postBranding.ts @@ -14,6 +14,7 @@ import crypto from "crypto"; import { QueryCacher } from "../utils/queryCacher"; import { acquireLock } from "../utils/redisLock"; import { hasFeature } from "../utils/features"; +import { checkBanStatus } from "../utils/checkBan"; enum BrandingType { Title, @@ -43,6 +44,7 @@ export async function postBranding(req: Request, res: Response) { const isVip = await isUserVIP(hashedUserID); const hashedVideoID = await getHashCache(videoID, 1); const hashedIP = await getHashCache(getIP(req) + config.globalSalt as IPAddress); + const isBanned = await checkBanStatus(hashedUserID, hashedIP); const lock = await acquireLock(`postBranding:${videoID}.${hashedUserID}`); if (!lock.status) { @@ -61,7 +63,11 @@ export async function postBranding(req: Request, res: Response) { await Promise.all([(async () => { if (title) { + // ignore original submissions from banned users - hiding those would cause issues + if (title.original && isBanned) return; + const existingUUID = (await db.prepare("get", `SELECT "UUID" from "titles" where "videoID" = ? AND "title" = ?`, [videoID, title.title]))?.UUID; + if (existingUUID != undefined && isBanned) return; // ignore votes on existing details from banned users const UUID = existingUUID || crypto.randomUUID(); const existingVote = await handleExistingVotes(BrandingType.Title, videoID, hashedUserID, UUID, hashedIP, voteType); @@ -72,8 +78,8 @@ export async function postBranding(req: Request, res: Response) { [videoID, title.title, title.original ? 1 : 0, hashedUserID, service, hashedVideoID, now, UUID]); const verificationValue = await getVerificationValue(hashedUserID, isVip); - await db.prepare("run", `INSERT INTO "titleVotes" ("UUID", "votes", "locked", "shadowHidden", "verification") VALUES (?, 0, ?, 0, ?);`, - [UUID, isVip ? 1 : 0, verificationValue]); + await db.prepare("run", `INSERT INTO "titleVotes" ("UUID", "votes", "locked", "shadowHidden", "verification") VALUES (?, 0, ?, ?, ?);`, + [UUID, isVip ? 1 : 0, isBanned ? 1 : 0, verificationValue]); await verifyOldSubmissions(hashedUserID, verificationValue); } @@ -85,10 +91,14 @@ export async function postBranding(req: Request, res: Response) { } })(), (async () => { if (thumbnail) { + // ignore original submissions from banned users - hiding those would cause issues + if (thumbnail.original && isBanned) return; + const existingUUID = thumbnail.original ? (await db.prepare("get", `SELECT "UUID" from "thumbnails" where "videoID" = ? AND "original" = 1`, [videoID]))?.UUID : (await db.prepare("get", `SELECT "thumbnails"."UUID" from "thumbnailTimestamps" JOIN "thumbnails" ON "thumbnails"."UUID" = "thumbnailTimestamps"."UUID" WHERE "thumbnailTimestamps"."timestamp" = ? AND "thumbnails"."videoID" = ?`, [(thumbnail as TimeThumbnailSubmission).timestamp, videoID]))?.UUID; + if (existingUUID != undefined && isBanned) return; // ignore votes on existing details from banned users const UUID = existingUUID || crypto.randomUUID(); const existingVote = await handleExistingVotes(BrandingType.Thumbnail, videoID, hashedUserID, UUID, hashedIP, voteType); @@ -98,8 +108,8 @@ export async function postBranding(req: Request, res: Response) { await db.prepare("run", `INSERT INTO "thumbnails" ("videoID", "original", "userID", "service", "hashedVideoID", "timeSubmitted", "UUID") VALUES (?, ?, ?, ?, ?, ?, ?)`, [videoID, thumbnail.original ? 1 : 0, hashedUserID, service, hashedVideoID, now, UUID]); - await db.prepare("run", `INSERT INTO "thumbnailVotes" ("UUID", "votes", "locked", "shadowHidden") VALUES (?, 0, ?, 0)`, - [UUID, isVip ? 1 : 0]); + await db.prepare("run", `INSERT INTO "thumbnailVotes" ("UUID", "votes", "locked", "shadowHidden") VALUES (?, 0, ?, ?)`, + [UUID, isVip ? 1 : 0, isBanned ? 1 : 0]); if (!thumbnail.original) { await db.prepare("run", `INSERT INTO "thumbnailTimestamps" ("UUID", "timestamp") VALUES (?, ?)`, @@ -190,4 +200,4 @@ async function verifyOldSubmissions(hashedUserID: HashedUserID, verification: nu await db.prepare("run", `UPDATE "titleVotes" as tv SET "verification" = ? FROM "titles" WHERE "titles"."UUID" = tv."UUID" AND "titles"."userID" = ? AND tv."verification" < ?`, [verification, hashedUserID, verification]); } } -} \ No newline at end of file +} diff --git a/test/cases/postBranding.ts b/test/cases/postBranding.ts index 5119b4c..d39797f 100644 --- a/test/cases/postBranding.ts +++ b/test/cases/postBranding.ts @@ -16,6 +16,7 @@ describe("postBranding", () => { const userID6 = `PostBrandingUser6${".".repeat(16)}`; const userID7 = `PostBrandingUser7${".".repeat(16)}`; const userID8 = `PostBrandingUser8${".".repeat(16)}`; + const bannedUser = `BannedPostBrandingUser${".".repeat(16)}`; const endpoint = "/api/branding"; @@ -36,6 +37,9 @@ describe("postBranding", () => { await db.prepare("run", insertVipUserQuery, [getHash(vipUser)]); await db.prepare("run", insertVipUserQuery, [getHash(vipUser2)]); + const insertBannedUserQuery = 'INSERT INTO "shadowBannedUsers" ("userID") VALUES (?)'; + await db.prepare("run", insertBannedUserQuery, [getHash(bannedUser)]); + const insertTitleQuery = 'INSERT INTO "titles" ("videoID", "title", "original", "userID", "service", "hashedVideoID", "timeSubmitted", "UUID") VALUES (?, ?, ?, ?, ?, ?, ?, ?)'; await db.prepare("run", insertTitleQuery, ["postBrandLocked1", "Some title", 0, getHash(userID1), Service.YouTube, getHash("postBrandLocked1"), Date.now(), "postBrandLocked1"]); await db.prepare("run", insertTitleQuery, ["postBrandLocked2", "Some title", 1, getHash(userID2), Service.YouTube, getHash("postBrandLocked2"), Date.now(), "postBrandLocked2"]); @@ -61,6 +65,18 @@ describe("postBranding", () => { await db.prepare("run", insertSegment, ["postBrandVerified3", 1, 11, 1, 0, "postBrandVerified3", getHash(userID8), 0, 50, "sponsor", "skip", "YouTube", 100, 0, 0, ""]); await db.prepare("run", insertSegment, ["postBrandVerified3", 11, 21, 1, 0, "postBrandVerified32", getHash(userID8), 0, 50, "sponsor", "skip", "YouTube", 100, 0, 0, ""]); await db.prepare("run", insertSegment, ["postBrandVerified3", 21, 31, 1, 0, "postBrandVerified33", getHash(userID8), 0, 50, "sponsor", "skip", "YouTube", 100, 0, 0, ""]); + + // Testing details for banned user handling + await db.prepare("run", insertTitleQuery, ["postBrandBannedCustomVote", "Some title", 0, getHash(userID1), Service.YouTube, getHash("postBrandBannedCustomVote"), Date.now(), "postBrandBannedCustomVote"]); + await db.prepare("run", insertTitleQuery, ["postBrandBannedOriginalVote", "Some title", 1, getHash(userID1), Service.YouTube, getHash("postBrandBannedOriginalVote"), Date.now(), "postBrandBannedOriginalVote"]); + await db.prepare("run", insertTitleVotesQuery, ["postBrandBannedCustomVote", 0, 0, 0, 0]); + await db.prepare("run", insertTitleVotesQuery, ["postBrandBannedOriginalVote", 0, 0, 0, 0]); + await db.prepare("run", insertThumbnailQuery, ["postBrandBannedCustomVote", 0, getHash(userID1), Service.YouTube, getHash("postBrandBannedCustomVote"), Date.now(), "postBrandBannedCustomVote"]); + await db.prepare("run", insertThumbnailQuery, ["postBrandBannedOriginalVote", 1, getHash(userID1), Service.YouTube, getHash("postBrandBannedOriginalVote"), Date.now(), "postBrandBannedOriginalVote"]); + await db.prepare("run", insertThumbnailVotesQuery, ["postBrandBannedCustomVote", 0, 0, 0]); + await db.prepare("run", insertThumbnailVotesQuery, ["postBrandBannedOriginalVote", 0, 0, 0]); + const insertThumbnailTimestampQuery = 'INSERT INTO "thumbnailTimestamps" ("UUID", "timestamp") VALUES (?, ?)'; + await db.prepare("run", insertThumbnailTimestampQuery, ["postBrandBannedCustomVote", 12.34]); }); it("Submit only title", async () => { @@ -579,4 +595,193 @@ describe("postBranding", () => { assert.strictEqual(dbVotes.verification, 0); }); -}); \ No newline at end of file + + it("Banned users should not be able to vote (custom title)", async () => { + const videoID = "postBrandBannedCustomVote"; + const title = { + title: "Some title", + original: false + }; + + const res = await postBranding({ + title, + userID: bannedUser, + service: Service.YouTube, + videoID + }); + + assert.strictEqual(res.status, 200); + const dbTitle = await queryTitleByVideo(videoID); + const dbVotes = await queryTitleVotesByUUID(dbTitle.UUID); + + assert.strictEqual(dbTitle.title, title.title); + assert.strictEqual(dbTitle.original, title.original ? 1 : 0); + + assert.strictEqual(dbVotes.votes, 0); + assert.strictEqual(dbVotes.locked, 0); + assert.strictEqual(dbVotes.shadowHidden, 0); + }); + + it("Banned users should not be able to vote (original title)", async () => { + const videoID = "postBrandBannedOriginalVote"; + const title = { + title: "Some title", + original: true + }; + + const res = await postBranding({ + title, + userID: bannedUser, + service: Service.YouTube, + videoID + }); + + assert.strictEqual(res.status, 200); + const dbTitle = await queryTitleByVideo(videoID); + const dbVotes = await queryTitleVotesByUUID(dbTitle.UUID); + + assert.strictEqual(dbTitle.title, title.title); + assert.strictEqual(dbTitle.original, title.original ? 1 : 0); + + assert.strictEqual(dbVotes.votes, 0); + assert.strictEqual(dbVotes.locked, 0); + assert.strictEqual(dbVotes.shadowHidden, 0); + }); + + it("Banned users should not be able to vote (custom thumbnail)", async () => { + const videoID = "postBrandBannedCustomVote"; + const thumbnail = { + original: false, + timestamp: 12.34 + }; + + const res = await postBranding({ + thumbnail, + userID: bannedUser, + service: Service.YouTube, + videoID + }); + + assert.strictEqual(res.status, 200); + const dbThumbnail = await queryThumbnailByVideo(videoID); + const dbVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID); + + assert.strictEqual(dbThumbnail.original, thumbnail.original ? 1 : 0); + + assert.strictEqual(dbVotes.votes, 0); + assert.strictEqual(dbVotes.locked, 0); + assert.strictEqual(dbVotes.shadowHidden, 0); + }); + + it("Banned users should not be able to vote (original thumbnail)", async () => { + const videoID = "postBrandBannedOriginalVote"; + const thumbnail = { + original: true + }; + + const res = await postBranding({ + thumbnail, + userID: bannedUser, + service: Service.YouTube, + videoID + }); + + assert.strictEqual(res.status, 200); + const dbThumbnail = await queryThumbnailByVideo(videoID); + const dbVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID); + + assert.strictEqual(dbThumbnail.original, thumbnail.original ? 1 : 0); + + assert.strictEqual(dbVotes.votes, 0); + assert.strictEqual(dbVotes.locked, 0); + assert.strictEqual(dbVotes.shadowHidden, 0); + }); + + it("Banned users' custom submissions should be hidden (title)", async () => { + const videoID = "postBrandBannedCustom"; + const title = { + title: "Some title", + original: false + }; + + const res = await postBranding({ + title, + userID: bannedUser, + service: Service.YouTube, + videoID + }); + + assert.strictEqual(res.status, 200); + const dbTitle = await queryTitleByVideo(videoID); + const dbVotes = await queryTitleVotesByUUID(dbTitle.UUID); + + assert.strictEqual(dbTitle.title, title.title); + assert.strictEqual(dbTitle.original, title.original ? 1 : 0); + + assert.strictEqual(dbVotes.votes, 0); + assert.strictEqual(dbVotes.locked, 0); + assert.strictEqual(dbVotes.shadowHidden, 1); + }); + + it("Banned users' custom submissions should be hidden (thumbnail)", async () => { + const videoID = "postBrandBannedCustom"; + const thumbnail = { + original: false, + timestamp: 12.34 + }; + + const res = await postBranding({ + thumbnail, + userID: bannedUser, + service: Service.YouTube, + videoID + }); + + assert.strictEqual(res.status, 200); + const dbThumbnail = await queryThumbnailByVideo(videoID); + const dbVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID); + + assert.strictEqual(dbThumbnail.original, thumbnail.original ? 1 : 0); + + assert.strictEqual(dbVotes.votes, 0); + assert.strictEqual(dbVotes.locked, 0); + assert.strictEqual(dbVotes.shadowHidden, 1); + }); + + it("Banned users' original submissions should be ignored (title)", async () => { + const videoID = "postBrandBannedOriginal"; + const title = { + title: "Some title", + original: true + }; + + const res = await postBranding({ + title, + userID: bannedUser, + service: Service.YouTube, + videoID + }); + + assert.strictEqual(res.status, 200); + const dbTitle = await queryTitleByVideo(videoID); + assert.strictEqual(dbTitle, undefined); + }); + + it("Banned users' original submissions should be ignored (thumbnail)", async () => { + const videoID = "postBrandBannedOriginal"; + const thumbnail = { + original: true + }; + + const res = await postBranding({ + thumbnail, + userID: bannedUser, + service: Service.YouTube, + videoID + }); + + assert.strictEqual(res.status, 200); + const dbThumbnail = await queryThumbnailByVideo(videoID); + assert.strictEqual(dbThumbnail, undefined); + }); +});