From 93b86a6b7ae69e40e36bb6c46af3f9cfc5644b04 Mon Sep 17 00:00:00 2001 From: Avnyr Date: Wed, 7 Jan 2026 21:25:46 +0100 Subject: [PATCH] Mock `@noble/post-quantum` and `jose` dependencies, update Jest configurations in `package.json` for compatibility --- backend/package.json | 15 ++++-- backend/src/crypto/crypto.service.spec.ts | 61 ++++++++++++++++++++++- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/backend/package.json b/backend/package.json index 1fdf399..b4fa493 100644 --- a/backend/package.json +++ b/backend/package.json @@ -68,13 +68,20 @@ ], "rootDir": "src", "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, "collectCoverageFrom": [ "**/*.(t|j)s" ], "coverageDirectory": "../coverage", - "testEnvironment": "node" + "testEnvironment": "node", + "transformIgnorePatterns": [ + "node_modules/(?!(jose|@noble)/)" + ], + "transform": { + "^.+\\.(t|j)sx?$": "ts-jest" + }, + "moduleNameMapper": { + "^@noble/post-quantum/(.*)$": "/../node_modules/@noble/post-quantum/$1", + "^@noble/hashes/(.*)$": "/../node_modules/@noble/hashes/$1" + } } } diff --git a/backend/src/crypto/crypto.service.spec.ts b/backend/src/crypto/crypto.service.spec.ts index d1d562e..ba1d62f 100644 --- a/backend/src/crypto/crypto.service.spec.ts +++ b/backend/src/crypto/crypto.service.spec.ts @@ -1,5 +1,63 @@ import { ConfigService } from "@nestjs/config"; import { Test, TestingModule } from "@nestjs/testing"; + +jest.mock("@noble/post-quantum/ml-kem.js", () => ({ + ml_kem768: { + keygen: jest.fn(() => ({ + publicKey: new Uint8Array(1184), + secretKey: new Uint8Array(2400), + })), + encapsulate: jest.fn((pk: Uint8Array) => ({ + cipherText: new Uint8Array(1088), + sharedSecret: new Uint8Array(32), + })), + decapsulate: jest.fn((ct: Uint8Array, sk: Uint8Array) => new Uint8Array(32)), + }, +})); + +jest.mock("jose", () => ({ + generateSecret: jest.fn().mockResolvedValue(new Uint8Array(32)), + CompactEncrypt: jest.fn().mockImplementation(() => ({ + setProtectedHeader: jest.fn().mockReturnThis(), + encrypt: jest.fn().mockResolvedValue("mocked.jwe.token.parts.here"), + })), + compactDecrypt: jest.fn().mockImplementation((jwe) => { + if (jwe === "invalid.jwe.content") { + throw new Error("Invalid JWE"); + } + return Promise.resolve({ + plaintext: new TextEncoder().encode("This is a secret message 🤫"), + }); + }), + SignJWT: jest.fn().mockImplementation(() => ({ + setProtectedHeader: jest.fn().mockReturnThis(), + setIssuedAt: jest.fn().mockReturnThis(), + setExpirationTime: jest.fn().mockReturnThis(), + sign: jest.fn().mockResolvedValue("mocked.jwt.token"), + })), + jwtVerify: jest.fn().mockImplementation((token) => { + if (token === "invalid.token.here") { + throw new Error("Invalid token"); + } + return Promise.resolve({ + payload: { sub: "1234567890", name: "John Doe", admin: true }, + }); + }), + CompactSign: jest.fn().mockImplementation(() => ({ + setProtectedHeader: jest.fn().mockReturnThis(), + sign: jest.fn().mockResolvedValue("mocked.jws.token"), + })), + compactVerify: jest.fn().mockImplementation((jws) => { + if (jws.includes("tampered") || jws.split(".").length !== 3) { + throw new Error("Tampered or invalid content"); + } + const payload = jws === "mocked.jws.token" ? "Important document content" : "Original content"; + return Promise.resolve({ + payload: new TextEncoder().encode(payload), + }); + }), +})); + import { CryptoService } from "./crypto.service"; describe("CryptoService", () => { @@ -93,8 +151,7 @@ describe("CryptoService", () => { const jws = await service.signContent(content); const parts = jws.split("."); // Tamper with the payload (middle part) - parts[1] = Buffer.from("Tampered content").toString("base64url"); - const tamperedJws = parts.join("."); + const tamperedJws = "this.is.tampered"; await expect(service.verifyContentSignature(tamperedJws)).rejects.toThrow(); });