1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 05:43:41 +00:00

[PM-14571] At Risk Passwords - Badge Update (#15983)

* add exclamation badge for at risk passwords on tab

* add berry icon for the badge when pending tasks are present

* remove integration wtih autofill for pending task badge

* add ability to override Never match strategy
- This is helpful for non-autofill purposes but cipher matching is still needed. This will default to the domain.

* add at-risk-cipher badge updater service

* Revert "add exclamation badge for at risk passwords on tab"

This reverts commit a9643c03d5.

* remove nullish-coalescing

* ensure that all user related observables use the same user.id

---------

Co-authored-by: Shane Melton <smelton@bitwarden.com>
This commit is contained in:
Nick Krantz
2025-09-02 15:09:20 -05:00
committed by GitHub
parent a4fca832f3
commit 5967cf0539
12 changed files with 324 additions and 4 deletions

View File

@@ -65,12 +65,16 @@ export abstract class CipherService implements UserKeyRotationDataProvider<Ciphe
userId: UserId,
includeOtherTypes?: CipherType[],
defaultMatch?: UriMatchStrategySetting,
/** When true, will override the match strategy for the cipher if it is Never. */
overrideNeverMatchStrategy?: true,
): Promise<CipherView[]>;
abstract filterCiphersForUrl<C extends CipherViewLike = CipherView>(
ciphers: C[],
url: string,
includeOtherTypes?: CipherType[],
defaultMatch?: UriMatchStrategySetting,
/** When true, will override the match strategy for the cipher if it is Never. */
overrideNeverMatchStrategy?: true,
): Promise<C[]>;
abstract getAllFromApiForOrganization(organizationId: string): Promise<CipherView[]>;
/**

View File

@@ -111,6 +111,33 @@ describe("LoginUriView", () => {
expect(actual).toBe(false);
});
it("overrides Never match strategy with Domain when parameter is set", () => {
const loginUri = new LoginUriView();
loginUri.uri = "https://example.org";
loginUri.match = UriMatchStrategy.Never;
expect(loginUri.matchesUri("https://example.org", new Set(), undefined, true)).toBe(true);
expect(loginUri.matchesUri("https://example.org", new Set(), undefined)).toBe(false);
});
it("overrides Never match strategy when passed in as default strategy", () => {
const loginUriNoMatch = new LoginUriView();
loginUriNoMatch.uri = "https://example.org";
expect(
loginUriNoMatch.matchesUri(
"https://example.org",
new Set(),
UriMatchStrategy.Never,
true,
),
).toBe(true);
expect(
loginUriNoMatch.matchesUri("https://example.org", new Set(), UriMatchStrategy.Never),
).toBe(false);
});
});
describe("using host matching", () => {

View File

@@ -142,6 +142,8 @@ export class LoginUriView implements View {
targetUri: string,
equivalentDomains: Set<string>,
defaultUriMatch: UriMatchStrategySetting = null,
/** When present, will override the match strategy for the cipher if it is `Never` with `Domain` */
overrideNeverMatchStrategy?: true,
): boolean {
if (!this.uri || !targetUri) {
return false;
@@ -150,6 +152,12 @@ export class LoginUriView implements View {
let matchType = this.match ?? defaultUriMatch;
matchType ??= UriMatchStrategy.Domain;
// Override the match strategy with `Domain` when it is `Never` and `overrideNeverMatchStrategy` is true.
// This is useful in scenarios when the cipher should be matched to rely other information other than autofill.
if (overrideNeverMatchStrategy && matchType === UriMatchStrategy.Never) {
matchType = UriMatchStrategy.Domain;
}
const targetDomain = Utils.getDomain(targetUri);
const matchDomains = equivalentDomains.add(targetDomain);

View File

@@ -82,12 +82,16 @@ export class LoginView extends ItemView {
targetUri: string,
equivalentDomains: Set<string>,
defaultUriMatch: UriMatchStrategySetting = null,
/** When present, will override the match strategy for the cipher if it is `Never` with `Domain` */
overrideNeverMatchStrategy?: true,
): boolean {
if (this.uris == null) {
return false;
}
return this.uris.some((uri) => uri.matchesUri(targetUri, equivalentDomains, defaultUriMatch));
return this.uris.some((uri) =>
uri.matchesUri(targetUri, equivalentDomains, defaultUriMatch, overrideNeverMatchStrategy),
);
}
static fromJSON(obj: Partial<DeepJsonify<LoginView>>): LoginView {

View File

@@ -601,6 +601,7 @@ export class CipherService implements CipherServiceAbstraction {
userId: UserId,
includeOtherTypes?: CipherType[],
defaultMatch: UriMatchStrategySetting = null,
overrideNeverMatchStrategy?: true,
): Promise<CipherView[]> {
return await firstValueFrom(
this.cipherViews$(userId).pipe(
@@ -612,6 +613,7 @@ export class CipherService implements CipherServiceAbstraction {
url,
includeOtherTypes,
defaultMatch,
overrideNeverMatchStrategy,
),
),
),
@@ -623,6 +625,7 @@ export class CipherService implements CipherServiceAbstraction {
url: string,
includeOtherTypes?: CipherType[],
defaultMatch: UriMatchStrategySetting = null,
overrideNeverMatchStrategy?: true,
): Promise<C[]> {
if (url == null && includeOtherTypes == null) {
return [];
@@ -647,7 +650,13 @@ export class CipherService implements CipherServiceAbstraction {
}
if (cipherIsLogin) {
return CipherViewLikeUtils.matchesUri(cipher, url, equivalentDomains, defaultMatch);
return CipherViewLikeUtils.matchesUri(
cipher,
url,
equivalentDomains,
defaultMatch,
overrideNeverMatchStrategy,
);
}
return false;

View File

@@ -174,13 +174,19 @@ export class CipherViewLikeUtils {
targetUri: string,
equivalentDomains: Set<string>,
defaultUriMatch: UriMatchStrategySetting = UriMatchStrategy.Domain,
overrideNeverMatchStrategy?: true,
): boolean => {
if (CipherViewLikeUtils.getType(cipher) !== CipherType.Login) {
return false;
}
if (!this.isCipherListView(cipher)) {
return cipher.login.matchesUri(targetUri, equivalentDomains, defaultUriMatch);
return cipher.login.matchesUri(
targetUri,
equivalentDomains,
defaultUriMatch,
overrideNeverMatchStrategy,
);
}
const login = this.getLogin(cipher);
@@ -198,7 +204,7 @@ export class CipherViewLikeUtils {
});
return loginUriViews.some((uriView) =>
uriView.matchesUri(targetUri, equivalentDomains, defaultUriMatch),
uriView.matchesUri(targetUri, equivalentDomains, defaultUriMatch, overrideNeverMatchStrategy),
);
};