mirror of
https://github.com/bitwarden/browser
synced 2026-02-12 06:23:38 +00:00
Test cases update
This commit is contained in:
@@ -22,7 +22,6 @@ export type ApplicationHealthReportDetail = {
|
||||
passwordCount: number;
|
||||
atRiskPasswordCount: number;
|
||||
memberCount: number;
|
||||
atRiskMemberCount: number;
|
||||
|
||||
memberDetails: MemberDetailsFlat[];
|
||||
atRiskMemberDetails: MemberDetailsFlat[];
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
|
||||
|
||||
const createLoginUriView = (uri: string): LoginUriView => {
|
||||
const view = new LoginUriView();
|
||||
view.uri = uri;
|
||||
return view;
|
||||
};
|
||||
|
||||
export const mockCiphers: any[] = [
|
||||
{
|
||||
initializerKey: 1,
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001228ab1",
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: "Cannot Be Edited",
|
||||
name: "Weak Password Cipher",
|
||||
notes: null,
|
||||
isDeleted: false,
|
||||
type: 1,
|
||||
@@ -14,10 +22,11 @@ export const mockCiphers: any[] = [
|
||||
password: "123",
|
||||
hasUris: true,
|
||||
uris: [
|
||||
{ uri: "www.google.com" },
|
||||
{ uri: "accounts.google.com" },
|
||||
{ uri: "https://www.google.com" },
|
||||
{ uri: "https://www.google.com/login" },
|
||||
createLoginUriView("101domain.com"),
|
||||
createLoginUriView("www.google.com"),
|
||||
createLoginUriView("accounts.google.com"),
|
||||
createLoginUriView("https://www.google.com"),
|
||||
createLoginUriView("https://www.google.com/login"),
|
||||
],
|
||||
},
|
||||
edit: false,
|
||||
@@ -31,23 +40,18 @@ export const mockCiphers: any[] = [
|
||||
},
|
||||
{
|
||||
initializerKey: 1,
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001228ab2",
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001228cd3",
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: "Can Be Edited id ending 2",
|
||||
name: "Strong Password Cipher",
|
||||
notes: null,
|
||||
isDeleted: false,
|
||||
type: 1,
|
||||
favorite: false,
|
||||
organizationUseTotp: false,
|
||||
login: {
|
||||
password: "123",
|
||||
password: "Password!123",
|
||||
hasUris: true,
|
||||
uris: [
|
||||
{
|
||||
uri: "http://nothing.com",
|
||||
},
|
||||
],
|
||||
uris: [createLoginUriView("http://example.com")],
|
||||
},
|
||||
edit: true,
|
||||
viewPassword: true,
|
||||
@@ -60,22 +64,18 @@ export const mockCiphers: any[] = [
|
||||
},
|
||||
{
|
||||
initializerKey: 1,
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001228cd3",
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001228ab2",
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: "Can Be Edited id ending 3",
|
||||
name: "Strong password Cipher",
|
||||
notes: null,
|
||||
type: 1,
|
||||
favorite: false,
|
||||
organizationUseTotp: false,
|
||||
login: {
|
||||
password: "123",
|
||||
hasUris: true,
|
||||
uris: [
|
||||
{
|
||||
uri: "http://example.com",
|
||||
},
|
||||
],
|
||||
password: "Password!1234",
|
||||
uris: [createLoginUriView("101domain.com")],
|
||||
},
|
||||
edit: true,
|
||||
viewPassword: true,
|
||||
@@ -91,14 +91,15 @@ export const mockCiphers: any[] = [
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001228xy4",
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: "Can Be Edited id ending 4",
|
||||
name: "Strong password Cipher",
|
||||
notes: null,
|
||||
type: 1,
|
||||
favorite: false,
|
||||
organizationUseTotp: false,
|
||||
login: {
|
||||
hasUris: true,
|
||||
uris: [{ uri: "101domain.com" }],
|
||||
password: "Password!123",
|
||||
uris: [createLoginUriView("example.com")],
|
||||
},
|
||||
edit: true,
|
||||
viewPassword: true,
|
||||
@@ -114,14 +115,39 @@ export const mockCiphers: any[] = [
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001227nm5",
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: "Can Be Edited id ending 5",
|
||||
name: "Exposed password Cipher",
|
||||
notes: null,
|
||||
type: 1,
|
||||
favorite: false,
|
||||
organizationUseTotp: false,
|
||||
login: {
|
||||
hasUris: true,
|
||||
uris: [{ uri: "123formbuilder.com" }],
|
||||
password: "123",
|
||||
uris: [createLoginUriView("123formbuilder.com"), createLoginUriView("www.google.com")],
|
||||
},
|
||||
edit: true,
|
||||
viewPassword: true,
|
||||
collectionIds: [],
|
||||
revisionDate: "2023-08-03T17:40:59.793Z",
|
||||
creationDate: "2023-08-03T17:40:59.793Z",
|
||||
deletedDate: null,
|
||||
reprompt: 0,
|
||||
localData: null,
|
||||
},
|
||||
{
|
||||
initializerKey: 1,
|
||||
id: "cbea34a8-bde4-46ad-9d19-b05001227tt1",
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: "Secure Co Login",
|
||||
notes: null,
|
||||
type: 1,
|
||||
favorite: false,
|
||||
organizationUseTotp: false,
|
||||
login: {
|
||||
hasUris: true,
|
||||
password: "4gRyhhOX2Og2p0",
|
||||
uris: [createLoginUriView("SecureCo.com")],
|
||||
},
|
||||
edit: true,
|
||||
viewPassword: true,
|
||||
|
||||
@@ -69,6 +69,12 @@ export const mockMemberCipherDetails: any = [
|
||||
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
|
||||
],
|
||||
},
|
||||
{
|
||||
userName: "Mister Secure",
|
||||
email: "mister.secure@secureco.com",
|
||||
usesKeyConnector: true,
|
||||
cipherIds: ["cbea34a8-bde4-46ad-9d19-b05001227tt1"],
|
||||
},
|
||||
];
|
||||
|
||||
describe("Member Cipher Details API Service", () => {
|
||||
|
||||
@@ -52,6 +52,88 @@ describe("RiskInsightsReportService", () => {
|
||||
it("should generate the raw data report correctly", async () => {
|
||||
const result = await service.generateRawDataReport("orgId");
|
||||
|
||||
expect(result).toHaveLength(4);
|
||||
expect(result).toHaveLength(6);
|
||||
|
||||
let testCase = result.filter((x) => x.id === "cbea34a8-bde4-46ad-9d19-b05001228ab1")[0];
|
||||
expect(testCase).toBeTruthy();
|
||||
expect(testCase.cipherMembers).toHaveLength(2);
|
||||
expect(testCase.trimmedUris).toHaveLength(3);
|
||||
expect(testCase.weakPasswordDetail).toBeTruthy();
|
||||
expect(testCase.exposedPasswordDetail).toBeTruthy();
|
||||
expect(testCase.reusedPasswordCount).toEqual(2);
|
||||
|
||||
testCase = result.filter((x) => x.id === "cbea34a8-bde4-46ad-9d19-b05001227tt1")[0];
|
||||
expect(testCase).toBeTruthy();
|
||||
expect(testCase.cipherMembers).toHaveLength(1);
|
||||
expect(testCase.trimmedUris).toHaveLength(1);
|
||||
expect(testCase.weakPasswordDetail).toBeFalsy();
|
||||
expect(testCase.exposedPasswordDetail).toBeFalsy();
|
||||
expect(testCase.reusedPasswordCount).toEqual(1);
|
||||
});
|
||||
|
||||
it("should generate the raw data + uri report correctly", async () => {
|
||||
const result = await service.generateRawDataUriReport("orgId");
|
||||
|
||||
expect(result).toHaveLength(9);
|
||||
|
||||
// Two ciphers that have google.com as their uri. There should be 2 results
|
||||
const googleResults = result.filter((x) => x.trimmedUri === "google.com");
|
||||
expect(googleResults).toHaveLength(2);
|
||||
|
||||
// Verify the details for one of the googles matches the password health info
|
||||
// expected
|
||||
const firstGoogle = googleResults.filter(
|
||||
(x) => x.cipherId === "cbea34a8-bde4-46ad-9d19-b05001228ab1" && x.trimmedUri === "google.com",
|
||||
)[0];
|
||||
expect(firstGoogle.weakPasswordDetail).toBeTruthy();
|
||||
expect(firstGoogle.exposedPasswordDetail).toBeTruthy();
|
||||
expect(firstGoogle.reusedPasswordCount).toEqual(2);
|
||||
});
|
||||
|
||||
it("should generate applications health report data correctly", async () => {
|
||||
const result = await service.generateApplicationsReport("orgId");
|
||||
|
||||
expect(result).toHaveLength(6);
|
||||
|
||||
// Two ciphers have google.com associated with them. The first cipher
|
||||
// has 2 members and the second has 4. However, the 2 members in the first
|
||||
// cipher are also associated with the second. The total amount of members
|
||||
// should be 4 not 6
|
||||
const googleTest = result.filter((x) => x.applicationName === "google.com")[0];
|
||||
expect(googleTest.memberCount).toEqual(4);
|
||||
|
||||
// Both ciphers have at risk passwords
|
||||
expect(googleTest.passwordCount).toEqual(2);
|
||||
|
||||
// All members are at risk since both ciphers are at risk
|
||||
expect(googleTest.atRiskMemberDetails).toHaveLength(4);
|
||||
expect(googleTest.atRiskPasswordCount).toEqual(2);
|
||||
|
||||
// There are 2 ciphers associated with 101domain.com
|
||||
const doman101Test = result.filter((x) => x.applicationName === "101domain.com")[0];
|
||||
expect(doman101Test.passwordCount).toEqual(2);
|
||||
|
||||
// The first cipher is at risk. The second cipher is not at risk
|
||||
expect(doman101Test.atRiskPasswordCount).toEqual(1);
|
||||
|
||||
// The first cipher has 2 members. The second cipher the second
|
||||
// cipher has 4. One of the members in the first cipher is associated
|
||||
// with the second. So there should be 5 members total.
|
||||
expect(doman101Test.memberCount).toEqual(5);
|
||||
|
||||
// The first cipher is at risk. The total at risk members is 2 and
|
||||
// at risk password count is 1.
|
||||
expect(doman101Test.atRiskMemberDetails).toHaveLength(2);
|
||||
expect(doman101Test.atRiskPasswordCount).toEqual(1);
|
||||
});
|
||||
|
||||
it("should generate applications summary data correctly", async () => {
|
||||
const reportResult = await service.generateApplicationsReport("orgId");
|
||||
const reportSummary = service.generateApplicationsSummary(reportResult);
|
||||
|
||||
expect(reportSummary.totalMemberCount).toEqual(7);
|
||||
expect(reportSummary.totalAtRiskMemberCount).toEqual(6);
|
||||
expect(reportSummary.totalApplicationCount).toEqual(6);
|
||||
expect(reportSummary.totalAtRiskApplicationCount).toEqual(5);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,8 +22,6 @@ import { MemberCipherDetailsApiService } from "./member-cipher-details-api.servi
|
||||
|
||||
@Injectable()
|
||||
export class RiskInsightsReportService {
|
||||
passwordUseMap = new Map<string, number>();
|
||||
|
||||
constructor(
|
||||
private passwordStrengthService: PasswordStrengthServiceAbstraction,
|
||||
private auditService: AuditService,
|
||||
@@ -109,13 +107,18 @@ export class RiskInsightsReportService {
|
||||
memberDetails: MemberDetailsFlat[],
|
||||
): Promise<CipherHealthReportDetail[]> {
|
||||
const cipherHealthReports: CipherHealthReportDetail[] = [];
|
||||
|
||||
const passwordUseMap = new Map<string, number>();
|
||||
for (const cipher of ciphers) {
|
||||
if (this.validateCipher(cipher)) {
|
||||
const weakPassword = this.findWeakPassword(cipher);
|
||||
// Looping over all ciphers needs to happen first to determine reused passwords over all ciphers.
|
||||
// Store in the set and evaluate later
|
||||
this.findReusedPassword(cipher);
|
||||
passwordUseMap.has(cipher.login.password)
|
||||
? passwordUseMap.set(
|
||||
cipher.login.password,
|
||||
(passwordUseMap.get(cipher.login.password) || 0) + 1,
|
||||
)
|
||||
: passwordUseMap.set(cipher.login.password, 1);
|
||||
const exposedPassword = await this.findExposedPassword(cipher);
|
||||
|
||||
// Get the cipher members
|
||||
@@ -137,7 +140,7 @@ export class RiskInsightsReportService {
|
||||
|
||||
// loop for reused passwords
|
||||
cipherHealthReports.forEach((detail) => {
|
||||
detail.reusedPasswordCount = this.passwordUseMap.get(detail.id) ?? 0;
|
||||
detail.reusedPasswordCount = passwordUseMap.get(detail.login.password) ?? 0;
|
||||
});
|
||||
return cipherHealthReports;
|
||||
}
|
||||
@@ -169,7 +172,7 @@ export class RiskInsightsReportService {
|
||||
const index = appReports.findIndex((item) => item.applicationName === uri.trimmedUri);
|
||||
|
||||
let atRisk: boolean = false;
|
||||
if (uri.exposedPasswordDetail || uri.weakPasswordDetail || uri.reusedPasswordCount > 0) {
|
||||
if (uri.exposedPasswordDetail || uri.weakPasswordDetail || uri.reusedPasswordCount > 1) {
|
||||
atRisk = true;
|
||||
}
|
||||
|
||||
@@ -182,17 +185,6 @@ export class RiskInsightsReportService {
|
||||
return appReports;
|
||||
}
|
||||
|
||||
private findReusedPassword(cipher: CipherView) {
|
||||
if (this.passwordUseMap.has(cipher.login.password)) {
|
||||
this.passwordUseMap.set(
|
||||
cipher.login.password,
|
||||
(this.passwordUseMap.get(cipher.login.password) || 0) + 1,
|
||||
);
|
||||
} else {
|
||||
this.passwordUseMap.set(cipher.login.password, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private async findExposedPassword(cipher: CipherView): Promise<ExposedPasswordDetail> {
|
||||
const exposedCount = await this.auditService.passwordLeaked(cipher.login.password);
|
||||
if (exposedCount > 0) {
|
||||
|
||||
Reference in New Issue
Block a user