1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-12 06:23:38 +00:00

Test cases update

This commit is contained in:
Tom
2024-12-09 11:51:19 -05:00
parent b0a7df2231
commit ffbd0cf4fb
6 changed files with 150 additions and 45 deletions

View File

@@ -22,7 +22,6 @@ export type ApplicationHealthReportDetail = {
passwordCount: number;
atRiskPasswordCount: number;
memberCount: number;
atRiskMemberCount: number;
memberDetails: MemberDetailsFlat[];
atRiskMemberDetails: MemberDetailsFlat[];

View File

@@ -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,

View File

@@ -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", () => {

View File

@@ -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);
});
});

View File

@@ -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) {