refactor(api): 🎨 Don't use node:crypto for random strings

This commit is contained in:
Jesse Wierzbinski 2024-06-12 19:38:26 -10:00
parent d8cb1d475b
commit d301d4da09
No known key found for this signature in database
12 changed files with 37 additions and 35 deletions

View file

@ -59,6 +59,6 @@
"ignore": ["node_modules", "dist", "glitch", "glitch-dev"] "ignore": ["node_modules", "dist", "glitch", "glitch-dev"]
}, },
"javascript": { "javascript": {
"globals": ["Bun", "HTMLRewriter"] "globals": ["Bun", "HTMLRewriter", "BufferEncoding"]
} }
} }

View file

@ -1,6 +1,6 @@
import { randomBytes } from "node:crypto";
import { idValidator } from "@/api"; import { idValidator } from "@/api";
import { getBestContentType, urlToContentFormat } from "@/content_types"; import { getBestContentType, urlToContentFormat } from "@/content_types";
import { randomString } from "@/math";
import { addUserToMeilisearch } from "@/meilisearch"; import { addUserToMeilisearch } from "@/meilisearch";
import { proxyUrl } from "@/response"; import { proxyUrl } from "@/response";
import { EntityValidator } from "@lysand-org/federation"; import { EntityValidator } from "@lysand-org/federation";
@ -190,7 +190,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
} }
async resetPassword() { async resetPassword() {
const resetToken = await randomBytes(32).toString("hex"); const resetToken = randomString(32, "hex");
await this.update({ await this.update({
passwordResetToken: resetToken, passwordResetToken: resetToken,

View file

@ -1,5 +1,5 @@
import { afterAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import { randomBytes } from "node:crypto"; import { randomString } from "@/math";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { db } from "~/drizzle/db"; import { db } from "~/drizzle/db";
import { Applications } from "~/drizzle/schema"; import { Applications } from "~/drizzle/schema";
@ -15,7 +15,7 @@ const application = (
.insert(Applications) .insert(Applications)
.values({ .values({
name: "Test Application", name: "Test Application",
clientId: randomBytes(32).toString("hex"), clientId: randomString(32, "hex"),
secret: "test", secret: "test",
redirectUri: "https://example.com", redirectUri: "https://example.com",
scopes: "read write", scopes: "read write",

View file

@ -1,5 +1,5 @@
import { randomBytes } from "node:crypto";
import { applyConfig, handleZodError } from "@/api"; import { applyConfig, handleZodError } from "@/api";
import { randomString } from "@/math";
import { response } from "@/response"; import { response } from "@/response";
import { zValidator } from "@hono/zod-validator"; import { zValidator } from "@hono/zod-validator";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
@ -81,8 +81,8 @@ export default (app: Hono) =>
}); });
} }
const code = randomBytes(32).toString("hex"); const code = randomString(32, "hex");
const accessToken = randomBytes(64).toString("base64url"); const accessToken = randomString(64, "base64url");
await db.insert(Tokens).values({ await db.insert(Tokens).values({
accessToken, accessToken,

View file

@ -1,5 +1,5 @@
import { afterAll, describe, expect, test } from "bun:test"; import { afterAll, describe, expect, test } from "bun:test";
import { randomBytes } from "node:crypto"; import { randomString } from "@/math";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { db } from "~/drizzle/db"; import { db } from "~/drizzle/db";
import { Applications } from "~/drizzle/schema"; import { Applications } from "~/drizzle/schema";
@ -8,8 +8,8 @@ import { getTestUsers, sendTestRequest } from "~/tests/utils";
import { meta } from "./index"; import { meta } from "./index";
const { users, deleteUsers, passwords } = await getTestUsers(1); const { users, deleteUsers, passwords } = await getTestUsers(1);
const token = randomBytes(32).toString("hex"); const token = randomString(32, "hex");
const newPassword = randomBytes(16).toString("hex"); const newPassword = randomString(16, "hex");
// Create application // Create application
const application = ( const application = (
@ -17,7 +17,7 @@ const application = (
.insert(Applications) .insert(Applications)
.values({ .values({
name: "Test Application", name: "Test Application",
clientId: randomBytes(32).toString("hex"), clientId: randomString(32, "hex"),
secret: "test", secret: "test",
redirectUri: "https://example.com", redirectUri: "https://example.com",
scopes: "read write", scopes: "read write",

View file

@ -1,5 +1,5 @@
import { afterEach, describe, expect, test } from "bun:test"; import { afterEach, describe, expect, test } from "bun:test";
import { randomBytes } from "node:crypto"; import { randomString } from "@/math";
import { config } from "config-manager"; import { config } from "config-manager";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { db } from "~/drizzle/db"; import { db } from "~/drizzle/db";
@ -7,8 +7,8 @@ import { Users } from "~/drizzle/schema";
import { sendTestRequest } from "~/tests/utils"; import { sendTestRequest } from "~/tests/utils";
import { meta } from "./index"; import { meta } from "./index";
const username = randomBytes(10).toString("hex"); const username = randomString(10, "hex");
const username2 = randomBytes(10).toString("hex"); const username2 = randomString(10, "hex");
afterEach(async () => { afterEach(async () => {
await db.delete(Users).where(eq(Users.username, username)); await db.delete(Users).where(eq(Users.username, username));

View file

@ -1,5 +1,5 @@
import { randomBytes } from "node:crypto";
import { applyConfig, handleZodError, jsonOrForm } from "@/api"; import { applyConfig, handleZodError, jsonOrForm } from "@/api";
import { randomString } from "@/math";
import { jsonResponse } from "@/response"; import { jsonResponse } from "@/response";
import { zValidator } from "@hono/zod-validator"; import { zValidator } from "@hono/zod-validator";
import type { Hono } from "hono"; import type { Hono } from "hono";
@ -54,8 +54,8 @@ export default (app: Hono) =>
redirectUri: decodeURIComponent(redirect_uris) || "", redirectUri: decodeURIComponent(redirect_uris) || "",
scopes: scopes || "read", scopes: scopes || "read",
website: website || null, website: website || null,
clientId: randomBytes(32).toString("base64url"), clientId: randomString(32, "base64url"),
secret: randomBytes(64).toString("base64url"), secret: randomString(64, "base64url"),
}) })
.returning() .returning()
)[0]; )[0];

View file

@ -1,6 +1,6 @@
import { randomBytes } from "node:crypto";
import { applyConfig, auth, handleZodError, jsonOrForm } from "@/api"; import { applyConfig, auth, handleZodError, jsonOrForm } from "@/api";
import { oauthRedirectUri } from "@/constants"; import { oauthRedirectUri } from "@/constants";
import { randomString } from "@/math";
import { errorResponse, jsonResponse } from "@/response"; import { errorResponse, jsonResponse } from "@/response";
import { zValidator } from "@hono/zod-validator"; import { zValidator } from "@hono/zod-validator";
import type { Hono } from "hono"; import type { Hono } from "hono";
@ -134,9 +134,7 @@ export default (app: Hono) =>
await db await db
.insert(Applications) .insert(Applications)
.values({ .values({
clientId: clientId: user.id + randomString(32, "base64"),
user.id +
randomBytes(32).toString("base64url"),
name: "Lysand", name: "Lysand",
redirectUri: `${oauthRedirectUri(issuerId)}`, redirectUri: `${oauthRedirectUri(issuerId)}`,
scopes: "openid profile email", scopes: "openid profile email",

View file

@ -1,5 +1,5 @@
import { randomBytes } from "node:crypto";
import { applyConfig, handleZodError, jsonOrForm } from "@/api"; import { applyConfig, handleZodError, jsonOrForm } from "@/api";
import { randomString } from "@/math";
import { response } from "@/response"; import { response } from "@/response";
import { zValidator } from "@hono/zod-validator"; import { zValidator } from "@hono/zod-validator";
import type { Hono } from "hono"; import type { Hono } from "hono";
@ -240,7 +240,7 @@ export default (app: Hono) =>
} }
// Generate tokens // Generate tokens
const code = randomBytes(256).toString("base64url"); const code = randomString(256, "base64url");
// Handle the requested scopes // Handle the requested scopes
let idTokenPayload = {}; let idTokenPayload = {};
@ -287,7 +287,7 @@ export default (app: Hono) =>
.sign(privateKey); .sign(privateKey);
await db.insert(Tokens).values({ await db.insert(Tokens).values({
accessToken: randomBytes(64).toString("base64url"), accessToken: randomString(64, "base64url"),
code: code, code: code,
scope: scope ?? application.scopes, scope: scope ?? application.scopes,
tokenType: TokenType.Bearer, tokenType: TokenType.Bearer,

View file

@ -1,5 +1,5 @@
import { randomBytes } from "node:crypto";
import { applyConfig, handleZodError } from "@/api"; import { applyConfig, handleZodError } from "@/api";
import { randomString } from "@/math";
import { errorResponse, response } from "@/response"; import { errorResponse, response } from "@/response";
import { zValidator } from "@hono/zod-validator"; import { zValidator } from "@hono/zod-validator";
import type { Hono } from "hono"; import type { Hono } from "hono";
@ -174,10 +174,10 @@ export default (app: Hono) =>
return errorResponse("Application not found", 500); return errorResponse("Application not found", 500);
} }
const code = randomBytes(32).toString("hex"); const code = randomString(32, "hex");
await db.insert(Tokens).values({ await db.insert(Tokens).values({
accessToken: randomBytes(64).toString("base64url"), accessToken: randomString(64, "base64url"),
code: code, code: code,
scope: flow.application.scopes, scope: flow.application.scopes,
tokenType: TokenType.Bearer, tokenType: TokenType.Bearer,

View file

@ -1,5 +1,5 @@
import { randomBytes } from "node:crypto";
import { consoleLogger } from "@/loggers"; import { consoleLogger } from "@/loggers";
import { randomString } from "@/math";
import { asc, inArray, like } from "drizzle-orm"; import { asc, inArray, like } from "drizzle-orm";
import type { Status } from "~/database/entities/status"; import type { Status } from "~/database/entities/status";
import { db } from "~/drizzle/db"; import { db } from "~/drizzle/db";
@ -35,11 +35,11 @@ export const getTestUsers = async (count: number) => {
const passwords: string[] = []; const passwords: string[] = [];
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const password = randomBytes(32).toString("hex"); const password = randomString(32, "hex");
const user = await User.fromDataLocal({ const user = await User.fromDataLocal({
username: `test-${randomBytes(32).toString("hex")}`, username: `test-${randomString(32, "hex")}`,
email: `${randomBytes(32).toString("hex")}@test.com`, email: `${randomString(32, "hex")}@test.com`,
password, password,
}); });
@ -55,11 +55,11 @@ export const getTestUsers = async (count: number) => {
.insert(Tokens) .insert(Tokens)
.values( .values(
users.map((u) => ({ users.map((u) => ({
accessToken: randomBytes(32).toString("hex"), accessToken: randomString(32, "hex"),
tokenType: "bearer", tokenType: "bearer",
userId: u.id, userId: u.id,
applicationId: null, applicationId: null,
code: randomBytes(32).toString("hex"), code: randomString(32, "hex"),
scope: "read write follow push", scope: "read write follow push",
})), })),
) )
@ -89,7 +89,7 @@ export const getTestStatuses = async (
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const newStatus = await Note.insert({ const newStatus = await Note.insert({
content: `${i} ${randomBytes(32).toString("hex")}`, content: `${i} ${randomString(32, "hex")}`,
authorId: user.id, authorId: user.id,
sensitive: false, sensitive: false,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),

4
utils/math.ts Normal file
View file

@ -0,0 +1,4 @@
export const randomString = (length: number, encoding?: BufferEncoding) =>
Buffer.from(crypto.getRandomValues(new Uint8Array(length))).toString(
encoding,
);