Skip to content

Commit

Permalink
Merge main into branch
Browse files Browse the repository at this point in the history
  • Loading branch information
coltonhurst committed Apr 4, 2024
2 parents b428d1b + 775c8a1 commit cf11a0b
Show file tree
Hide file tree
Showing 164 changed files with 12,231 additions and 9,102 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/scan.yml
Expand Up @@ -40,7 +40,10 @@ jobs:
base_uri: https://ast.checkmarx.net/
cx_client_id: ${{ secrets.CHECKMARX_CLIENT_ID }}
cx_client_secret: ${{ secrets.CHECKMARX_SECRET }}
additional_params: --report-format sarif --output-path . ${{ env.INCREMENTAL }}
additional_params: |
--report-format sarif \
--filter "state=TO_VERIFY;PROPOSED_NOT_EXPLOITABLE;CONFIRMED;URGENT" \
--output-path . ${{ env.INCREMENTAL }}
- name: Upload Checkmarx results to GitHub
uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
Expand Down
6 changes: 2 additions & 4 deletions apps/browser/src/autofill/background/overlay.background.ts
Expand Up @@ -604,9 +604,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
* @param sender - The sender of the port message
*/
private getNewVaultItemDetails({ sender }: chrome.runtime.Port) {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
BrowserApi.tabSendMessage(sender.tab, { command: "addNewVaultItemFromOverlay" });
void BrowserApi.tabSendMessage(sender.tab, { command: "addNewVaultItemFromOverlay" });
}

/**
Expand Down Expand Up @@ -643,8 +641,8 @@ class OverlayBackground implements OverlayBackgroundInterface {
collectionIds: cipherView.collectionIds,
});

await BrowserApi.sendMessage("inlineAutofillMenuRefreshAddEditCipher");
await this.openAddEditVaultItemPopout(sender.tab, { cipherId: cipherView.id });
await BrowserApi.sendMessage("inlineAutofillMenuRefreshAddEditCipher");
}

/**
Expand Down
Expand Up @@ -16,10 +16,6 @@ import {
logServiceFactory,
LogServiceInitOptions,
} from "../../../platform/background/service-factories/log-service.factory";
import {
stateServiceFactory,
StateServiceInitOptions,
} from "../../../platform/background/service-factories/state-service.factory";
import {
cipherServiceFactory,
CipherServiceInitOptions,
Expand All @@ -44,7 +40,6 @@ type AutoFillServiceOptions = FactoryOptions;

export type AutoFillServiceInitOptions = AutoFillServiceOptions &
CipherServiceInitOptions &
StateServiceInitOptions &
AutofillSettingsServiceInitOptions &
TotpServiceInitOptions &
EventCollectionServiceInitOptions &
Expand All @@ -63,7 +58,6 @@ export function autofillServiceFactory(
async () =>
new AutofillService(
await cipherServiceFactory(cache, opts),
await stateServiceFactory(cache, opts),
await autofillSettingsServiceFactory(cache, opts),
await totpServiceFactory(cache, opts),
await eventCollectionServiceFactory(cache, opts),
Expand Down
4 changes: 1 addition & 3 deletions apps/browser/src/autofill/content/autofill-init.ts
Expand Up @@ -99,9 +99,7 @@ class AutofillInit implements AutofillInitInterface {
return pageDetails;
}

// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
chrome.runtime.sendMessage({
void chrome.runtime.sendMessage({
command: "collectPageDetailsResponse",
tab: message.tab,
details: pageDetails,
Expand Down
Expand Up @@ -2,9 +2,7 @@

exports[`AutofillOverlayList initAutofillOverlayList the list of ciphers for an authenticated user creates the view for a list of ciphers 1`] = `
<div
aria-modal="true"
class="overlay-list-container theme_light"
role="dialog"
>
<ul
class="overlay-actions-list"
Expand Down Expand Up @@ -436,9 +434,7 @@ exports[`AutofillOverlayList initAutofillOverlayList the list of ciphers for an

exports[`AutofillOverlayList initAutofillOverlayList the locked overlay for an unauthenticated user creates the views for the locked overlay 1`] = `
<div
aria-modal="true"
class="overlay-list-container theme_light"
role="dialog"
>
<div
class="locked-overlay overlay-list-message"
Expand Down Expand Up @@ -490,9 +486,7 @@ exports[`AutofillOverlayList initAutofillOverlayList the locked overlay for an u

exports[`AutofillOverlayList initAutofillOverlayList the overlay with an empty list of ciphers creates the views for the no results overlay 1`] = `
<div
aria-modal="true"
class="overlay-list-container theme_light"
role="dialog"
>
<div
class="no-items overlay-list-message"
Expand Down
Expand Up @@ -312,6 +312,24 @@ describe("AutofillOverlayList", () => {
});

describe("directing user focus into the overlay list", () => {
it("sets ARIA attributes that define the list as a `dialog` to screen reader users", () => {
postWindowMessage(
createInitAutofillOverlayListMessageMock({
authStatus: AuthenticationStatus.Locked,
cipherList: [],
}),
);
const overlayContainerSetAttributeSpy = jest.spyOn(
autofillOverlayList["overlayListContainer"],
"setAttribute",
);

postWindowMessage({ command: "focusOverlayList" });

expect(overlayContainerSetAttributeSpy).toHaveBeenCalledWith("role", "dialog");
expect(overlayContainerSetAttributeSpy).toHaveBeenCalledWith("aria-modal", "true");
});

it("focuses the unlock button element if the user is not authenticated", () => {
postWindowMessage(
createInitAutofillOverlayListMessageMock({
Expand Down
Expand Up @@ -59,8 +59,6 @@ class AutofillOverlayList extends AutofillOverlayPageElement {

this.overlayListContainer = globalThis.document.createElement("div");
this.overlayListContainer.classList.add("overlay-list-container", themeClass);
this.overlayListContainer.setAttribute("role", "dialog");
this.overlayListContainer.setAttribute("aria-modal", "true");
this.resizeObserver.observe(this.overlayListContainer);

this.shadowDom.append(linkElement, this.overlayListContainer);
Expand Down Expand Up @@ -487,6 +485,9 @@ class AutofillOverlayList extends AutofillOverlayPageElement {
* the first cipher button.
*/
private focusOverlayList() {
this.overlayListContainer.setAttribute("role", "dialog");
this.overlayListContainer.setAttribute("aria-modal", "true");

const unlockButtonElement = this.overlayListContainer.querySelector(
"#unlock-button",
) as HTMLElement;
Expand Down
3 changes: 0 additions & 3 deletions apps/browser/src/autofill/services/autofill.service.spec.ts
Expand Up @@ -32,7 +32,6 @@ import { CipherService } from "@bitwarden/common/vault/services/cipher.service";
import { TotpService } from "@bitwarden/common/vault/services/totp.service";

import { BrowserApi } from "../../platform/browser/browser-api";
import { BrowserStateService } from "../../platform/services/browser-state.service";
import { AutofillPort } from "../enums/autofill-port.enums";
import AutofillField from "../models/autofill-field";
import AutofillPageDetails from "../models/autofill-page-details";
Expand Down Expand Up @@ -63,7 +62,6 @@ const mockEquivalentDomains = [
describe("AutofillService", () => {
let autofillService: AutofillService;
const cipherService = mock<CipherService>();
const stateService = mock<BrowserStateService>();
const autofillSettingsService = mock<AutofillSettingsService>();
const mockUserId = Utils.newGuid() as UserId;
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
Expand All @@ -78,7 +76,6 @@ describe("AutofillService", () => {
beforeEach(() => {
autofillService = new AutofillService(
cipherService,
stateService,
autofillSettingsService,
totpService,
eventCollectionService,
Expand Down
2 changes: 0 additions & 2 deletions apps/browser/src/autofill/services/autofill.service.ts
Expand Up @@ -20,7 +20,6 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FieldView } from "@bitwarden/common/vault/models/view/field.view";

import { BrowserApi } from "../../platform/browser/browser-api";
import { BrowserStateService } from "../../platform/services/abstractions/browser-state.service";
import { openVaultItemPasswordRepromptPopout } from "../../vault/popup/utils/vault-popout-window";
import { AutofillPort } from "../enums/autofill-port.enums";
import AutofillField from "../models/autofill-field";
Expand Down Expand Up @@ -49,7 +48,6 @@ export default class AutofillService implements AutofillServiceInterface {

constructor(
private cipherService: CipherService,
private stateService: BrowserStateService,
private autofillSettingsService: AutofillSettingsServiceAbstraction,
private totpService: TotpService,
private eventCollectionService: EventCollectionService,
Expand Down
Expand Up @@ -807,6 +807,36 @@ describe("CollectAutofillContentService", () => {
});

describe("buildAutofillFieldItem", () => {
it("returns a `null` value if the field is a child of a `button[type='submit']`", async () => {
const usernameField = {
labelText: "Username",
id: "username-id",
type: "text",
};
document.body.innerHTML = `
<form>
<div>
<div>
<label for="${usernameField.id}">${usernameField.labelText}</label>
<button type="submit">
<input id="${usernameField.id}" type="${usernameField.type}" />
</button>
</div>
</div>
</form>
`;
const usernameInput = document.getElementById(
usernameField.id,
) as ElementWithOpId<FillableFormFieldElement>;

const autofillFieldItem = await collectAutofillContentService["buildAutofillFieldItem"](
usernameInput,
0,
);

expect(autofillFieldItem).toBeNull();
});

it("returns an existing autofill field item if it exists", async () => {
const index = 0;
const usernameField = {
Expand Down Expand Up @@ -847,27 +877,6 @@ describe("CollectAutofillContentService", () => {
/>
</form>
`;
document.body.innerHTML = `
<form>
<label for="${usernameField.id}">${usernameField.labelText}</label>
<input
id="${usernameField.id}"
class="${usernameField.classes}"
name="${usernameField.name}"
type="${usernameField.type}"
maxlength="${usernameField.maxLength}"
tabindex="${usernameField.tabIndex}"
title="${usernameField.title}"
autocomplete="${usernameField.autocomplete}"
data-label="${usernameField.dataLabel}"
aria-label="${usernameField.ariaLabel}"
placeholder="${usernameField.placeholder}"
rel="${usernameField.rel}"
value="${usernameField.value}"
data-stripe="${usernameField.dataStripe}"
/>
</form>
`;
const existingFieldData: AutofillField = {
elementNumber: index,
htmlClass: usernameField.classes,
Expand Down
Expand Up @@ -92,9 +92,9 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
const { formElements, formFieldElements } = this.queryAutofillFormAndFieldElements();
const autofillFormsData: Record<string, AutofillForm> =
this.buildAutofillFormsData(formElements);
const autofillFieldsData: AutofillField[] = await this.buildAutofillFieldsData(
formFieldElements as FormFieldElement[],
);
const autofillFieldsData: AutofillField[] = (
await this.buildAutofillFieldsData(formFieldElements as FormFieldElement[])
).filter((field) => !!field);
this.sortAutofillFieldElementsMap();

if (!autofillFieldsData.length) {
Expand Down Expand Up @@ -333,15 +333,18 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
* Builds an AutofillField object from the given form element. Will only return
* shared field values if the element is a span element. Will not return any label
* values if the element is a hidden input element.
* @param {ElementWithOpId<FormFieldElement>} element
* @param {number} index
* @returns {Promise<AutofillField>}
* @private
*
* @param element - The form field element to build the AutofillField object from
* @param index - The index of the form field element
*/
private buildAutofillFieldItem = async (
element: ElementWithOpId<FormFieldElement>,
index: number,
): Promise<AutofillField> => {
): Promise<AutofillField | null> => {
if (element.closest("button[type='submit']")) {
return null;
}

element.opid = `__${index}`;

const existingAutofillField = this.autofillFieldElements.get(element);
Expand Down
Expand Up @@ -98,7 +98,7 @@ describe("InsertAutofillContentService", () => {
});

afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
windowLocationSpy.mockRestore();
confirmSpy.mockRestore();
document.body.innerHTML = "";
Expand Down
8 changes: 6 additions & 2 deletions apps/browser/src/background/main.background.ts
Expand Up @@ -146,6 +146,7 @@ import {
} from "@bitwarden/common/tools/password-strength";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service";
import { SendApiService as SendApiServiceAbstraction } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { SendStateProvider } from "@bitwarden/common/tools/send/services/send-state.provider";
import { SendService } from "@bitwarden/common/tools/send/services/send.service";
import { InternalSendService as InternalSendServiceAbstraction } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { UserId } from "@bitwarden/common/types/guid";
Expand Down Expand Up @@ -276,6 +277,7 @@ export default class MainBackground {
eventUploadService: EventUploadServiceAbstraction;
policyService: InternalPolicyServiceAbstraction;
sendService: InternalSendServiceAbstraction;
sendStateProvider: SendStateProvider;
fileUploadService: FileUploadServiceAbstraction;
cipherFileUploadService: CipherFileUploadServiceAbstraction;
organizationService: InternalOrganizationServiceAbstraction;
Expand Down Expand Up @@ -707,11 +709,14 @@ export default class MainBackground {
logoutCallback,
);
this.containerService = new ContainerService(this.cryptoService, this.encryptService);

this.sendStateProvider = new SendStateProvider(this.stateProvider);
this.sendService = new SendService(
this.cryptoService,
this.i18nService,
this.keyGenerationService,
this.stateService,
this.sendStateProvider,
this.encryptService,
);
this.sendApiService = new SendApiService(
this.apiService,
Expand Down Expand Up @@ -762,7 +767,6 @@ export default class MainBackground {

this.autofillService = new AutofillService(
this.cipherService,
this.stateService,
this.autofillSettingsService,
this.totpService,
this.eventCollectionService,
Expand Down
Expand Up @@ -5,6 +5,10 @@ import {
CryptoServiceInitOptions,
cryptoServiceFactory,
} from "../../platform/background/service-factories/crypto-service.factory";
import {
EncryptServiceInitOptions,
encryptServiceFactory,
} from "../../platform/background/service-factories/encrypt-service.factory";
import {
FactoryOptions,
CachedServices,
Expand All @@ -18,18 +22,20 @@ import {
KeyGenerationServiceInitOptions,
keyGenerationServiceFactory,
} from "../../platform/background/service-factories/key-generation-service.factory";

import {
stateServiceFactory,
StateServiceInitOptions,
} from "../../platform/background/service-factories/state-service.factory";
SendStateProviderInitOptions,
sendStateProviderFactory,
} from "./send-state-provider.factory";

type SendServiceFactoryOptions = FactoryOptions;

export type SendServiceInitOptions = SendServiceFactoryOptions &
CryptoServiceInitOptions &
I18nServiceInitOptions &
KeyGenerationServiceInitOptions &
StateServiceInitOptions;
SendStateProviderInitOptions &
EncryptServiceInitOptions;

export function sendServiceFactory(
cache: { sendService?: InternalSendService } & CachedServices,
Expand All @@ -44,7 +50,8 @@ export function sendServiceFactory(
await cryptoServiceFactory(cache, opts),
await i18nServiceFactory(cache, opts),
await keyGenerationServiceFactory(cache, opts),
await stateServiceFactory(cache, opts),
await sendStateProviderFactory(cache, opts),
await encryptServiceFactory(cache, opts),
),
);
}

0 comments on commit cf11a0b

Please sign in to comment.