fix(SEC-WEB-27+28): Robust email validation + role cast validation
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

SEC-WEB-27: Replace weak email.includes('@') check with RFC 5322-aligned
programmatic validation (isValidEmail). Uses character-level domain label
validation to avoid ReDoS vulnerabilities from complex regex patterns.

SEC-WEB-28: Replace unsafe 'as WorkspaceMemberRole' type casts with
runtime validation (toWorkspaceMemberRole) that checks against known enum
values and falls back to MEMBER for invalid inputs. Applied in both
InviteMember.tsx and MemberList.tsx.

Adds 43 tests covering validation logic, InviteMember component, and
MemberList component behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-06 15:40:05 -06:00
parent 65b078c85e
commit 6d92251fc1
6 changed files with 453 additions and 3 deletions

View File

@@ -2,6 +2,7 @@
import { useState } from "react";
import { WorkspaceMemberRole } from "@mosaic/shared";
import { isValidEmail, toWorkspaceMemberRole } from "./validation";
interface InviteMemberProps {
onInvite: (email: string, role: WorkspaceMemberRole) => Promise<void>;
@@ -22,7 +23,7 @@ export function InviteMember({ onInvite }: InviteMemberProps): React.JSX.Element
return;
}
if (!email.includes("@")) {
if (!isValidEmail(email.trim())) {
setError("Please enter a valid email address");
return;
}
@@ -72,7 +73,7 @@ export function InviteMember({ onInvite }: InviteMemberProps): React.JSX.Element
id="role"
value={role}
onChange={(e) => {
setRole(e.target.value as WorkspaceMemberRole);
setRole(toWorkspaceMemberRole(e.target.value));
}}
disabled={isInviting}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100"