mirror of
https://github.com/bitwarden/browser
synced 2025-12-17 16:53:34 +00:00
[EC-598] feat: check origin and rp.id effective domains
This commit is contained in:
@@ -18,18 +18,18 @@ describe("FidoAuthenticatorService", () => {
|
|||||||
|
|
||||||
describe("createCredential", () => {
|
describe("createCredential", () => {
|
||||||
describe("invalid input parameters", () => {
|
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 () => {
|
it("should throw error if sameOriginWithAncestors is false", async () => {
|
||||||
const params = createParams({ sameOriginWithAncestors: false });
|
const params = createParams({ sameOriginWithAncestors: false });
|
||||||
|
|
||||||
const result = async () => await client.createCredential(params);
|
const result = async () => await client.createCredential(params);
|
||||||
|
|
||||||
const rejects = await expect(result).rejects;
|
const rejects = expect(result).rejects;
|
||||||
rejects.toMatchObject({ name: "NotAllowedError" });
|
await rejects.toMatchObject({ name: "NotAllowedError" });
|
||||||
rejects.toBeInstanceOf(DOMException);
|
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 () => {
|
it("should throw error if user.id is too small", async () => {
|
||||||
const params = createParams({ user: { id: "", displayName: "name" } });
|
const params = createParams({ user: { id: "", displayName: "name" } });
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ describe("FidoAuthenticatorService", () => {
|
|||||||
await expect(result).rejects.toBeInstanceOf(TypeError);
|
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 () => {
|
it("should throw error if user.id is too large", async () => {
|
||||||
const params = createParams({
|
const params = createParams({
|
||||||
user: {
|
user: {
|
||||||
@@ -51,6 +51,37 @@ describe("FidoAuthenticatorService", () => {
|
|||||||
|
|
||||||
await expect(result).rejects.toBeInstanceOf(TypeError);
|
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 callerOrigin’s 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 {
|
function createParams(params: Partial<CreateCredentialParams> = {}): CreateCredentialParams {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { parse } from "tldts";
|
||||||
|
|
||||||
import { Fido2AuthenticatorService } from "../abstractions/fido2-authenticator.service.abstraction";
|
import { Fido2AuthenticatorService } from "../abstractions/fido2-authenticator.service.abstraction";
|
||||||
import {
|
import {
|
||||||
AssertCredentialParams,
|
AssertCredentialParams,
|
||||||
@@ -24,6 +26,16 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
|
|||||||
throw new TypeError("Invalid 'user.id' length");
|
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");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user