1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 17:23:37 +00:00
Files
browser/libs/common/src/tools/state/secret-classifier.spec.ts
2024-05-30 15:37:40 -04:00

178 lines
6.1 KiB
TypeScript

import { SecretClassifier } from "./secret-classifier";
describe("SecretClassifier", () => {
describe("forSecret", () => {
it("classifies a property as secret by default", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean }>();
expect(classifier.disclosed).toEqual([]);
expect(classifier.excluded).toEqual([]);
});
});
describe("disclose", () => {
it("adds a property to the disclosed list", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean }>();
const withDisclosedFoo = classifier.disclose("foo");
expect(withDisclosedFoo.disclosed).toEqual(["foo"]);
expect(withDisclosedFoo.excluded).toEqual([]);
});
it("chains calls with excluded", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean; bar: boolean }>();
const withDisclosedFoo = classifier.disclose("foo").exclude("bar");
expect(withDisclosedFoo.disclosed).toEqual(["foo"]);
expect(withDisclosedFoo.excluded).toEqual(["bar"]);
});
it("returns a new classifier", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean }>();
const withDisclosedFoo = classifier.disclose("foo");
expect(withDisclosedFoo).not.toBe(classifier);
});
});
describe("exclude", () => {
it("adds a property to the excluded list", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean }>();
const withExcludedFoo = classifier.exclude("foo");
expect(withExcludedFoo.disclosed).toEqual([]);
expect(withExcludedFoo.excluded).toEqual(["foo"]);
});
it("chains calls with disclose", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean; bar: boolean }>();
const withExcludedFoo = classifier.exclude("foo").disclose("bar");
expect(withExcludedFoo.disclosed).toEqual(["bar"]);
expect(withExcludedFoo.excluded).toEqual(["foo"]);
});
it("returns a new classifier", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean }>();
const withExcludedFoo = classifier.exclude("foo");
expect(withExcludedFoo).not.toBe(classifier);
});
});
describe("classify", () => {
it("partitions disclosed properties into the disclosed member", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean; bar: boolean }>().disclose(
"foo",
);
const classified = classifier.classify({ foo: true, bar: false });
expect(classified.disclosed).toEqual({ foo: true });
});
it("jsonifies its outputs", () => {
const classifier = SecretClassifier.allSecret<{ foo: Date; bar: Date }>().disclose("foo");
const classified = classifier.classify({ foo: new Date(100), bar: new Date(100) });
expect(classified.disclosed).toEqual({ foo: "1970-01-01T00:00:00.100Z" });
expect(classified.secret).toEqual({ bar: "1970-01-01T00:00:00.100Z" });
});
it("deletes disclosed properties from the secret member", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean; bar: boolean }>().disclose(
"foo",
);
const classified = classifier.classify({ foo: true, bar: false });
expect(classified.secret).toEqual({ bar: false });
});
it("deletes excluded properties from the secret member", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean; bar: boolean }>().exclude(
"foo",
);
const classified = classifier.classify({ foo: true, bar: false });
expect(classified.secret).toEqual({ bar: false });
});
it("excludes excluded properties from the disclosed member", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean; bar: boolean }>().exclude(
"foo",
);
const classified = classifier.classify({ foo: true, bar: false });
expect(classified.disclosed).toEqual({});
});
});
describe("declassify", () => {
it("merges disclosed and secret members", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean; bar: boolean }>().disclose(
"foo",
);
const declassified = classifier.declassify({ foo: true }, { bar: false });
expect(declassified).toEqual({ foo: true, bar: false });
});
it("omits unknown disclosed members", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean }>().disclose("foo");
// `any` is required here because Typescript knows `bar` is not a disclosed member,
// but the feautre assumes the disclosed data bypassed the typechecker (e.g. someone
// is trying to clobber secret data.)
const declassified = classifier.declassify({ foo: true, bar: false } as any, {});
expect(declassified).toEqual({ foo: true });
});
it("clobbers disclosed members with secret members", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean; bar: boolean }>().disclose(
"foo",
);
// `any` is required here because `declassify` knows `bar` is supposed to be public,
// but the feature assumes the secret data bypassed the typechecker (e.g. migrated data)
const declassified = classifier.declassify({ foo: true }, { foo: false, bar: false } as any);
expect(declassified).toEqual({ foo: false, bar: false });
});
it("omits excluded secret members", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean; bar: boolean }>().exclude(
"foo",
);
// `any` is required here because `declassify` knows `bar` isn't allowed, but the
// feature assumes the data bypassed the typechecker (e.g. omitted legacy data).
const declassified = classifier.declassify({}, { foo: false, bar: false } as any);
expect(declassified).toEqual({ bar: false });
});
it("returns a new object", () => {
const classifier = SecretClassifier.allSecret<{ foo: boolean }>();
const disclosed = {};
const secret = { foo: false };
const declassified = classifier.declassify(disclosed, secret);
expect(declassified).not.toBe(disclosed);
expect(declassified).not.toBe(secret);
});
});
});