mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-09-12 13:48:58 +02:00
64 lines
3.5 KiB
JavaScript
64 lines
3.5 KiB
JavaScript
const { expect } = require("chai");
|
|
require("ts-node/register/transpile-only");
|
|
const { Types } = require("mongoose");
|
|
const config = require("../src/config").default;
|
|
const Model = require("../src/core/model/credentials/credentials.model").default;
|
|
const UserModel = require("../src/core/model/users/users.model").default;
|
|
const { getCredentialToken, setCredential, replaceCredential, credentialCipher } = require("../src/core/credentials");
|
|
|
|
describe("credential storage boundary", () => {
|
|
const owner = new Types.ObjectId().toString();
|
|
let original, settings, stored;
|
|
beforeEach(() => {
|
|
original = { findOne: Model.findOne, updateOne: Model.updateOne, findById: UserModel.findById };
|
|
settings = [config.CREDENTIAL_KEYS, config.CREDENTIAL_ACTIVE_KEY_ID, config.CREDENTIAL_LEGACY_READS];
|
|
config.CREDENTIAL_KEYS = JSON.stringify({ test: Buffer.alloc(32, 4).toString("base64") });
|
|
config.CREDENTIAL_ACTIVE_KEY_ID = "test";
|
|
config.CREDENTIAL_LEGACY_READS = false;
|
|
stored = null;
|
|
Model.findOne = () => ({ select: () => ({ lean: async () => stored }) });
|
|
Model.updateOne = async (filter, update) => {
|
|
stored = { _id: new Types.ObjectId(), ownerId: owner, provider: "github", ...update.$set };
|
|
return { modifiedCount: 1 };
|
|
};
|
|
});
|
|
afterEach(() => {
|
|
Object.assign(Model, { findOne: original.findOne, updateOne: original.updateOne });
|
|
UserModel.findById = original.findById;
|
|
[config.CREDENTIAL_KEYS, config.CREDENTIAL_ACTIVE_KEY_ID, config.CREDENTIAL_LEGACY_READS] = settings;
|
|
});
|
|
it("writes only ciphertext and resolves the token by owner/provider", async () => {
|
|
await setCredential(owner, "github-secret");
|
|
expect(JSON.stringify(stored)).not.to.include("github-secret");
|
|
expect(await getCredentialToken(owner)).to.equal("github-secret");
|
|
});
|
|
it("refreshes only the credential whose token still matches", async () => {
|
|
await setCredential(owner, "new-login-token");
|
|
expect(await replaceCredential(owner, "stale-token", "refresh-token")).to.equal(false);
|
|
expect(await getCredentialToken(owner)).to.equal("new-login-token");
|
|
expect(await replaceCredential(owner, "new-login-token", "refresh-token")).to.equal(true);
|
|
expect(await getCredentialToken(owner)).to.equal("refresh-token");
|
|
});
|
|
it("fails closed on corrupt ciphertext even with legacy reads enabled", async () => {
|
|
config.CREDENTIAL_LEGACY_READS = true;
|
|
await setCredential(owner, "github-secret");
|
|
stored.encryptedToken.tag = Buffer.alloc(16).toString("base64");
|
|
UserModel.findById = () => { throw new Error("must not fall back"); };
|
|
try { await getCredentialToken(owner); throw new Error("expected failure"); }
|
|
catch (error) { expect(error.message).to.equal("Credential decryption failed"); }
|
|
});
|
|
it("reads legacy users only when explicitly enabled", async () => {
|
|
let reads = 0;
|
|
UserModel.findById = () => ({ select: async () => { reads++; return { accessTokens: { github: "legacy" } }; } });
|
|
expect(await getCredentialToken(owner)).to.equal("");
|
|
expect(reads).to.equal(0);
|
|
config.CREDENTIAL_LEGACY_READS = true;
|
|
expect(await getCredentialToken(owner)).to.equal("legacy");
|
|
});
|
|
it("uses a unique owner/provider index and hides envelopes by default", () => {
|
|
expect(Model.schema.indexes()).to.deep.include([{ ownerId: 1, provider: 1 }, { unique: true, background: true }]);
|
|
expect(Model.schema.path("encryptedToken").options.select).to.equal(false);
|
|
expect(credentialCipher()).to.have.property("encrypt");
|
|
});
|
|
});
|