1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 16:23:44 +00:00

add BlockBrowserInjectionsByDomain feature flag and put feature behind it

This commit is contained in:
Jonathan Prusik
2024-12-13 11:06:17 -05:00
parent 05b0dc0b2f
commit 1c6ec55089
12 changed files with 49 additions and 14 deletions

View File

@@ -14,6 +14,7 @@ import {
} from "@bitwarden/common/autofill/services/domain-settings.service"; } from "@bitwarden/common/autofill/services/domain-settings.service";
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types"; import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service"; import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { import {
EnvironmentService, EnvironmentService,
Region, Region,
@@ -93,6 +94,7 @@ describe("OverlayBackground", () => {
let logService: MockProxy<LogService>; let logService: MockProxy<LogService>;
let cipherService: MockProxy<CipherService>; let cipherService: MockProxy<CipherService>;
let autofillService: MockProxy<AutofillService>; let autofillService: MockProxy<AutofillService>;
let configService: MockProxy<ConfigService>;
let activeAccountStatusMock$: BehaviorSubject<AuthenticationStatus>; let activeAccountStatusMock$: BehaviorSubject<AuthenticationStatus>;
let authService: MockProxy<AuthService>; let authService: MockProxy<AuthService>;
let environmentMock$: BehaviorSubject<CloudEnvironment>; let environmentMock$: BehaviorSubject<CloudEnvironment>;
@@ -153,7 +155,7 @@ describe("OverlayBackground", () => {
fakeStateProvider = new FakeStateProvider(accountService); fakeStateProvider = new FakeStateProvider(accountService);
showFaviconsMock$ = new BehaviorSubject(true); showFaviconsMock$ = new BehaviorSubject(true);
neverDomainsMock$ = new BehaviorSubject({}); neverDomainsMock$ = new BehaviorSubject({});
domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider); domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider, configService);
domainSettingsService.showFavicons$ = showFaviconsMock$; domainSettingsService.showFavicons$ = showFaviconsMock$;
domainSettingsService.neverDomains$ = neverDomainsMock$; domainSettingsService.neverDomains$ = neverDomainsMock$;
logService = mock<LogService>(); logService = mock<LogService>();

View File

@@ -12,6 +12,7 @@ import {
DefaultDomainSettingsService, DefaultDomainSettingsService,
DomainSettingsService, DomainSettingsService,
} from "@bitwarden/common/autofill/services/domain-settings.service"; } from "@bitwarden/common/autofill/services/domain-settings.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { import {
EnvironmentService, EnvironmentService,
Region, Region,
@@ -61,6 +62,7 @@ describe("OverlayBackground", () => {
let overlayBackground: LegacyOverlayBackground; let overlayBackground: LegacyOverlayBackground;
const cipherService = mock<CipherService>(); const cipherService = mock<CipherService>();
const autofillService = mock<AutofillService>(); const autofillService = mock<AutofillService>();
let configService: MockProxy<ConfigService>;
let activeAccountStatusMock$: BehaviorSubject<AuthenticationStatus>; let activeAccountStatusMock$: BehaviorSubject<AuthenticationStatus>;
let authService: MockProxy<AuthService>; let authService: MockProxy<AuthService>;
@@ -92,7 +94,7 @@ describe("OverlayBackground", () => {
}; };
beforeEach(() => { beforeEach(() => {
domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider); domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider, configService);
activeAccountStatusMock$ = new BehaviorSubject(AuthenticationStatus.Unlocked); activeAccountStatusMock$ = new BehaviorSubject(AuthenticationStatus.Unlocked);
authService = mock<AuthService>(); authService = mock<AuthService>();
authService.activeAccountStatus$ = activeAccountStatusMock$; authService.activeAccountStatus$ = activeAccountStatusMock$;

View File

@@ -255,7 +255,7 @@
{{ "showIdentitiesCurrentTabDesc" | i18n }} {{ "showIdentitiesCurrentTabDesc" | i18n }}
</div> </div>
</div> </div>
<div class="box list"> <div class="box list" *ngIf="blockBrowserInjectionsByDomainEnabled">
<div class="box-content single-line"> <div class="box-content single-line">
<button <button
type="button" type="button"

View File

@@ -36,6 +36,7 @@ export class AutofillV1Component implements OnInit {
protected autoFillOverlayVisibilityOptions: any[]; protected autoFillOverlayVisibilityOptions: any[];
protected disablePasswordManagerLink: string; protected disablePasswordManagerLink: string;
protected inlineMenuPositioningImprovementsEnabled: boolean = false; protected inlineMenuPositioningImprovementsEnabled: boolean = false;
protected blockBrowserInjectionsByDomainEnabled: boolean = false;
protected showInlineMenuIdentities: boolean = true; protected showInlineMenuIdentities: boolean = true;
protected showInlineMenuCards: boolean = true; protected showInlineMenuCards: boolean = true;
inlineMenuIsEnabled: boolean = false; inlineMenuIsEnabled: boolean = false;
@@ -120,6 +121,10 @@ export class AutofillV1Component implements OnInit {
FeatureFlag.InlineMenuPositioningImprovements, FeatureFlag.InlineMenuPositioningImprovements,
); );
this.blockBrowserInjectionsByDomainEnabled = await this.configService.getFeatureFlag(
FeatureFlag.BlockBrowserInjectionsByDomain,
);
this.inlineMenuIsEnabled = this.isInlineMenuEnabled(); this.inlineMenuIsEnabled = this.isInlineMenuEnabled();
this.showInlineMenuIdentities = this.showInlineMenuIdentities =

View File

@@ -282,7 +282,7 @@
</bit-form-field> </bit-form-field>
</bit-card> </bit-card>
</bit-section> </bit-section>
<bit-section> <bit-section *ngIf="blockBrowserInjectionsByDomainEnabled">
<bit-item> <bit-item>
<a bit-item-content routerLink="/blocked-domains">{{ "blockedDomains" | i18n }}</a> <a bit-item-content routerLink="/blocked-domains">{{ "blockedDomains" | i18n }}</a>
<i slot="end" class="bwi bwi-angle-right bwi-lg row-sub-icon" aria-hidden="true"></i> <i slot="end" class="bwi bwi-angle-right bwi-lg row-sub-icon" aria-hidden="true"></i>

View File

@@ -49,7 +49,6 @@ import {
import { BrowserApi } from "../../../platform/browser/browser-api"; import { BrowserApi } from "../../../platform/browser/browser-api";
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
import { PopupFooterComponent } from "../../../platform/popup/layout/popup-footer.component";
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
@@ -67,7 +66,6 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
JslibModule, JslibModule,
LinkModule, LinkModule,
PopOutComponent, PopOutComponent,
PopupFooterComponent,
PopupHeaderComponent, PopupHeaderComponent,
PopupPageComponent, PopupPageComponent,
RouterModule, RouterModule,
@@ -87,6 +85,7 @@ export class AutofillComponent implements OnInit {
protected inlineMenuVisibility: InlineMenuVisibilitySetting = protected inlineMenuVisibility: InlineMenuVisibilitySetting =
AutofillOverlayVisibility.OnFieldFocus; AutofillOverlayVisibility.OnFieldFocus;
protected inlineMenuPositioningImprovementsEnabled: boolean = false; protected inlineMenuPositioningImprovementsEnabled: boolean = false;
protected blockBrowserInjectionsByDomainEnabled: boolean = false;
protected browserClientVendor: BrowserClientVendor = BrowserClientVendors.Unknown; protected browserClientVendor: BrowserClientVendor = BrowserClientVendors.Unknown;
protected disablePasswordManagerURI: DisablePasswordManagerUri = protected disablePasswordManagerURI: DisablePasswordManagerUri =
DisablePasswordManagerUris.Unknown; DisablePasswordManagerUris.Unknown;
@@ -164,6 +163,10 @@ export class AutofillComponent implements OnInit {
FeatureFlag.InlineMenuPositioningImprovements, FeatureFlag.InlineMenuPositioningImprovements,
); );
this.blockBrowserInjectionsByDomainEnabled = await this.configService.getFeatureFlag(
FeatureFlag.BlockBrowserInjectionsByDomain,
);
this.showInlineMenuIdentities = this.showInlineMenuIdentities =
this.inlineMenuPositioningImprovementsEnabled && this.inlineMenuPositioningImprovementsEnabled &&
(await firstValueFrom(this.autofillSettingsService.showInlineMenuIdentities$)); (await firstValueFrom(this.autofillSettingsService.showInlineMenuIdentities$));

View File

@@ -136,7 +136,7 @@ describe("AutofillService", () => {
userNotificationsSettings, userNotificationsSettings,
messageListener, messageListener,
); );
domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider); domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider, configService);
domainSettingsService.equivalentDomains$ = of(mockEquivalentDomains); domainSettingsService.equivalentDomains$ = of(mockEquivalentDomains);
jest.spyOn(BrowserApi, "tabSendMessage"); jest.spyOn(BrowserApi, "tabSendMessage");
}); });

View File

@@ -695,7 +695,6 @@ export default class MainBackground {
this.vaultTimeoutSettingsService, this.vaultTimeoutSettingsService,
); );
this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider);
this.fileUploadService = new FileUploadService(this.logService, this.apiService); this.fileUploadService = new FileUploadService(this.logService, this.apiService);
this.cipherFileUploadService = new CipherFileUploadService( this.cipherFileUploadService = new CipherFileUploadService(
this.apiService, this.apiService,
@@ -809,6 +808,11 @@ export default class MainBackground {
this.authService, this.authService,
); );
this.domainSettingsService = new DefaultDomainSettingsService(
this.stateProvider,
this.configService,
);
this.themeStateService = new DefaultThemeStateService( this.themeStateService = new DefaultThemeStateService(
this.globalStateProvider, this.globalStateProvider,
this.configService, this.configService,

View File

@@ -327,7 +327,7 @@ const safeProviders: SafeProvider[] = [
safeProvider({ safeProvider({
provide: DomainSettingsService, provide: DomainSettingsService,
useClass: DefaultDomainSettingsService, useClass: DefaultDomainSettingsService,
deps: [StateProvider], deps: [StateProvider, ConfigService],
}), }),
safeProvider({ safeProvider({
provide: AbstractStorageService, provide: AbstractStorageService,

View File

@@ -1246,7 +1246,7 @@ const safeProviders: SafeProvider[] = [
safeProvider({ safeProvider({
provide: DomainSettingsService, provide: DomainSettingsService,
useClass: DefaultDomainSettingsService, useClass: DefaultDomainSettingsService,
deps: [StateProvider], deps: [StateProvider, ConfigService],
}), }),
safeProvider({ safeProvider({
provide: BiometricStateService, provide: BiometricStateService,

View File

@@ -1,6 +1,9 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { map, Observable } from "rxjs"; import { combineLatest, map, Observable } from "rxjs";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { import {
NeverDomains, NeverDomains,
@@ -81,16 +84,30 @@ export class DefaultDomainSettingsService implements DomainSettingsService {
private defaultUriMatchStrategyState: ActiveUserState<UriMatchStrategySetting>; private defaultUriMatchStrategyState: ActiveUserState<UriMatchStrategySetting>;
readonly defaultUriMatchStrategy$: Observable<UriMatchStrategySetting>; readonly defaultUriMatchStrategy$: Observable<UriMatchStrategySetting>;
constructor(private stateProvider: StateProvider) { constructor(
private stateProvider: StateProvider,
private configService: ConfigService,
) {
this.showFaviconsState = this.stateProvider.getGlobal(SHOW_FAVICONS); this.showFaviconsState = this.stateProvider.getGlobal(SHOW_FAVICONS);
this.showFavicons$ = this.showFaviconsState.state$.pipe(map((x) => x ?? true)); this.showFavicons$ = this.showFaviconsState.state$.pipe(map((x) => x ?? true));
this.neverDomainsState = this.stateProvider.getGlobal(NEVER_DOMAINS); this.neverDomainsState = this.stateProvider.getGlobal(NEVER_DOMAINS);
this.neverDomains$ = this.neverDomainsState.state$.pipe(map((x) => x ?? null)); this.neverDomains$ = this.neverDomainsState.state$.pipe(map((x) => x ?? null));
// Needs to be global to prevent pre-login injections
this.blockedInteractionsUrisState = this.stateProvider.getGlobal(BLOCKED_INTERACTIONS_URIS); this.blockedInteractionsUrisState = this.stateProvider.getGlobal(BLOCKED_INTERACTIONS_URIS);
this.blockedInteractionsUris$ = this.blockedInteractionsUrisState.state$.pipe(
map((x) => x ?? null), this.blockedInteractionsUris$ = combineLatest([
this.blockedInteractionsUrisState.state$,
this.configService.getFeatureFlag$(FeatureFlag.BlockBrowserInjectionsByDomain),
]).pipe(
map(([blockedUris, blockBrowserInjectionsByDomainEnabled]) => {
if (!blockBrowserInjectionsByDomainEnabled) {
return null;
}
return blockedUris ?? null;
}),
); );
this.equivalentDomainsState = this.stateProvider.getActive(EQUIVALENT_DOMAINS); this.equivalentDomainsState = this.stateProvider.getActive(EQUIVALENT_DOMAINS);

View File

@@ -27,6 +27,7 @@ export enum FeatureFlag {
SSHKeyVaultItem = "ssh-key-vault-item", SSHKeyVaultItem = "ssh-key-vault-item",
SSHAgent = "ssh-agent", SSHAgent = "ssh-agent",
NotificationBarAddLoginImprovements = "notification-bar-add-login-improvements", NotificationBarAddLoginImprovements = "notification-bar-add-login-improvements",
BlockBrowserInjectionsByDomain = "block-browser-injections-by-domain",
AC2476_DeprecateStripeSourcesAPI = "AC-2476-deprecate-stripe-sources-api", AC2476_DeprecateStripeSourcesAPI = "AC-2476-deprecate-stripe-sources-api",
CipherKeyEncryption = "cipher-key-encryption", CipherKeyEncryption = "cipher-key-encryption",
VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint", VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint",
@@ -81,6 +82,7 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.SSHKeyVaultItem]: FALSE, [FeatureFlag.SSHKeyVaultItem]: FALSE,
[FeatureFlag.SSHAgent]: FALSE, [FeatureFlag.SSHAgent]: FALSE,
[FeatureFlag.NotificationBarAddLoginImprovements]: FALSE, [FeatureFlag.NotificationBarAddLoginImprovements]: FALSE,
[FeatureFlag.BlockBrowserInjectionsByDomain]: FALSE,
[FeatureFlag.AC2476_DeprecateStripeSourcesAPI]: FALSE, [FeatureFlag.AC2476_DeprecateStripeSourcesAPI]: FALSE,
[FeatureFlag.CipherKeyEncryption]: FALSE, [FeatureFlag.CipherKeyEncryption]: FALSE,
[FeatureFlag.VerifiedSsoDomainEndpoint]: FALSE, [FeatureFlag.VerifiedSsoDomainEndpoint]: FALSE,