1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-03-02 03:41:09 +01:00

[EC-598] feat: check origin and rp.id effective domains

This commit is contained in:
Andreas Coroiu 2023-03-30 14:25:10 +02:00
parent b8821ccd3d
commit 142aa016d4
No known key found for this signature in database
GPG Key ID: E70B5FFC81DFEC1A
2 changed files with 49 additions and 6 deletions

View File

@ -18,18 +18,18 @@ describe("FidoAuthenticatorService", () => {
describe("createCredential", () => {
describe("invalid input parameters", () => {
/** Spec: If sameOriginWithAncestors is false, return a "NotAllowedError" DOMException. */
// Spec: If sameOriginWithAncestors is false, return a "NotAllowedError" DOMException.
it("should throw error if sameOriginWithAncestors is false", async () => {
const params = createParams({ sameOriginWithAncestors: false });
const result = async () => await client.createCredential(params);
const rejects = await expect(result).rejects;
rejects.toMatchObject({ name: "NotAllowedError" });
rejects.toBeInstanceOf(DOMException);
const rejects = expect(result).rejects;
await rejects.toMatchObject({ name: "NotAllowedError" });
await rejects.toBeInstanceOf(DOMException);
});
/** Spec: If the length of options.user.id is not between 1 and 64 bytes (inclusive) then return a TypeError. */
// Spec: If the length of options.user.id is not between 1 and 64 bytes (inclusive) then return a TypeError.
it("should throw error if user.id is too small", async () => {
const params = createParams({ user: { id: "", displayName: "name" } });
@ -38,7 +38,7 @@ describe("FidoAuthenticatorService", () => {
await expect(result).rejects.toBeInstanceOf(TypeError);
});
/** Spec: If the length of options.user.id is not between 1 and 64 bytes (inclusive) then return a TypeError. */
// Spec: If the length of options.user.id is not between 1 and 64 bytes (inclusive) then return a TypeError.
it("should throw error if user.id is too large", async () => {
const params = createParams({
user: {
@ -51,6 +51,37 @@ describe("FidoAuthenticatorService", () => {
await expect(result).rejects.toBeInstanceOf(TypeError);
});
// Spec: If callerOrigin is an opaque origin, return a DOMException whose name is "NotAllowedError", and terminate this algorithm.
// Not sure how to check this, or if it matters.
it.todo("should throw error if origin is an opaque origin");
// Spec: Let effectiveDomain be the callerOrigins effective domain. If effective domain is not a valid domain, then return a DOMException whose name is "SecurityError" and terminate this algorithm.
it("should throw error if origin is not a valid domain name", async () => {
const params = createParams({
origin: "invalid-domain-name",
});
const result = async () => await client.createCredential(params);
const rejects = expect(result).rejects;
await rejects.toMatchObject({ name: "SecurityError" });
await rejects.toBeInstanceOf(DOMException);
});
// Spec: If options.rp.id is not a registrable domain suffix of and is not equal to effectiveDomain, return a DOMException whose name is "SecurityError", and terminate this algorithm.
it("should throw error if rp.id does not match origin effective domain", async () => {
const params = createParams({
origin: "passwordless.dev",
rp: { id: "bitwarden.com", name: "Bitwarden" },
});
const result = async () => await client.createCredential(params);
const rejects = expect(result).rejects;
await rejects.toMatchObject({ name: "SecurityError" });
await rejects.toBeInstanceOf(DOMException);
});
});
function createParams(params: Partial<CreateCredentialParams> = {}): CreateCredentialParams {

View File

@ -1,3 +1,5 @@
import { parse } from "tldts";
import { Fido2AuthenticatorService } from "../abstractions/fido2-authenticator.service.abstraction";
import {
AssertCredentialParams,
@ -24,6 +26,16 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
throw new TypeError("Invalid 'user.id' length");
}
const { domain: effectiveDomain } = parse(params.origin, { allowPrivateDomains: true });
if (effectiveDomain == undefined) {
throw new DOMException("'origin' is not a valid domain", "SecurityError");
}
const rpId = params.rp.id ?? effectiveDomain;
if (effectiveDomain !== rpId) {
throw new DOMException("'rp.id' does not match origin effective domain", "SecurityError");
}
throw new Error("Not implemented");
}